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