1 /*
2  * Copyright 2002 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 	int	flags;
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.  It should be kadmin/kerberos.master.host, but
65  * we'll fix that in the next release.
66  */
67 static const struct krb_convert sconv_list[] = {
68     {"kadmin",	"kadmin",	0},
69     {"rcmd",	"host",		DO_REALM_CONVERSION},
70     {"discuss",	"discuss",	DO_REALM_CONVERSION},
71     {"rvdsrv",	"rvdsrv",	DO_REALM_CONVERSION},
72     {"sample",	"sample",	DO_REALM_CONVERSION},
73     {"olc",	"olc",		DO_REALM_CONVERSION},
74     {"pop",	"pop",		DO_REALM_CONVERSION},
75     {"sis",	"sis",		DO_REALM_CONVERSION},
76     {"rfs",	"rfs",		DO_REALM_CONVERSION},
77     {"imap",	"imap",		DO_REALM_CONVERSION},
78     {"ftp",	"ftp",		DO_REALM_CONVERSION},
79     {"ecat",	"ecat",		DO_REALM_CONVERSION},
80     {"daemon",        "daemon",       DO_REALM_CONVERSION},
81     {"gnats", "gnats",        DO_REALM_CONVERSION},
82     {"moira", "moira",        DO_REALM_CONVERSION},
83     {"prms",  "prms",         DO_REALM_CONVERSION},
84     {"mandarin",      "mandarin",     DO_REALM_CONVERSION},
85     {"register",      "register",     DO_REALM_CONVERSION},
86     {"changepw",      "changepw",     DO_REALM_CONVERSION},
87     {"sms",   "sms",          DO_REALM_CONVERSION},
88     {"afpserver",     "afpserver",    DO_REALM_CONVERSION},
89     {"gdss",  "gdss",         DO_REALM_CONVERSION},
90     {"news",  "news",         DO_REALM_CONVERSION},
91     {"abs",   "abs",          DO_REALM_CONVERSION},
92     {"nfs",   "nfs",          DO_REALM_CONVERSION},
93     {"tftp",  "tftp",         DO_REALM_CONVERSION},
94     {"zephyr",        "zephyr",       0},
95     {"http",  "http",         DO_REALM_CONVERSION},
96     {"khttp", "khttp",        DO_REALM_CONVERSION},
97     {"pgpsigner", "pgpsigner",        DO_REALM_CONVERSION},
98     {"irc",   "irc",          DO_REALM_CONVERSION},
99     {"mandarin-agent",        "mandarin-agent",       DO_REALM_CONVERSION},
100     {"write", "write",        DO_REALM_CONVERSION},
101     {"palladium", "palladium",        DO_REALM_CONVERSION},
102     {0,		0,		0},
103 };
104 
105 /*
106  * char *strnchr(s, c, n)
107  *   char *s;
108  *   char c;
109  *   int n;
110  *
111  * returns a pointer to the first occurrence of character c in the
112  * string s, or a NULL pointer if c does not occur in in the string;
113  * however, at most the first n characters will be considered.
114  *
115  * This falls in the "should have been in the ANSI C library"
116  * category. :-)
117  */
118 static char *strnchr(s, c, n)
119    register char *s, c;
120    register int n;
121 {
122      if (n < 1)
123 	  return 0;
124 
125      while (n-- && *s) {
126 	  if (*s == c)
127 	       return s;
128 	  s++;
129      }
130      return 0;
131 }
132 
133 
134 /* XXX This calls for a new error code */
135 #define KRB5_INVALID_PRINCIPAL KRB5_LNAME_BADFORMAT
136 
137 /*ARGSUSED*/
138 KRB5_DLLIMP krb5_error_code KRB5_CALLCONV
139 krb5_524_conv_principal(context, princ, name, inst, realm)
140     krb5_context context;
141     const krb5_principal princ;
142     char FAR *name;
143     char FAR *inst;
144     char FAR *realm;
145 {
146      const struct krb_convert *p;
147      krb5_data *compo;
148      char *c, *tmp_realm, *tmp_prealm;
149      int tmp_realm_len, retval;
150 
151      *name = *inst = '\0';
152      switch (krb5_princ_size(context, princ)) {
153      case 2:
154 	  /* Check if this principal is listed in the table */
155 	  compo = krb5_princ_component(context, princ, 0);
156 	  p = sconv_list;
157 	  while (p->v4_str) {
158 	       if (strncmp(p->v5_str, compo->data, compo->length) == 0) {
159 		   /*
160 		    * It is, so set the new name now, and chop off
161 		    * instance's domain name if requested.
162 		    */
163 		   if (strlen (p->v4_str) > ANAME_SZ - 1)
164 		       return KRB5_INVALID_PRINCIPAL;
165 		   strcpy(name, p->v4_str);
166 		   if (p->flags & DO_REALM_CONVERSION) {
167 		       compo = krb5_princ_component(context, princ, 1);
168 		       c = strnchr(compo->data, '.', compo->length);
169 		       if (!c || (c - compo->data) >= INST_SZ - 1)
170 			   return KRB5_INVALID_PRINCIPAL;
171 		       memcpy(inst, compo->data, c - compo->data);
172 		       inst[c - compo->data] = '\0';
173 		   }
174 		   break;
175 	       }
176 	       p++;
177 	  }
178 	  /* If inst isn't set, the service isn't listed in the table, */
179 	  /* so just copy it. */
180 	  if (*inst == '\0') {
181 	       compo = krb5_princ_component(context, princ, 1);
182 	       if (compo->length >= INST_SZ - 1)
183 		    return KRB5_INVALID_PRINCIPAL;
184 	       memcpy(inst, compo->data, compo->length);
185 	       inst[compo->length] = '\0';
186 	  }
187 	  /* fall through */
188 	  /*FALLTHRU*/
189      case 1:
190 	  /* name may have been set above; otherwise, just copy it */
191 	  if (*name == '\0') {
192 	       compo = krb5_princ_component(context, princ, 0);
193 	       if (compo->length >= ANAME_SZ)
194 		    return KRB5_INVALID_PRINCIPAL;
195 	       memcpy(name, compo->data, compo->length);
196 	       name[compo->length] = '\0';
197 	  }
198 	  break;
199      default:
200 	  return KRB5_INVALID_PRINCIPAL;
201      }
202 
203      compo = krb5_princ_realm(context, princ);
204 
205      tmp_prealm = malloc(compo->length + 1);
206      if (tmp_prealm == NULL)
207 	 return ENOMEM;
208      strncpy(tmp_prealm, compo->data, compo->length);
209      tmp_prealm[compo->length] = '\0';
210 
211      /* Ask for v4_realm corresponding to
212 	krb5 principal realm from krb5.conf realms stanza */
213 
214      if (context->profile == 0)
215        return KRB5_CONFIG_CANTOPEN;
216      retval = profile_get_string(context->profile, "realms",
217 				 tmp_prealm, "v4_realm", 0,
218 				 &tmp_realm);
219      free(tmp_prealm);
220      if (retval) {
221 	 return retval;
222      } else {
223 	 if (tmp_realm == 0) {
224 	     if (compo->length > REALM_SZ - 1)
225 		 return KRB5_INVALID_PRINCIPAL;
226 	     strncpy(realm, compo->data, compo->length);
227 	     realm[compo->length] = '\0';
228 	 } else {
229 	     tmp_realm_len =  strlen(tmp_realm);
230 	     if (tmp_realm_len > REALM_SZ - 1)
231 		 return KRB5_INVALID_PRINCIPAL;
232 	     strncpy(realm, tmp_realm, tmp_realm_len);
233 	     realm[tmp_realm_len] = '\0';
234 	     profile_release_string(tmp_realm);
235 	 }
236      }
237      return 0;
238 }
239 
240 /*ARGSUSED*/
241 KRB5_DLLIMP krb5_error_code KRB5_CALLCONV
242 krb5_425_conv_principal(context, name, instance, realm, princ)
243    krb5_context context;
244    const char	FAR *name;
245    const char	FAR *instance;
246    const char	FAR *realm;
247    krb5_principal	FAR *princ;
248 {
249      const struct krb_convert *p;
250      char buf[256];		/* V4 instances are limited to 40 characters */
251      krb5_error_code retval;
252      char **full_name = 0;
253      char *domain, *cp;
254      const char *names[5];
255      void*	iterator = NULL;
256      char** v4realms = NULL;
257      char* realm_name = NULL;
258      char* dummy_value = NULL;
259 
260      /* First, convert the realm, since the v4 realm is not necessarily the same as the v5 realm
261         To do that, iterate over all the realms in the config file, looking for a matching
262         v4_realm line */
263      names [0] = "realms";
264      names [1] = NULL;
265      retval = profile_iterator_create (context -> profile, names, PROFILE_ITER_LIST_SECTION | PROFILE_ITER_SECTIONS_ONLY, &iterator);
266      while (retval == 0) {
267      	retval = profile_iterator (&iterator, &realm_name, &dummy_value);
268      	if ((retval == 0) && (realm_name != NULL)) {
269      		names [0] = "realms";
270      		names [1] = realm_name;
271      		names [2] = "v4_realm";
272      		names [3] = NULL;
273 
274      		retval = profile_get_values (context -> profile, names, &v4realms);
275      		if ((retval == 0) && (v4realms != NULL) && (v4realms [0] != NULL) && (strcmp (v4realms [0], realm) == 0)) {
276      			realm = realm_name;
277      			break;
278      		} else if (retval == PROF_NO_RELATION) {
279      			/* If it's not found, just keep going */
280      			retval = 0;
281      		}
282      	} else if ((retval == 0) && (realm_name == NULL)) {
283      		break;
284      	}
285      	if (realm_name != NULL) {
286      		profile_release_string (realm_name);
287      		realm_name = NULL;
288      	}
289      	if (dummy_value != NULL) {
290      		profile_release_string (dummy_value);
291      		dummy_value = NULL;
292      	}
293      }
294 
295      if (instance) {
296 	  if (instance[0] == '\0') {
297 	       instance = 0;
298 	       goto not_service;
299 	  }
300 	  p = sconv_list;
301 	 /*CONSTCOND*/
302 	  while (TRUE) {
303 	       if (!p->v4_str)
304 		    goto not_service;
305 	       if (!strcmp(p->v4_str, name))
306 		    break;
307 	       p++;
308 	  }
309 	  name = p->v5_str;
310 	  if ((p->flags & DO_REALM_CONVERSION) && !strchr(instance, '.')) {
311 	      names[0] = "realms";
312 	      names[1] = realm;
313 	      names[2] = "v4_instance_convert";
314 	      names[3] = instance;
315 	      names[4] = 0;
316 	      retval = profile_get_values(context->profile, names, &full_name);
317 	      if (retval == 0 && full_name && full_name[0]) {
318 		  instance = full_name[0];
319 	      } else {
320 		  strncpy(buf, instance, sizeof(buf));
321 		  buf[sizeof(buf) - 1] = '\0';
322 		  retval = krb5_get_realm_domain(context, realm, &domain);
323 		  if (retval)
324 		      return retval;
325 		  if (domain) {
326 		      for (cp = domain; *cp; cp++)
327 			  if (isupper(*cp))
328 			      *cp = tolower(*cp);
329 		      strncat(buf, ".", sizeof(buf) - 1 - strlen(buf));
330 		      strncat(buf, domain, sizeof(buf) - 1 - strlen(buf));
331 		      krb5_xfree(domain);
332 		  }
333 		  instance = buf;
334 	      }
335 	  }
336      }
337 
338 not_service:
339      retval = krb5_build_principal(context, princ, strlen(realm), realm, name,
340 				   instance, 0);
341      profile_iterator_free (&iterator);
342      profile_free_list(full_name);
343      profile_free_list(v4realms);
344      profile_release_string (realm_name);
345      profile_release_string (dummy_value);
346      return retval;
347 }
348