xref: /freebsd/crypto/krb5/src/lib/krb5/krb/conv_princ.c (revision 7f2fe78b9dd5f51c821d771b63d2e096f6fd49e9)
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