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