xref: /freebsd/crypto/krb5/src/lib/gssapi/krb5/import_name.c (revision f1c4c3daccbaf3820f0e2224de53df12fc952fcc)
1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3  * Copyright 1993 by OpenVision Technologies, Inc.
4  *
5  * Permission to use, copy, modify, distribute, and sell this software
6  * and its documentation for any purpose is hereby granted without fee,
7  * provided that the above copyright notice appears in all copies and
8  * that both that copyright notice and this permission notice appear in
9  * supporting documentation, and that the name of OpenVision not be used
10  * in advertising or publicity pertaining to distribution of the software
11  * without specific, written prior permission. OpenVision makes no
12  * representations about the suitability of this software for any
13  * purpose.  It is provided "as is" without express or implied warranty.
14  *
15  * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
16  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
17  * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR
18  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
19  * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
20  * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
21  * PERFORMANCE OF THIS SOFTWARE.
22  */
23 
24 /*
25  * $Id$
26  */
27 
28 #include "gssapiP_krb5.h"
29 
30 #ifndef NO_PASSWORD
31 #include <pwd.h>
32 #include <stdio.h>
33 #endif
34 
35 #ifdef HAVE_STRING_H
36 #include <string.h>
37 #else
38 #include <strings.h>
39 #endif
40 
41 /*
42  * errors:
43  * GSS_S_BAD_NAMETYPE   if the type is bogus
44  * GSS_S_BAD_NAME       if the type is good but the name is bogus
45  * GSS_S_FAILURE        if memory allocation fails
46  */
47 
48 /*
49  * Import serialized authdata context
50  */
51 static krb5_error_code
import_name_composite(krb5_context context,unsigned char * enc_data,size_t enc_length,krb5_authdata_context * pad_context)52 import_name_composite(krb5_context context,
53                       unsigned char *enc_data, size_t enc_length,
54                       krb5_authdata_context *pad_context)
55 {
56     krb5_authdata_context ad_context;
57     krb5_error_code code;
58     krb5_data data;
59 
60     if (enc_length == 0)
61         return 0;
62 
63     code = krb5_authdata_context_init(context, &ad_context);
64     if (code != 0)
65         return code;
66 
67     data.data = (char *)enc_data;
68     data.length = enc_length;
69 
70     code = krb5_authdata_import_attributes(context,
71                                            ad_context,
72                                            AD_USAGE_MASK,
73                                            &data);
74     if (code != 0) {
75         krb5_authdata_context_free(context, ad_context);
76         return code;
77     }
78 
79     *pad_context = ad_context;
80 
81     return 0;
82 }
83 
84 /* Split a host-based name "service[@host]" into allocated strings
85  * placed in *service_out and *host_out (possibly NULL). */
86 static krb5_error_code
parse_hostbased(const char * str,size_t len,char ** service_out,char ** host_out)87 parse_hostbased(const char *str, size_t len,
88                 char **service_out, char **host_out)
89 {
90     const char *at;
91     size_t servicelen, hostlen;
92     char *service, *host = NULL;
93 
94     *service_out = *host_out = NULL;
95 
96     /* Find the bound of the service name and copy it. */
97     at = memchr(str, '@', len);
98     servicelen = (at == NULL) ? len : (size_t)(at - str);
99     service = xmalloc(servicelen + 1);
100     if (service == NULL)
101         return ENOMEM;
102     memcpy(service, str, servicelen);
103     service[servicelen] = '\0';
104 
105     /* Copy the hostname if present (at least one character after '@'). */
106     if (len - servicelen > 1) {
107         hostlen = len - servicelen - 1;
108         host = malloc(hostlen + 1);
109         if (host == NULL) {
110             free(service);
111             return ENOMEM;
112         }
113         memcpy(host, at + 1, hostlen);
114         host[hostlen] = '\0';
115     }
116 
117     *service_out = service;
118     *host_out = host;
119     return 0;
120 }
121 
122 static OM_uint32 KRB5_CALLCONV
import_name(OM_uint32 * minor_status,gss_buffer_t input_name_buffer,gss_OID input_name_type,krb5_boolean iakerb,gss_name_t * output_name)123 import_name(OM_uint32 *minor_status, gss_buffer_t input_name_buffer,
124             gss_OID input_name_type, krb5_boolean iakerb,
125             gss_name_t *output_name)
126 {
127     krb5_context context;
128     krb5_principal princ = NULL;
129     krb5_error_code code;
130     unsigned char *cp, *end;
131     char *tmp = NULL, *tmp2 = NULL, *service = NULL, *host = NULL, *stringrep;
132     ssize_t    length;
133 #ifndef NO_PASSWORD
134     struct passwd *pw;
135 #endif
136     int is_composite = 0, is_cert = 0;
137     krb5_authdata_context ad_context = NULL;
138     OM_uint32 status = GSS_S_FAILURE;
139     krb5_gss_name_t name;
140     int flags = 0;
141 
142     *output_name = NULL;
143     *minor_status = 0;
144 
145     code = krb5_gss_init_context(&context);
146     if (code)
147         goto cleanup;
148 
149     if ((input_name_type != GSS_C_NULL_OID) &&
150         (g_OID_equal(input_name_type, gss_nt_service_name) ||
151          g_OID_equal(input_name_type, gss_nt_service_name_v2))) {
152         /* Split the name into service and host (or NULL). */
153         code = parse_hostbased(input_name_buffer->value,
154                                input_name_buffer->length, &service, &host);
155         if (code)
156             goto cleanup;
157 
158         /*
159          * Compute the initiator target name.  In some cases this is a waste of
160          * getaddrinfo/getnameinfo queries, but computing the name when we need
161          * it would require a lot of code changes.
162          */
163         code = krb5_sname_to_principal(context, host, service, KRB5_NT_SRV_HST,
164                                        &princ);
165         if (code)
166             goto cleanup;
167     } else if ((input_name_type != GSS_C_NULL_OID) &&
168                (g_OID_equal(input_name_type, gss_nt_krb5_principal))) {
169         krb5_principal input;
170 
171         if (input_name_buffer->length != sizeof(krb5_principal)) {
172             code = G_WRONG_SIZE;
173             status = GSS_S_BAD_NAME;
174             goto cleanup;
175         }
176 
177         input = *((krb5_principal *) input_name_buffer->value);
178 
179         code = krb5_copy_principal(context, input, &princ);
180         if (code)
181             goto cleanup;
182     } else if ((input_name_type != NULL) &&
183                g_OID_equal(input_name_type, GSS_C_NT_ANONYMOUS)) {
184         code = krb5_copy_principal(context, krb5_anonymous_principal(),
185                                    &princ);
186         if (code)
187             goto cleanup;
188     } else if ((input_name_type != NULL) &&
189                g_OID_equal(input_name_type, GSS_KRB5_NT_X509_CERT)) {
190         code = krb5_build_principal_ext(context, &princ, 0, NULL,
191                                         input_name_buffer->length,
192                                         input_name_buffer->value, 0);
193         if (code)
194             goto cleanup;
195         is_cert = 1;
196     } else {
197 #ifndef NO_PASSWORD
198         uid_t uid;
199         struct passwd pwx;
200         char pwbuf[BUFSIZ];
201 #endif
202 
203         stringrep = NULL;
204 
205         tmp = k5memdup0(input_name_buffer->value, input_name_buffer->length,
206                         &code);
207         if (tmp == NULL)
208             goto cleanup;
209         tmp2 = NULL;
210 
211         /* Find the appropriate string rep to pass into parse_name. */
212         if ((input_name_type == GSS_C_NULL_OID) ||
213             g_OID_equal(input_name_type, gss_nt_krb5_name) ||
214             g_OID_equal(input_name_type, gss_nt_user_name)) {
215             stringrep = tmp;
216         } else if (g_OID_equal(input_name_type, GSS_KRB5_NT_ENTERPRISE_NAME)) {
217             stringrep = tmp;
218             flags |= KRB5_PRINCIPAL_PARSE_ENTERPRISE;
219 #ifndef NO_PASSWORD
220         } else if (g_OID_equal(input_name_type, gss_nt_machine_uid_name)) {
221             uid = *(uid_t *) input_name_buffer->value;
222         do_getpwuid:
223             if (k5_getpwuid_r(uid, &pwx, pwbuf, sizeof(pwbuf), &pw) == 0)
224                 stringrep = pw->pw_name;
225             else
226                 code = G_NOUSER;
227         } else if (g_OID_equal(input_name_type, gss_nt_string_uid_name)) {
228             uid = atoi(tmp);
229             goto do_getpwuid;
230 #endif
231         } else if (g_OID_equal(input_name_type, gss_nt_exported_name) ||
232                    g_OID_equal(input_name_type, GSS_C_NT_COMPOSITE_EXPORT)) {
233 #define BOUNDS_CHECK(cp, end, n)                                        \
234             do { if ((end) - (cp) < (n)) goto fail_name; } while (0)
235             cp = (unsigned char *)tmp;
236             end = cp + input_name_buffer->length;
237 
238             BOUNDS_CHECK(cp, end, 2);
239             if (*cp++ != 0x04)
240                 goto fail_name;
241             switch (*cp++) {
242             case 0x01:
243                 break;
244             case 0x02:
245                 is_composite++;
246                 break;
247             default:
248                 goto fail_name;
249             }
250 
251             BOUNDS_CHECK(cp, end, 2);
252             if (*cp++ != 0x00)
253                 goto fail_name;
254             length = *cp++;
255             if (length != (ssize_t)gss_mech_krb5->length+2)
256                 goto fail_name;
257 
258             BOUNDS_CHECK(cp, end, 2);
259             if (*cp++ != 0x06)
260                 goto fail_name;
261             length = *cp++;
262             if (length != (ssize_t)gss_mech_krb5->length)
263                 goto fail_name;
264 
265             BOUNDS_CHECK(cp, end, length);
266             if (memcmp(cp, gss_mech_krb5->elements, length) != 0)
267                 goto fail_name;
268             cp += length;
269 
270             BOUNDS_CHECK(cp, end, 4);
271             length = *cp++;
272             length = (length << 8) | *cp++;
273             length = (length << 8) | *cp++;
274             length = (length << 8) | *cp++;
275 
276             BOUNDS_CHECK(cp, end, length);
277             tmp2 = k5alloc(length + 1, &code);
278             if (tmp2 == NULL)
279                 goto cleanup;
280             strncpy(tmp2, (char *)cp, length);
281             tmp2[length] = 0;
282             stringrep = tmp2;
283             cp += length;
284 
285             if (is_composite) {
286                 BOUNDS_CHECK(cp, end, 4);
287                 length = *cp++;
288                 length = (length << 8) | *cp++;
289                 length = (length << 8) | *cp++;
290                 length = (length << 8) | *cp++;
291 
292                 BOUNDS_CHECK(cp, end, length);
293                 code = import_name_composite(context,
294                                              cp, length,
295                                              &ad_context);
296                 if (code != 0)
297                     goto fail_name;
298                 cp += length;
299             }
300             assert(cp == end);
301         } else {
302             status = GSS_S_BAD_NAMETYPE;
303             goto cleanup;
304         }
305 
306         /* At this point, stringrep is set, or if not, code is. */
307         if (stringrep) {
308             /* For IAKERB, use realm discovery instead of the default realm. */
309             if (iakerb)
310                 flags |= KRB5_PRINCIPAL_PARSE_NO_DEF_REALM;
311             code = krb5_parse_name_flags(context, stringrep, flags, &princ);
312             if (code)
313                 goto cleanup;
314         } else {
315         fail_name:
316             status = GSS_S_BAD_NAME;
317             goto cleanup;
318         }
319     }
320 
321     /* Create a name and save it in the validation database. */
322     code = kg_init_name(context, princ, service, host, ad_context,
323                         KG_INIT_NAME_NO_COPY, &name);
324     if (code)
325         goto cleanup;
326     name->is_cert = is_cert;
327 
328     princ = NULL;
329     ad_context = NULL;
330     service = host = NULL;
331     *output_name = (gss_name_t)name;
332     status = GSS_S_COMPLETE;
333 
334 cleanup:
335     *minor_status = (OM_uint32)code;
336     if (*minor_status)
337         save_error_info(*minor_status, context);
338     krb5_free_principal(context, princ);
339     krb5_authdata_context_free(context, ad_context);
340     krb5_free_context(context);
341     free(tmp);
342     free(tmp2);
343     free(service);
344     free(host);
345     return status;
346 }
347 
348 OM_uint32 KRB5_CALLCONV
krb5_gss_import_name(OM_uint32 * minor_status,gss_buffer_t input_name_buffer,gss_OID input_name_type,gss_name_t * output_name)349 krb5_gss_import_name(OM_uint32 *minor_status, gss_buffer_t input_name_buffer,
350                      gss_OID input_name_type, gss_name_t *output_name)
351 {
352     return import_name(minor_status, input_name_buffer, input_name_type, FALSE,
353                        output_name);
354 }
355 
356 OM_uint32 KRB5_CALLCONV
iakerb_gss_import_name(OM_uint32 * minor_status,gss_buffer_t input_name_buffer,gss_OID input_name_type,gss_name_t * output_name)357 iakerb_gss_import_name(OM_uint32 *minor_status, gss_buffer_t input_name_buffer,
358                        gss_OID input_name_type, gss_name_t *output_name)
359 {
360     return import_name(minor_status, input_name_buffer, input_name_type, TRUE,
361                        output_name);
362 }
363