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