1 /*
2  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 
7 /*
8  * lib/krb5/krb/conv_princ.c
9  *
10  * Copyright 1992 by the Massachusetts Institute of Technology.
11  * All Rights Reserved.
12  *
13  * Export of this software from the United States of America may
14  *   require a specific license from the United States Government.
15  *   It is the responsibility of any person or organization contemplating
16  *   export to obtain such a license before exporting.
17  *
18  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
19  * distribute this software and its documentation for any purpose and
20  * without fee is hereby granted, provided that the above copyright
21  * notice appear in all copies and that both that copyright notice and
22  * this permission notice appear in supporting documentation, and that
23  * the name of M.I.T. not be used in advertising or publicity pertaining
24  * to distribution of the software without specific, written prior
25  * permission.  Furthermore if you modify this software you must label
26  * your software as modified software and not distribute it in such a
27  * fashion that it might be confused with the original M.I.T. software.
28  * M.I.T. makes no representations about the suitability of
29  * this software for any purpose.  It is provided "as is" without express
30  * or implied warranty.
31  *
32  * Build a principal from a V4 specification, or separate a V5
33  * principal into name, instance, and realm.
34  *
35  * NOTE: This is highly site specific, and is only really necessary
36  * for sites who need to convert from V4 to V5.  It is used by both
37  * the KDC and the kdb5_convert program.  Since its use is highly
38  * specialized, the necesary information is just going to be
39  * hard-coded in this file.
40  */
41 
42 #include "k5-int.h"
43 #include <string.h>
44 #include <ctype.h>
45 
46 /* The maximum sizes for V4 aname, realm, sname, and instance +1 */
47 /* Taken from krb.h */
48 #define 	ANAME_SZ	40
49 #define		REALM_SZ	40
50 #define		SNAME_SZ	40
51 #define		INST_SZ		40
52 
53 struct krb_convert {
54     char		*v4_str;
55     char		*v5_str;
56     unsigned int	flags : 8;
57     unsigned int	len : 8;
58 };
59 
60 #define DO_REALM_CONVERSION 0x00000001
61 
62 /*
63  * Kadmin doesn't do realm conversion because it's currently
64  * kadmin/REALM.NAME.  Zephyr doesn't because it's just zephyr/zephyr.
65  *
66  * "Realm conversion" is a bit of a misnomer; really, the v5 name is
67  * using a FQDN or something that looks like it, where the v4 name is
68  * just using the first label.  Sometimes that second principal name
69  * component is a hostname, sometimes the realm name, sometimes it's
70  * neither.
71  *
72  * This list should probably be more configurable, and more than
73  * likely on a per-realm basis, so locally-defined services can be
74  * added, or not.
75  */
76 static const struct krb_convert sconv_list[] = {
77     /* Realm conversion, Change service name */
78 #define RC(V5NAME,V4NAME) { V5NAME, V4NAME, DO_REALM_CONVERSION, sizeof(V5NAME)-1 }
79     /* Realm conversion */
80 #define R(NAME)		{ NAME, NAME, DO_REALM_CONVERSION, sizeof(NAME)-1 }
81     /* No Realm conversion */
82 #define NR(NAME)	{ NAME, NAME, 0, sizeof(NAME)-1 }
83 
84     NR("kadmin"),
85     RC("rcmd", "host"),
86     R("discuss"),
87     R("rvdsrv"),
88     R("sample"),
89     R("olc"),
90     R("pop"),
91     R("sis"),
92     R("rfs"),
93     R("imap"),
94     R("ftp"),
95     R("ecat"),
96     R("daemon"),
97     R("gnats"),
98     R("moira"),
99     R("prms"),
100     R("mandarin"),
101     R("register"),
102     R("changepw"),
103     R("sms"),
104     R("afpserver"),
105     R("gdss"),
106     R("news"),
107     R("abs"),
108     R("nfs"),
109     R("tftp"),
110     NR("zephyr"),
111     R("http"),
112     R("khttp"),
113     R("pgpsigner"),
114     R("irc"),
115     R("mandarin-agent"),
116     R("write"),
117     R("palladium"),
118     {0, 0, 0, 0},
119 #undef R
120 #undef RC
121 #undef NR
122 };
123 
124 /*
125  * char *strnchr(s, c, n)
126  *   char *s;
127  *   char c;
128  *   unsigned int n;
129  *
130  * returns a pointer to the first occurrence of character c in the
131  * string s, or a NULL pointer if c does not occur in in the string;
132  * however, at most the first n characters will be considered.
133  *
134  * This falls in the "should have been in the ANSI C library"
135  * category. :-)
136  */
strnchr(register char * s,register char c,register unsigned int n)137 static char *strnchr(register char *s, register char c,
138 		     register unsigned int n)
139 {
140      if (n < 1)
141 	  return 0;
142 
143      while (n-- && *s) {
144 	  if (*s == c)
145 	       return s;
146 	  s++;
147      }
148      return 0;
149 }
150 
151 
152 /* XXX This calls for a new error code */
153 #define KRB5_INVALID_PRINCIPAL KRB5_LNAME_BADFORMAT
154 
155 /*ARGSUSED*/
156 krb5_error_code KRB5_CALLCONV
krb5_524_conv_principal(krb5_context context,krb5_const_principal princ,char * name,char * inst,char * realm)157 krb5_524_conv_principal(krb5_context context, krb5_const_principal princ,
158 			char *name, char *inst, char *realm)
159 {
160      const struct krb_convert *p;
161      const krb5_data *compo;
162      char *c, *tmp_realm, *tmp_prealm;
163      unsigned int tmp_realm_len;
164      int retval;
165 
166      *name = *inst = '\0';
167      switch (krb5_princ_size(context, princ)) {
168      case 2:
169 	  /* Check if this principal is listed in the table */
170 	  compo = krb5_princ_component(context, princ, 0);
171 	  p = sconv_list;
172 	  while (p->v4_str) {
173 	       if (p->len == compo->length
174 		   && memcmp(p->v5_str, compo->data, compo->length) == 0) {
175 		   /*
176 		    * It is, so set the new name now, and chop off
177 		    * instance's domain name if requested.
178 		    */
179 		   if (strlen (p->v4_str) > ANAME_SZ - 1)
180 		       return KRB5_INVALID_PRINCIPAL;
181 		   strcpy(name, p->v4_str);
182 		   if (p->flags & DO_REALM_CONVERSION) {
183 		       compo = krb5_princ_component(context, princ, 1);
184 		       c = strnchr(compo->data, '.', compo->length);
185 		       if (!c || (c - compo->data) >= INST_SZ - 1)
186 			   return KRB5_INVALID_PRINCIPAL;
187 		       memcpy(inst, compo->data, (size_t) (c - compo->data));
188 		       inst[c - compo->data] = '\0';
189 		   }
190 		   break;
191 	       }
192 	       p++;
193 	  }
194 	  /* If inst isn't set, the service isn't listed in the table, */
195 	  /* so just copy it. */
196 	  if (*inst == '\0') {
197 	       compo = krb5_princ_component(context, princ, 1);
198 	       if (compo->length >= INST_SZ - 1)
199 		    return KRB5_INVALID_PRINCIPAL;
200 	       memcpy(inst, compo->data, compo->length);
201 	       inst[compo->length] = '\0';
202 	  }
203 	  /* fall through */
204 	  /*FALLTHRU*/
205      case 1:
206 	  /* name may have been set above; otherwise, just copy it */
207 	  if (*name == '\0') {
208 	       compo = krb5_princ_component(context, princ, 0);
209 	       if (compo->length >= ANAME_SZ)
210 		    return KRB5_INVALID_PRINCIPAL;
211 	       memcpy(name, compo->data, compo->length);
212 	       name[compo->length] = '\0';
213 	  }
214 	  break;
215      default:
216 	  return KRB5_INVALID_PRINCIPAL;
217      }
218 
219      compo = krb5_princ_realm(context, princ);
220 
221      tmp_prealm = malloc(compo->length + 1);
222      if (tmp_prealm == NULL)
223 	 return ENOMEM;
224      strncpy(tmp_prealm, compo->data, compo->length);
225      tmp_prealm[compo->length] = '\0';
226 
227      /* Ask for v4_realm corresponding to
228 	krb5 principal realm from krb5.conf realms stanza */
229 
230      if (context->profile == 0)
231        return KRB5_CONFIG_CANTOPEN;
232      retval = profile_get_string(context->profile, "realms",
233 				 tmp_prealm, "v4_realm", 0,
234 				 &tmp_realm);
235      free(tmp_prealm);
236      if (retval) {
237 	 return retval;
238      } else {
239 	 if (tmp_realm == 0) {
240 	     if (compo->length > REALM_SZ - 1)
241 		 return KRB5_INVALID_PRINCIPAL;
242 	     strncpy(realm, compo->data, compo->length);
243 	     realm[compo->length] = '\0';
244 	 } else {
245 	     tmp_realm_len =  strlen(tmp_realm);
246 	     if (tmp_realm_len > REALM_SZ - 1)
247 		 return KRB5_INVALID_PRINCIPAL;
248 	     strncpy(realm, tmp_realm, tmp_realm_len);
249 	     realm[tmp_realm_len] = '\0';
250 	     profile_release_string(tmp_realm);
251 	 }
252      }
253      return 0;
254 }
255 
256 /*ARGSUSED*/
257 krb5_error_code KRB5_CALLCONV
krb5_425_conv_principal(krb5_context context,const char * name,const char * instance,const char * realm,krb5_principal * princ)258 krb5_425_conv_principal(krb5_context context, const char *name,
259 			const char *instance, const char *realm,
260 			krb5_principal *princ)
261 {
262      const struct krb_convert *p;
263      char buf[256];		/* V4 instances are limited to 40 characters */
264      krb5_error_code retval;
265      char *domain, *cp;
266      char **full_name = 0;
267      const char *names[5], *names2[2];
268      void*	iterator = NULL;
269      char** v4realms = NULL;
270      char* realm_name = NULL;
271      char* dummy_value = NULL;
272 
273      /* First, convert the realm, since the v4 realm is not necessarily the same as the v5 realm
274         To do that, iterate over all the realms in the config file, looking for a matching
275         v4_realm line */
276      names2 [0] = "realms";
277      names2 [1] = NULL;
278      retval = profile_iterator_create (context -> profile, names2, PROFILE_ITER_LIST_SECTION | PROFILE_ITER_SECTIONS_ONLY, &iterator);
279      while (retval == 0) {
280      	retval = profile_iterator (&iterator, &realm_name, &dummy_value);
281      	if ((retval == 0) && (realm_name != NULL)) {
282      		names [0] = "realms";
283      		names [1] = realm_name;
284      		names [2] = "v4_realm";
285      		names [3] = NULL;
286 
287      		retval = profile_get_values (context -> profile, names, &v4realms);
288      		if ((retval == 0) && (v4realms != NULL) && (v4realms [0] != NULL) && (strcmp (v4realms [0], realm) == 0)) {
289      			realm = realm_name;
290      			break;
291      		} else if (retval == PROF_NO_RELATION) {
292      			/* If it's not found, just keep going */
293      			retval = 0;
294      		}
295      	} else if ((retval == 0) && (realm_name == NULL)) {
296      		break;
297      	}
298 	if (v4realms != NULL) {
299 	        profile_free_list(v4realms);
300 		v4realms = NULL;
301 	}
302      	if (realm_name != NULL) {
303      		profile_release_string (realm_name);
304      		realm_name = NULL;
305      	}
306      	if (dummy_value != NULL) {
307      		profile_release_string (dummy_value);
308      		dummy_value = NULL;
309      	}
310      }
311 
312      if (instance) {
313 	  if (instance[0] == '\0') {
314 	       instance = 0;
315 	       goto not_service;
316 	  }
317 	  p = sconv_list;
318 	 /*CONSTCOND*/
319 	  while (1) {
320 	       if (!p->v4_str)
321 		    goto not_service;
322 	       if (!strcmp(p->v4_str, name))
323 		    break;
324 	       p++;
325 	  }
326 	  name = p->v5_str;
327 	  if ((p->flags & DO_REALM_CONVERSION) && !strchr(instance, '.')) {
328 	      names[0] = "realms";
329 	      names[1] = realm;
330 	      names[2] = "v4_instance_convert";
331 	      names[3] = instance;
332 	      names[4] = 0;
333 	      retval = profile_get_values(context->profile, names, &full_name);
334 	      if (retval == 0 && full_name && full_name[0]) {
335 		  instance = full_name[0];
336 	      } else {
337 		  strncpy(buf, instance, sizeof(buf));
338 		  buf[sizeof(buf) - 1] = '\0';
339 		  retval = krb5_get_realm_domain(context, realm, &domain);
340 		  if (retval)
341 		      return retval;
342 		  if (domain) {
343 		      for (cp = domain; *cp; cp++)
344 			  if (isupper((unsigned char) (*cp)))
345 			      *cp = tolower((unsigned char) *cp);
346 		      strncat(buf, ".", sizeof(buf) - 1 - strlen(buf));
347 		      strncat(buf, domain, sizeof(buf) - 1 - strlen(buf));
348 		      krb5_xfree(domain);
349 		  }
350 		  instance = buf;
351 	      }
352 	  }
353      }
354 
355 not_service:
356      retval = krb5_build_principal(context, princ, strlen(realm), realm, name,
357 				   instance, NULL);
358      if (iterator) profile_iterator_free (&iterator);
359      if (full_name) profile_free_list(full_name);
360      if (v4realms) profile_free_list(v4realms);
361      if (realm_name) profile_release_string (realm_name);
362      if (dummy_value) profile_release_string (dummy_value);
363      return retval;
364 }
365