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 #include <sys/param.h>
36 #if __FreeBSD_version < 500100
37 #include <stdio.h>
38 #endif
39 
40 #ifdef HAVE_STRING_H
41 #include <string.h>
42 #else
43 #include <strings.h>
44 #endif
45 
46 /*
47  * errors:
48  * GSS_S_BAD_NAMETYPE   if the type is bogus
49  * GSS_S_BAD_NAME       if the type is good but the name is bogus
50  * GSS_S_FAILURE        if memory allocation fails
51  */
52 
53 /*
54  * Import serialized authdata context
55  */
56 static krb5_error_code
import_name_composite(krb5_context context,unsigned char * enc_data,size_t enc_length,krb5_authdata_context * pad_context)57 import_name_composite(krb5_context context,
58                       unsigned char *enc_data, size_t enc_length,
59                       krb5_authdata_context *pad_context)
60 {
61     krb5_authdata_context ad_context;
62     krb5_error_code code;
63     krb5_data data;
64 
65     if (enc_length == 0)
66         return 0;
67 
68     code = krb5_authdata_context_init(context, &ad_context);
69     if (code != 0)
70         return code;
71 
72     data.data = (char *)enc_data;
73     data.length = enc_length;
74 
75     code = krb5_authdata_import_attributes(context,
76                                            ad_context,
77                                            AD_USAGE_MASK,
78                                            &data);
79     if (code != 0) {
80         krb5_authdata_context_free(context, ad_context);
81         return code;
82     }
83 
84     *pad_context = ad_context;
85 
86     return 0;
87 }
88 
89 /* Split a host-based name "service[@host]" into allocated strings
90  * placed in *service_out and *host_out (possibly NULL). */
91 static krb5_error_code
parse_hostbased(const char * str,size_t len,char ** service_out,char ** host_out)92 parse_hostbased(const char *str, size_t len,
93                 char **service_out, char **host_out)
94 {
95     const char *at;
96     size_t servicelen, hostlen;
97     char *service, *host = NULL;
98 
99     *service_out = *host_out = NULL;
100 
101     /* Find the bound of the service name and copy it. */
102     at = memchr(str, '@', len);
103     servicelen = (at == NULL) ? len : (size_t)(at - str);
104     service = xmalloc(servicelen + 1);
105     if (service == NULL)
106         return ENOMEM;
107     memcpy(service, str, servicelen);
108     service[servicelen] = '\0';
109 
110     /* Copy the hostname if present (at least one character after '@'). */
111     if (len - servicelen > 1) {
112         hostlen = len - servicelen - 1;
113         host = malloc(hostlen + 1);
114         if (host == NULL) {
115             free(service);
116             return ENOMEM;
117         }
118         memcpy(host, at + 1, hostlen);
119         host[hostlen] = '\0';
120     }
121 
122     *service_out = service;
123     *host_out = host;
124     return 0;
125 }
126 
127 OM_uint32 KRB5_CALLCONV
krb5_gss_import_name(minor_status,input_name_buffer,input_name_type,output_name)128 krb5_gss_import_name(minor_status, input_name_buffer,
129                      input_name_type, output_name)
130     OM_uint32 *minor_status;
131     gss_buffer_t input_name_buffer;
132     gss_OID input_name_type;
133     gss_name_t *output_name;
134 {
135     krb5_context context;
136     krb5_principal princ = NULL;
137     krb5_error_code code;
138     unsigned char *cp, *end;
139     char *tmp = NULL, *tmp2 = NULL, *service = NULL, *host = NULL, *stringrep;
140     ssize_t    length;
141 #ifndef NO_PASSWORD
142     struct passwd *pw;
143 #endif
144     int is_composite = 0, is_cert = 0;
145     krb5_authdata_context ad_context = NULL;
146     OM_uint32 status = GSS_S_FAILURE;
147     krb5_gss_name_t name;
148     int flags = 0;
149 
150     *output_name = NULL;
151     *minor_status = 0;
152 
153     code = krb5_gss_init_context(&context);
154     if (code)
155         goto cleanup;
156 
157     if ((input_name_type != GSS_C_NULL_OID) &&
158         (g_OID_equal(input_name_type, gss_nt_service_name) ||
159          g_OID_equal(input_name_type, gss_nt_service_name_v2))) {
160         /* Split the name into service and host (or NULL). */
161         code = parse_hostbased(input_name_buffer->value,
162                                input_name_buffer->length, &service, &host);
163         if (code)
164             goto cleanup;
165 
166         /*
167          * Compute the initiator target name.  In some cases this is a waste of
168          * getaddrinfo/getnameinfo queries, but computing the name when we need
169          * it would require a lot of code changes.
170          */
171         code = krb5_sname_to_principal(context, host, service, KRB5_NT_SRV_HST,
172                                        &princ);
173         if (code)
174             goto cleanup;
175     } else if ((input_name_type != GSS_C_NULL_OID) &&
176                (g_OID_equal(input_name_type, gss_nt_krb5_principal))) {
177         krb5_principal input;
178 
179         if (input_name_buffer->length != sizeof(krb5_principal)) {
180             code = G_WRONG_SIZE;
181             status = GSS_S_BAD_NAME;
182             goto cleanup;
183         }
184 
185         input = *((krb5_principal *) input_name_buffer->value);
186 
187         code = krb5_copy_principal(context, input, &princ);
188         if (code)
189             goto cleanup;
190     } else if ((input_name_type != NULL) &&
191                g_OID_equal(input_name_type, GSS_C_NT_ANONYMOUS)) {
192         code = krb5_copy_principal(context, krb5_anonymous_principal(),
193                                    &princ);
194         if (code)
195             goto cleanup;
196     } else if ((input_name_type != NULL) &&
197                g_OID_equal(input_name_type, GSS_KRB5_NT_X509_CERT)) {
198         code = krb5_build_principal_ext(context, &princ, 0, NULL,
199                                         input_name_buffer->length,
200                                         input_name_buffer->value, 0);
201         if (code)
202             goto cleanup;
203         is_cert = 1;
204     } else {
205 #ifndef NO_PASSWORD
206         uid_t uid;
207         struct passwd pwx;
208         char pwbuf[BUFSIZ];
209 #endif
210 
211         stringrep = NULL;
212 
213         tmp = k5memdup0(input_name_buffer->value, input_name_buffer->length,
214                         &code);
215         if (tmp == NULL)
216             goto cleanup;
217         tmp2 = NULL;
218 
219         /* Find the appropriate string rep to pass into parse_name. */
220         if ((input_name_type == GSS_C_NULL_OID) ||
221             g_OID_equal(input_name_type, gss_nt_krb5_name) ||
222             g_OID_equal(input_name_type, gss_nt_user_name)) {
223             stringrep = tmp;
224         } else if (g_OID_equal(input_name_type, GSS_KRB5_NT_ENTERPRISE_NAME)) {
225             stringrep = tmp;
226             flags |= KRB5_PRINCIPAL_PARSE_ENTERPRISE;
227 #ifndef NO_PASSWORD
228         } else if (g_OID_equal(input_name_type, gss_nt_machine_uid_name)) {
229             uid = *(uid_t *) input_name_buffer->value;
230         do_getpwuid:
231             if (k5_getpwuid_r(uid, &pwx, pwbuf, sizeof(pwbuf), &pw) == 0)
232                 stringrep = pw->pw_name;
233             else
234                 code = G_NOUSER;
235         } else if (g_OID_equal(input_name_type, gss_nt_string_uid_name)) {
236             uid = atoi(tmp);
237             goto do_getpwuid;
238 #endif
239         } else if (g_OID_equal(input_name_type, gss_nt_exported_name) ||
240                    g_OID_equal(input_name_type, GSS_C_NT_COMPOSITE_EXPORT)) {
241 #define BOUNDS_CHECK(cp, end, n)                                        \
242             do { if ((end) - (cp) < (n)) goto fail_name; } while (0)
243             cp = (unsigned char *)tmp;
244             end = cp + input_name_buffer->length;
245 
246             BOUNDS_CHECK(cp, end, 2);
247             if (*cp++ != 0x04)
248                 goto fail_name;
249             switch (*cp++) {
250             case 0x01:
251                 break;
252             case 0x02:
253                 is_composite++;
254                 break;
255             default:
256                 goto fail_name;
257             }
258 
259             BOUNDS_CHECK(cp, end, 2);
260             if (*cp++ != 0x00)
261                 goto fail_name;
262             length = *cp++;
263             if (length != (ssize_t)gss_mech_krb5->length+2)
264                 goto fail_name;
265 
266             BOUNDS_CHECK(cp, end, 2);
267             if (*cp++ != 0x06)
268                 goto fail_name;
269             length = *cp++;
270             if (length != (ssize_t)gss_mech_krb5->length)
271                 goto fail_name;
272 
273             BOUNDS_CHECK(cp, end, length);
274             if (memcmp(cp, gss_mech_krb5->elements, length) != 0)
275                 goto fail_name;
276             cp += length;
277 
278             BOUNDS_CHECK(cp, end, 4);
279             length = *cp++;
280             length = (length << 8) | *cp++;
281             length = (length << 8) | *cp++;
282             length = (length << 8) | *cp++;
283 
284             BOUNDS_CHECK(cp, end, length);
285             tmp2 = k5alloc(length + 1, &code);
286             if (tmp2 == NULL)
287                 goto cleanup;
288             strncpy(tmp2, (char *)cp, length);
289             tmp2[length] = 0;
290             stringrep = tmp2;
291             cp += length;
292 
293             if (is_composite) {
294                 BOUNDS_CHECK(cp, end, 4);
295                 length = *cp++;
296                 length = (length << 8) | *cp++;
297                 length = (length << 8) | *cp++;
298                 length = (length << 8) | *cp++;
299 
300                 BOUNDS_CHECK(cp, end, length);
301                 code = import_name_composite(context,
302                                              cp, length,
303                                              &ad_context);
304                 if (code != 0)
305                     goto fail_name;
306                 cp += length;
307             }
308             assert(cp == end);
309         } else {
310             status = GSS_S_BAD_NAMETYPE;
311             goto cleanup;
312         }
313 
314         /* At this point, stringrep is set, or if not, code is. */
315         if (stringrep) {
316             code = krb5_parse_name_flags(context, stringrep, flags, &princ);
317             if (code)
318                 goto cleanup;
319         } else {
320         fail_name:
321             status = GSS_S_BAD_NAME;
322             goto cleanup;
323         }
324     }
325 
326     /* Create a name and save it in the validation database. */
327     code = kg_init_name(context, princ, service, host, ad_context,
328                         KG_INIT_NAME_NO_COPY, &name);
329     if (code)
330         goto cleanup;
331     name->is_cert = is_cert;
332 
333     princ = NULL;
334     ad_context = NULL;
335     service = host = NULL;
336     *output_name = (gss_name_t)name;
337     status = GSS_S_COMPLETE;
338 
339 cleanup:
340     *minor_status = (OM_uint32)code;
341     if (*minor_status)
342         save_error_info(*minor_status, context);
343     krb5_free_principal(context, princ);
344     krb5_authdata_context_free(context, ad_context);
345     krb5_free_context(context);
346     free(tmp);
347     free(tmp2);
348     free(service);
349     free(host);
350     return status;
351 }
352