1/*
2 * Copyright (c) 1997 - 2003 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
5 *
6 * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 *
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * 3. Neither the name of the Institute nor the names of its contributors
20 *    may be used to endorse or promote products derived from this software
21 *    without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36#include "gsskrb5_locl.h"
37
38static OM_uint32
39import_krb5_name(OM_uint32 *minor_status,
40		 gss_const_OID mech,
41		 const gss_buffer_t input_name_buffer,
42		 gss_const_OID input_name_type,
43		 gss_name_t *output_name)
44{
45    krb5_context context;
46    krb5_principal princ;
47    krb5_error_code ret;
48    char *tmp;
49
50    GSSAPI_KRB5_INIT (&context);
51
52    tmp = malloc (input_name_buffer->length + 1);
53    if (tmp == NULL) {
54	*minor_status = ENOMEM;
55	return GSS_S_FAILURE;
56    }
57    memcpy (tmp,
58	    input_name_buffer->value,
59	    input_name_buffer->length);
60    tmp[input_name_buffer->length] = '\0';
61
62    if (tmp[0] == '@') {
63	princ = calloc(1, sizeof(*princ));
64	if (princ == NULL) {
65	    free(tmp);
66	    *minor_status = ENOMEM;
67	    return GSS_S_FAILURE;
68	}
69
70	princ->realm = strdup(&tmp[1]);
71	if (princ->realm == NULL) {
72	    free(tmp);
73	    free(princ);
74	    return GSS_S_FAILURE;
75	}
76    } else {
77	ret = krb5_parse_name (context, tmp, &princ);
78	if (ret) {
79	    free(tmp);
80	    *minor_status = ret;
81
82	    if (ret == KRB5_PARSE_ILLCHAR || ret == KRB5_PARSE_MALFORMED)
83		return GSS_S_BAD_NAME;
84
85	    return GSS_S_FAILURE;
86	}
87    }
88
89    if (mech && gss_oid_equal(mech, GSS_PKU2U_MECHANISM) && strchr(tmp, '@') == NULL)
90	krb5_principal_set_realm(context, princ, KRB5_PKU2U_REALM_NAME);
91
92    free(tmp);
93
94    if (princ->name.name_string.len == 2 &&
95	gss_oid_equal(input_name_type, GSS_KRB5_NT_PRINCIPAL_NAME_REFERRAL))
96	krb5_principal_set_type(context, princ, KRB5_NT_GSS_HOSTBASED_SERVICE);
97
98    *output_name = (gss_name_t)princ;
99    return GSS_S_COMPLETE;
100}
101
102static OM_uint32
103import_krb5_principal(OM_uint32 *minor_status,
104		      gss_const_OID mech,
105		      const gss_buffer_t input_name_buffer,
106		      gss_const_OID input_name_type,
107		      gss_name_t *output_name)
108{
109    krb5_context context;
110    krb5_principal *princ, res = NULL;
111    OM_uint32 ret;
112
113    GSSAPI_KRB5_INIT (&context);
114
115    princ = (krb5_principal *)input_name_buffer->value;
116
117    ret = krb5_copy_principal(context, *princ, &res);
118    if (ret) {
119	*minor_status = ret;
120	return GSS_S_FAILURE;
121    }
122    *output_name = (gss_name_t)res;
123    return GSS_S_COMPLETE;
124}
125
126
127OM_uint32
128_gsskrb5_canon_name(OM_uint32 *minor_status, krb5_context context,
129		    int use_dns, krb5_const_principal sourcename, gss_name_t targetname,
130		    krb5_principal *out)
131{
132    krb5_principal p = (krb5_principal)targetname;
133    krb5_error_code ret;
134    char *hostname = NULL, *service;
135
136    *minor_status = 0;
137
138    /* If its not a hostname */
139    if (krb5_principal_get_type(context, p) != KRB5_NT_GSS_HOSTBASED_SERVICE) {
140	ret = krb5_copy_principal(context, p, out);
141    } else if (!use_dns) {
142	ret = krb5_copy_principal(context, p, out);
143	if (ret)
144	    goto out;
145	krb5_principal_set_type(context, *out, KRB5_NT_SRV_HST);
146	if (sourcename)
147	    ret = krb5_principal_set_realm(context, *out, sourcename->realm);
148    } else {
149	if (p->name.name_string.len == 0)
150	    return GSS_S_BAD_NAME;
151	else if (p->name.name_string.len > 1)
152	    hostname = p->name.name_string.val[1];
153
154	service = p->name.name_string.val[0];
155
156	ret = krb5_sname_to_principal(context,
157				      hostname,
158				      service,
159				      KRB5_NT_SRV_HST,
160				      out);
161    }
162
163 out:
164    if (ret) {
165	*minor_status = ret;
166	return GSS_S_FAILURE;
167    }
168
169    return 0;
170}
171
172
173static OM_uint32
174import_hostbased_name (OM_uint32 *minor_status,
175		       gss_const_OID mech,
176		       const gss_buffer_t input_name_buffer,
177		       gss_const_OID input_name_type,
178		       gss_name_t *output_name)
179{
180    krb5_context context;
181    krb5_principal princ = NULL;
182    krb5_error_code kerr;
183    char *tmp, *p, *host = NULL, *realm = NULL;
184
185    if (gss_oid_equal(mech, GSS_PKU2U_MECHANISM))
186	realm = KRB5_PKU2U_REALM_NAME;
187    else
188	realm = KRB5_GSS_REFERALS_REALM_NAME; /* should never hit the network */
189
190    GSSAPI_KRB5_INIT (&context);
191
192    tmp = malloc (input_name_buffer->length + 1);
193    if (tmp == NULL) {
194	*minor_status = ENOMEM;
195	return GSS_S_FAILURE;
196    }
197    memcpy (tmp,
198	    input_name_buffer->value,
199	    input_name_buffer->length);
200    tmp[input_name_buffer->length] = '\0';
201
202    p = strchr (tmp, '@');
203    if (p != NULL && p[1] != '\0') {
204	size_t len;
205
206	*p = '\0';
207	host = p + 1;
208
209	/*
210	 * Squash any trailing . on the hostname since that is jolly
211	 * good to have when looking up a DNS name (qualified), but
212	 * its no good to have in the kerberos principal since those
213	 * are supposed to be in qualified format already.
214	 */
215
216	len = strlen(host);
217	if (len > 0 && host[len - 1] == '.')
218	    host[len - 1] = '\0';
219    } else {
220	host = KRB5_GSS_HOSTBASED_SERVICE_NAME;
221    }
222
223    kerr = krb5_make_principal(context, &princ, realm, tmp, host, NULL);
224    free (tmp);
225    *minor_status = kerr;
226    if (kerr == KRB5_PARSE_ILLCHAR || kerr == KRB5_PARSE_MALFORMED)
227	return GSS_S_BAD_NAME;
228    else if (kerr)
229	return GSS_S_FAILURE;
230
231    krb5_principal_set_type(context, princ, KRB5_NT_GSS_HOSTBASED_SERVICE);
232    *output_name = (gss_name_t)princ;
233
234    return 0;
235}
236
237static OM_uint32
238import_dn_name(OM_uint32 *minor_status,
239	       gss_const_OID mech,
240	       const gss_buffer_t input_name_buffer,
241	       gss_const_OID input_name_type,
242	       gss_name_t *output_name)
243{
244    /* XXX implement me */
245    *output_name = NULL;
246    *minor_status = 0;
247    return GSS_S_FAILURE;
248}
249
250static OM_uint32
251import_pku2u_export_name(OM_uint32 *minor_status,
252			 gss_const_OID mech,
253			 const gss_buffer_t input_name_buffer,
254			 gss_const_OID input_name_type,
255			 gss_name_t *output_name)
256{
257    /* XXX implement me */
258    *output_name = NULL;
259    *minor_status = 0;
260    return GSS_S_FAILURE;
261}
262
263static OM_uint32
264import_uuid_name(OM_uint32 *minor_status,
265		 gss_const_OID mech,
266		 const gss_buffer_t input_name_buffer,
267		 gss_const_OID input_name_type,
268		 gss_name_t *output_name)
269{
270    krb5_context context;
271    krb5_error_code ret;
272    krb5_principal princ;
273    char uuid[36 + 1];
274
275    GSSAPI_KRB5_INIT(&context);
276
277    if (input_name_buffer->length < sizeof(uuid) - 1) {
278	*minor_status = 0;
279	return GSS_S_BAD_NAME;
280    }
281
282    memcpy(uuid, input_name_buffer->value, sizeof(uuid) - 1);
283    uuid[sizeof(uuid) - 1] = '\0';
284
285    /* validate that uuid is only uuid chars and the right length*/
286    if (strspn(uuid, "0123456789abcdefABCDEF-") != 36) {
287	*minor_status = 0;
288	return GSS_S_BAD_NAME;
289    }
290
291    ret = krb5_make_principal(context, &princ, "UUID", uuid, NULL);
292    if (ret) {
293	*minor_status = ret;
294	return GSS_S_FAILURE;
295    }
296    krb5_principal_set_type(context, princ, KRB5_NT_CACHE_UUID);
297
298    *output_name = (gss_name_t)princ;
299    *minor_status = 0;
300
301    return GSS_S_COMPLETE;
302}
303
304static struct _gss_name_type krb5_names[] = {
305    { GSS_C_NT_HOSTBASED_SERVICE, import_hostbased_name },
306    { GSS_C_NT_HOSTBASED_SERVICE_X, import_hostbased_name },
307    { GSS_KRB5_NT_PRINCIPAL, import_krb5_principal},
308    { GSS_C_NO_OID, import_krb5_name },
309    { GSS_C_NT_USER_NAME, import_krb5_name },
310    { GSS_KRB5_NT_PRINCIPAL_NAME, import_krb5_name },
311    { GSS_KRB5_NT_PRINCIPAL_NAME_REFERRAL, import_krb5_name },
312    { GSS_C_NT_EXPORT_NAME, import_krb5_name },
313    { GSS_C_NT_UUID, import_uuid_name },
314    { NULL, NULL }
315};
316
317static struct _gss_name_type pku2u_names[] = {
318    { GSS_C_NT_HOSTBASED_SERVICE, import_hostbased_name },
319    { GSS_C_NT_HOSTBASED_SERVICE_X, import_hostbased_name },
320    { GSS_C_NO_OID, import_krb5_name },
321    { GSS_C_NT_USER_NAME, import_krb5_name },
322    { GSS_KRB5_NT_PRINCIPAL_NAME, import_krb5_name },
323    { GSS_C_NT_DN, import_dn_name },
324    { GSS_C_NT_EXPORT_NAME, import_pku2u_export_name },
325    { GSS_C_NT_UUID, import_uuid_name },
326    { NULL, NULL }
327};
328
329static struct _gss_name_type iakerb_names[] = {
330    { GSS_C_NT_HOSTBASED_SERVICE, import_hostbased_name },
331    { GSS_C_NT_HOSTBASED_SERVICE_X, import_hostbased_name },
332    { GSS_C_NO_OID, import_krb5_name },
333    { GSS_C_NT_USER_NAME, import_krb5_name },
334    { GSS_KRB5_NT_PRINCIPAL_NAME, import_krb5_name },
335    { GSS_KRB5_NT_PRINCIPAL_NAME_REFERRAL, import_krb5_name },
336    { GSS_C_NT_EXPORT_NAME, import_krb5_name },
337    { GSS_C_NT_UUID, import_uuid_name },
338    { NULL, NULL }
339};
340
341OM_uint32 GSSAPI_CALLCONV _gsskrb5_import_name
342           (OM_uint32 * minor_status,
343            const gss_buffer_t input_name_buffer,
344            gss_const_OID input_name_type,
345            gss_name_t * output_name
346           )
347{
348    return _gss_mech_import_name(minor_status, GSS_KRB5_MECHANISM,
349				 krb5_names, input_name_buffer,
350				 input_name_type, output_name);
351}
352
353OM_uint32 _gsspku2u_import_name
354           (OM_uint32 * minor_status,
355            const gss_buffer_t input_name_buffer,
356            gss_const_OID input_name_type,
357            gss_name_t * output_name
358           )
359{
360    return _gss_mech_import_name(minor_status, GSS_PKU2U_MECHANISM,
361				 pku2u_names, input_name_buffer,
362				 input_name_type, output_name);
363}
364
365OM_uint32
366_gssiakerb_import_name(OM_uint32 * minor_status,
367		       const gss_buffer_t input_name_buffer,
368		       gss_const_OID input_name_type,
369		       gss_name_t * output_name)
370{
371    return _gss_mech_import_name(minor_status, GSS_IAKERB_MECHANISM,
372				 iakerb_names, input_name_buffer,
373				 input_name_type, output_name);
374}
375
376OM_uint32
377_gsskrb5_inquire_names_for_mech (OM_uint32 * minor_status,
378				 gss_const_OID mechanism,
379				 gss_OID_set * name_types)
380{
381    return _gss_mech_inquire_names_for_mech(minor_status, krb5_names,
382					    name_types);
383}
384
385OM_uint32
386_gsspku2u_inquire_names_for_mech (OM_uint32 * minor_status,
387				  gss_const_OID mechanism,
388				  gss_OID_set * name_types)
389{
390    return _gss_mech_inquire_names_for_mech(minor_status, pku2u_names,
391					    name_types);
392}
393
394OM_uint32
395_gssiakerb_inquire_names_for_mech (OM_uint32 * minor_status,
396				   gss_const_OID mechanism,
397				   gss_OID_set * name_types)
398{
399    return _gss_mech_inquire_names_for_mech(minor_status, iakerb_names,
400					    name_types);
401}
402