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