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