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