xref: /freebsd/crypto/krb5/src/lib/gssapi/krb5/import_name.c (revision 7f2fe78b9dd5f51c821d771b63d2e096f6fd49e9)
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 OM_uint32 KRB5_CALLCONV
krb5_gss_import_name(minor_status,input_name_buffer,input_name_type,output_name)123 krb5_gss_import_name(minor_status, input_name_buffer,
124                      input_name_type, output_name)
125     OM_uint32 *minor_status;
126     gss_buffer_t input_name_buffer;
127     gss_OID input_name_type;
128     gss_name_t *output_name;
129 {
130     krb5_context context;
131     krb5_principal princ = NULL;
132     krb5_error_code code;
133     unsigned char *cp, *end;
134     char *tmp = NULL, *tmp2 = NULL, *service = NULL, *host = NULL, *stringrep;
135     ssize_t    length;
136 #ifndef NO_PASSWORD
137     struct passwd *pw;
138 #endif
139     int is_composite = 0, is_cert = 0;
140     krb5_authdata_context ad_context = NULL;
141     OM_uint32 status = GSS_S_FAILURE;
142     krb5_gss_name_t name;
143     int flags = 0;
144 
145     *output_name = NULL;
146     *minor_status = 0;
147 
148     code = krb5_gss_init_context(&context);
149     if (code)
150         goto cleanup;
151 
152     if ((input_name_type != GSS_C_NULL_OID) &&
153         (g_OID_equal(input_name_type, gss_nt_service_name) ||
154          g_OID_equal(input_name_type, gss_nt_service_name_v2))) {
155         /* Split the name into service and host (or NULL). */
156         code = parse_hostbased(input_name_buffer->value,
157                                input_name_buffer->length, &service, &host);
158         if (code)
159             goto cleanup;
160 
161         /*
162          * Compute the initiator target name.  In some cases this is a waste of
163          * getaddrinfo/getnameinfo queries, but computing the name when we need
164          * it would require a lot of code changes.
165          */
166         code = krb5_sname_to_principal(context, host, service, KRB5_NT_SRV_HST,
167                                        &princ);
168         if (code)
169             goto cleanup;
170     } else if ((input_name_type != GSS_C_NULL_OID) &&
171                (g_OID_equal(input_name_type, gss_nt_krb5_principal))) {
172         krb5_principal input;
173 
174         if (input_name_buffer->length != sizeof(krb5_principal)) {
175             code = G_WRONG_SIZE;
176             status = GSS_S_BAD_NAME;
177             goto cleanup;
178         }
179 
180         input = *((krb5_principal *) input_name_buffer->value);
181 
182         code = krb5_copy_principal(context, input, &princ);
183         if (code)
184             goto cleanup;
185     } else if ((input_name_type != NULL) &&
186                g_OID_equal(input_name_type, GSS_C_NT_ANONYMOUS)) {
187         code = krb5_copy_principal(context, krb5_anonymous_principal(),
188                                    &princ);
189         if (code)
190             goto cleanup;
191     } else if ((input_name_type != NULL) &&
192                g_OID_equal(input_name_type, GSS_KRB5_NT_X509_CERT)) {
193         code = krb5_build_principal_ext(context, &princ, 0, NULL,
194                                         input_name_buffer->length,
195                                         input_name_buffer->value, 0);
196         if (code)
197             goto cleanup;
198         is_cert = 1;
199     } else {
200 #ifndef NO_PASSWORD
201         uid_t uid;
202         struct passwd pwx;
203         char pwbuf[BUFSIZ];
204 #endif
205 
206         stringrep = NULL;
207 
208         tmp = k5memdup0(input_name_buffer->value, input_name_buffer->length,
209                         &code);
210         if (tmp == NULL)
211             goto cleanup;
212         tmp2 = NULL;
213 
214         /* Find the appropriate string rep to pass into parse_name. */
215         if ((input_name_type == GSS_C_NULL_OID) ||
216             g_OID_equal(input_name_type, gss_nt_krb5_name) ||
217             g_OID_equal(input_name_type, gss_nt_user_name)) {
218             stringrep = tmp;
219         } else if (g_OID_equal(input_name_type, GSS_KRB5_NT_ENTERPRISE_NAME)) {
220             stringrep = tmp;
221             flags |= KRB5_PRINCIPAL_PARSE_ENTERPRISE;
222 #ifndef NO_PASSWORD
223         } else if (g_OID_equal(input_name_type, gss_nt_machine_uid_name)) {
224             uid = *(uid_t *) input_name_buffer->value;
225         do_getpwuid:
226             if (k5_getpwuid_r(uid, &pwx, pwbuf, sizeof(pwbuf), &pw) == 0)
227                 stringrep = pw->pw_name;
228             else
229                 code = G_NOUSER;
230         } else if (g_OID_equal(input_name_type, gss_nt_string_uid_name)) {
231             uid = atoi(tmp);
232             goto do_getpwuid;
233 #endif
234         } else if (g_OID_equal(input_name_type, gss_nt_exported_name) ||
235                    g_OID_equal(input_name_type, GSS_C_NT_COMPOSITE_EXPORT)) {
236 #define BOUNDS_CHECK(cp, end, n)                                        \
237             do { if ((end) - (cp) < (n)) goto fail_name; } while (0)
238             cp = (unsigned char *)tmp;
239             end = cp + input_name_buffer->length;
240 
241             BOUNDS_CHECK(cp, end, 2);
242             if (*cp++ != 0x04)
243                 goto fail_name;
244             switch (*cp++) {
245             case 0x01:
246                 break;
247             case 0x02:
248                 is_composite++;
249                 break;
250             default:
251                 goto fail_name;
252             }
253 
254             BOUNDS_CHECK(cp, end, 2);
255             if (*cp++ != 0x00)
256                 goto fail_name;
257             length = *cp++;
258             if (length != (ssize_t)gss_mech_krb5->length+2)
259                 goto fail_name;
260 
261             BOUNDS_CHECK(cp, end, 2);
262             if (*cp++ != 0x06)
263                 goto fail_name;
264             length = *cp++;
265             if (length != (ssize_t)gss_mech_krb5->length)
266                 goto fail_name;
267 
268             BOUNDS_CHECK(cp, end, length);
269             if (memcmp(cp, gss_mech_krb5->elements, length) != 0)
270                 goto fail_name;
271             cp += length;
272 
273             BOUNDS_CHECK(cp, end, 4);
274             length = *cp++;
275             length = (length << 8) | *cp++;
276             length = (length << 8) | *cp++;
277             length = (length << 8) | *cp++;
278 
279             BOUNDS_CHECK(cp, end, length);
280             tmp2 = k5alloc(length + 1, &code);
281             if (tmp2 == NULL)
282                 goto cleanup;
283             strncpy(tmp2, (char *)cp, length);
284             tmp2[length] = 0;
285             stringrep = tmp2;
286             cp += length;
287 
288             if (is_composite) {
289                 BOUNDS_CHECK(cp, end, 4);
290                 length = *cp++;
291                 length = (length << 8) | *cp++;
292                 length = (length << 8) | *cp++;
293                 length = (length << 8) | *cp++;
294 
295                 BOUNDS_CHECK(cp, end, length);
296                 code = import_name_composite(context,
297                                              cp, length,
298                                              &ad_context);
299                 if (code != 0)
300                     goto fail_name;
301                 cp += length;
302             }
303             assert(cp == end);
304         } else {
305             status = GSS_S_BAD_NAMETYPE;
306             goto cleanup;
307         }
308 
309         /* At this point, stringrep is set, or if not, code is. */
310         if (stringrep) {
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