xref: /illumos-gate/usr/src/lib/gss_mechs/mech_dh/backend/mech/name.c (revision bbf215553c7233fbab8a0afdf1fac74c44781867)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  *	name.c
24  *
25  * Copyright 2003 Sun Microsystems, Inc.  All rights reserved.
26  * Use is subject to license terms.
27  *
28  */
29 
30 #include "dh_gssapi.h"
31 #include <pwd.h>
32 #include <string.h>
33 #include <stdlib.h>
34 #include <sys/types.h>
35 #include <sys/param.h>
36 #include <sys/note.h>
37 #include <thread.h>
38 
39 extern int
40 get_der_length(unsigned char **, unsigned int, unsigned int *);
41 
42 extern unsigned int
43 der_length_size(unsigned int);
44 
45 extern int
46 put_der_length(unsigned int, unsigned char **, unsigned int);
47 
48 /* Diffie-Hellman ONC RPC netname name type */
49 static gss_OID_desc __DH_GSS_C_NT_NETNAME_desc =
50 	{ 9,  "\053\006\004\001\052\002\032\001\001" };
51 
52 const gss_OID_desc * const __DH_GSS_C_NT_NETNAME = &__DH_GSS_C_NT_NETNAME_desc;
53 
54 #define	OID_MAX_NAME_ENTRIES  32
55 
56 /*
57  * __dh_gss_compare_name: Diffie-Hellman machanism support for
58  * gss_compare_name. Given two gss_name_ts that are presumed to
59  * be rpc netnames set the *equal parameter to true if they are
60  * the same, else set it to false.
61  */
62 
63 OM_uint32
__dh_gss_compare_name(void * ctx,OM_uint32 * minor,gss_name_t name1,gss_name_t name2,int * equal)64 __dh_gss_compare_name(void *ctx,	/* Per mechanism context (not used) */
65 		    OM_uint32 *minor,	/* Mechanism status */
66 		    gss_name_t name1,	/* First name to compare */
67 		    gss_name_t name2,	/* Second name to compare */
68 		    int *equal		/* The result */)
69 {
70 _NOTE(ARGUNUSED(ctx))
71 
72 	if (minor == 0 || equal == 0)
73 		return (GSS_S_CALL_INACCESSIBLE_WRITE);
74 
75 	*minor = DH_SUCCESS;
76 
77 	if (name1 == 0 || name2 == 0) {
78 		*minor = DH_BADARG_FAILURE;
79 		return (GSS_S_BAD_NAME | GSS_S_CALL_INACCESSIBLE_READ);
80 	}
81 
82 	*equal = (strcmp((char *)name1, (char *)name2) == 0);
83 
84 	return (GSS_S_COMPLETE);
85 }
86 
87 /*
88  * __dh_gss_display_name: Supports gss_display_name for Diffie-Hellman
89  * mechanism. This takes a gss internal name and converts it to
90  * a counted string suitable for display.
91  */
92 OM_uint32
__dh_gss_display_name(void * ctx,OM_uint32 * minor,gss_name_t name,gss_buffer_t output,gss_OID * name_type)93 __dh_gss_display_name(void * ctx, /* Per mechanism context (not used) */
94 		    OM_uint32* minor, /* Mechanism status */
95 		    gss_name_t name, /* Diffie-Hellman internal name */
96 		    gss_buffer_t output, /* Were the printable name goes */
97 		    gss_OID *name_type /* Name type of the internal name */)
98 {
99 _NOTE(ARGUNUSED(ctx))
100 
101 	if (minor == 0 || output == 0)
102 		return (GSS_S_CALL_INACCESSIBLE_WRITE);
103 
104 	if (name == 0)
105 		return (GSS_S_CALL_INACCESSIBLE_READ | GSS_S_BAD_NAME);
106 
107 	*minor = DH_SUCCESS;
108 
109 	output->length = 0;
110 	output->value = (void *)strdup((char *)name);
111 	if (output->value == NULL) {
112 		*minor = DH_NOMEM_FAILURE;
113 		return (GSS_S_FAILURE);
114 	}
115 	output->length = strlen((char *)name) + 1;
116 
117 /*
118  * Note: we no longer copy the name type OID. The current draft of
119  * the standard specifies:
120  *
121  * "The returned gss_OID will be a pointer into static stoarge
122  *  and should be treated as read-only by the caller (in particular,
123  *  it does not need to be freed)."
124  *
125  *	if (name_type) {
126  *		if ((*minor = __OID_copy(name_type, __DH_GSS_C_NT_NETNAME))
127  *			!= DH_SUCCESS) {
128  *			free(output->value);
129  *			output->value = NULL;
130  *			return (GSS_S_FAILURE);
131  *		}
132  *	}
133  */
134 
135 	if (name_type)
136 		*name_type = (gss_OID) __DH_GSS_C_NT_NETNAME;
137 
138 	return (GSS_S_COMPLETE);
139 }
140 
141 /*
142  * Routine that takes a netname as a character string and assigns it
143  * to a an gss_name_t pointed to by output.
144  */
145 static OM_uint32
do_netname_nametype(OM_uint32 * minor,char * input,gss_name_t * output)146 do_netname_nametype(OM_uint32 *minor, char *input, gss_name_t *output)
147 {
148 	if (__dh_validate_principal(input) != DH_SUCCESS)
149 		return (GSS_S_BAD_NAME);
150 
151 	*minor = DH_SUCCESS;
152 	*output = (gss_name_t)strdup((char *)input);
153 
154 	if (*output == NULL) {
155 		*minor = DH_NOMEM_FAILURE;
156 		return (GSS_S_FAILURE);
157 	}
158 
159 	return (GSS_S_COMPLETE);
160 }
161 
162 /*
163  * do_uid_nametype converts a uid to a gss_name_t pointed to by output
164  */
165 static OM_uint32
do_uid_nametype(OM_uint32 * minor,uid_t uid,gss_name_t * output)166 do_uid_nametype(OM_uint32 *minor, uid_t uid, gss_name_t *output)
167 {
168 	char netname[MAXNETNAMELEN+1];
169 
170 	if (!user2netname(netname, uid, NULL)) {
171 		*minor = DH_NETNAME_FAILURE;
172 		return (GSS_S_FAILURE);
173 	}
174 	return (do_netname_nametype(minor, netname, output));
175 }
176 
177 /*
178  * do_username_nametype converts a username to a gss_name_t pointed to by
179  * output.
180  *
181  * A username will be represented by the following:
182  * 	name[/node][@security-domain]
183  *
184  * Then optional security-domain will represent secure rpc domain if
185  * present. If not present the local domain will be used. name is the
186  * user name as found in the unix password file. If name is root and
187  * node is present, then node will represent the host. If the host is
188  * a qualified name we assume that it is a DNS name and will only return
189  * the first commponnet since we want host name that are relative to
190  * the security domain (secure rpc domain).
191  */
192 
193 static OM_uint32
do_username_nametype(OM_uint32 * minor,char * uname,gss_name_t * output)194 do_username_nametype(OM_uint32 *minor, char *uname, gss_name_t *output)
195 {
196 	char netname[MAXNETNAMELEN+1];
197 	char *user, *node, *domain;
198 	struct passwd pwd;
199 	char buff[1024];
200 
201 	/* Set outputs to sane values */
202 
203 	*output = 0;
204 	*minor = DH_SUCCESS;
205 
206 	/* See if we have a name */
207 	if (uname == 0) {
208 		*minor = DH_NO_SUCH_USER;
209 		return (GSS_S_FAILURE);
210 	}
211 
212 	/* copy the name so that we can do surgery on it */
213 	user = strdup(uname);
214 	if (user == 0) {
215 		*minor = DH_NOMEM_FAILURE;
216 		return (GSS_S_FAILURE);
217 	}
218 
219 
220 	/* Look for optional node part */
221 	node = strchr(user, '/');
222 	if (node) {
223 		/*
224 		 * user is now just the user portion and node
225 		 * points to the start of the node part.
226 		 */
227 		*node++ = '\0';
228 
229 		/* Now see if there is a domain */
230 		domain = strchr(node, '@');
231 	}
232 	else
233 		/* Check for a domain */
234 		domain = strchr(user, '@');
235 
236 	/* Set domain to the beginning of the domain part if pressent */
237 	if (domain)
238 		*domain++ = '\0';
239 
240 	/*
241 	 * See if the node part is important. If the user is root get
242 	 * the host from the node. If node is not present we assume
243 	 * we're the local host.
244 	 */
245 	if (strcmp(user, "root") == 0) {
246 		char *dot;
247 
248 		/*
249 		 * We only want the host part of a qualfied host name. We
250 		 * assume the domain part of a hostname is a DNS domain,
251 		 * not an rpc domain. The rpc domain can be specified
252 		 * in the optional security domain part.
253 		 */
254 		if (node) {
255 			dot = strchr(node, '.');
256 			if (dot)
257 				*dot = '\0';
258 		}
259 		/*
260 		 * If node is null, assume local host. If domain is
261 		 * null assume local domain. See host2netname(3NSL)
262 		 */
263 		if (!host2netname(netname, node,  domain)) {
264 			*minor = DH_NETNAME_FAILURE;
265 			free(user);
266 			return (GSS_S_FAILURE);
267 		}
268 		free(user);
269 		return (do_netname_nametype(minor, netname, output));
270 	}
271 
272 	/*
273 	 * We use getpwnam_r to convert the name to uid.  Note it is
274 	 * important to use getpwnam_r to preserve MT safty.
275 	 */
276 	if (getpwnam_r(user, &pwd, buff, sizeof (buff)) == NULL) {
277 		*minor = DH_NO_SUCH_USER;
278 		free(user);
279 		return (GSS_S_FAILURE);
280 	}
281 
282 	/* If domain is null assume local domain. See user2netname(3NSL) */
283 	if (!user2netname(netname, pwd.pw_uid, domain)) {
284 		*minor = DH_NETNAME_FAILURE;
285 		free(user);
286 		return (GSS_S_FAILURE);
287 	}
288 	free(user);
289 	return (do_netname_nametype(minor, netname, output));
290 }
291 
292 /*
293  * do_hostbase_nametype convert a hostbase service name of the form
294  *	service@hostname.
295  *
296  * For Diffie-Hellman we assume that the service is running with the
297  * credtials of the machine, i.e., as root.
298  */
299 static OM_uint32
do_hostbase_nametype(OM_uint32 * minor,char * input,gss_name_t * output)300 do_hostbase_nametype(OM_uint32 *minor, char *input, gss_name_t *output)
301 {
302 	/* Get the nostname */
303 	char *host = strchr(input, '@');
304 	char netname[MAXNETNAMELEN+1];
305 
306 
307 	/* If no host return bad name */
308 	if (host == NULL)
309 		return (GSS_S_BAD_NAME);
310 
311 	/* Advance pass the "@" sign */
312 	host += 1;
313 
314 	/* Convert the hostname to its netname */
315 	if (!host2netname(netname, host, NULL)) {
316 		*minor = DH_NETNAME_FAILURE;
317 		return (GSS_S_FAILURE);
318 	}
319 
320 	/* Internalize the netname to output */
321 	return (do_netname_nametype(minor, netname, output));
322 }
323 
324 /*
325  * do_exported_netname: Convert an exported Diffie-Hellman name
326  * to a Diffie-Hellman internal name.
327  */
328 static OM_uint32
do_exported_netname(dh_context_t ctx,OM_uint32 * minor,gss_buffer_t input,gss_name_t * output)329 do_exported_netname(dh_context_t ctx, /* Diffie-Hellman mech context */
330 		    OM_uint32 *minor, /* Mech status */
331 		    gss_buffer_t input, /* The export name to convert */
332 		    gss_name_t *output /* The converted internal name */)
333 {
334 	/* All export names must start with this */
335 	const char tokid[] = "\x04\x01";
336 	const int tokid_len = 2;
337 	const int OIDlen_len = 2;
338 	const int namelen_len = 4;
339 	unsigned char *p = (unsigned char *)input->value;
340 	OM_uint32 len = input->length;
341 	int	 mechoidlen;
342 	OM_uint32 oidlen; /* includes object tag len & DER len bytes */
343 	OM_uint32 namelen;
344 	OM_uint32 currlen;
345 	OM_uint32 bytes;
346 
347 	*minor = DH_BADARG_FAILURE;
348 
349 	/* The len must be at least this big */
350 	if (len < tokid_len + OIDlen_len + namelen_len)
351 		return (GSS_S_DEFECTIVE_TOKEN);
352 
353 	/* Export names must start with the token id of 0x04 0x01 */
354 	if (memcmp(p, tokid, tokid_len) != 0)
355 		return (GSS_S_DEFECTIVE_TOKEN);
356 	p += tokid_len;
357 
358 	/* Decode the Mechanism oid */
359 	oidlen = (*p++ << 8) & 0xff00;
360 	oidlen |= *p++ & 0xff;
361 
362 	/* Check that we actually have the mechanism oid elements */
363 	if (len < tokid_len + OIDlen_len + oidlen + namelen_len)
364 		return (GSS_S_DEFECTIVE_TOKEN);
365 
366 	/* Compare that the input is for this mechanism */
367 	if (*p++ != 0x06)
368 		return (GSS_S_DEFECTIVE_TOKEN);
369 	currlen = len - (tokid_len + OIDlen_len + oidlen + namelen_len);
370 	if ((mechoidlen = get_der_length(&p, currlen, &bytes)) < 0)
371 		return (GSS_S_DEFECTIVE_TOKEN);
372 	if (mechoidlen != ctx->mech->length)
373 		return (GSS_S_DEFECTIVE_TOKEN);
374 	if (memcmp(p, ctx->mech->elements, mechoidlen) != 0)
375 		return (GSS_S_DEFECTIVE_TOKEN);
376 	p += mechoidlen;
377 
378 	/* Grab the length of the mechanism specific name per RFC 2078 */
379 	namelen = (*p++ << 24) & 0xff000000;
380 	namelen |= (*p++ << 16) & 0xff0000;
381 	namelen |= (*p++ << 8) & 0xff00;
382 	namelen |= *p++ & 0xff;
383 
384 	/* This should alway be false */
385 	if (len < tokid_len + OIDlen_len + oidlen + namelen_len + namelen)
386 		return (GSS_S_DEFECTIVE_TOKEN);
387 
388 	/* Make sure the bytes for the netname oid length are available */
389 	if (namelen < OIDlen_len)
390 		return (GSS_S_DEFECTIVE_TOKEN);
391 
392 	/* Get the netname oid length */
393 	oidlen = (*p++ << 8) & 0xff00;
394 	oidlen = *p++ & 0xff;
395 
396 	/* See if we have the elements of the netname oid */
397 	if (namelen < OIDlen_len + oidlen)
398 		return (GSS_S_DEFECTIVE_TOKEN);
399 
400 	/* Check that the oid is really a netname */
401 	if (oidlen != __DH_GSS_C_NT_NETNAME->length)
402 		return (GSS_S_DEFECTIVE_TOKEN);
403 	if (memcmp(p, __DH_GSS_C_NT_NETNAME->elements,
404 	    __DH_GSS_C_NT_NETNAME->length) != 0)
405 		return (GSS_S_DEFECTIVE_TOKEN);
406 
407 	/* p now points to the netname wich is null terminated */
408 	p += oidlen;
409 
410 	/*
411 	 * How the netname is encoded in an export name type for
412 	 * this mechanism. See _dh_gss_export_name below.
413 	 */
414 
415 	if (namelen != OIDlen_len + oidlen + strlen((char *)p) + 1)
416 		return (GSS_S_DEFECTIVE_TOKEN);
417 
418 	/* Grab the netname */
419 	*output = (gss_name_t)strdup((char *)p);
420 	if (*output) {
421 		*minor = 0;
422 		return (GSS_S_COMPLETE);
423 	}
424 
425 	*minor = DH_NOMEM_FAILURE;
426 	return (GSS_S_FAILURE);
427 }
428 
429 /*
430  * __dh_gss_import_name: Diffie-Hellman entry point for gss_import_name.
431  * Given an input name of a specified name type, convert this to a
432  * Diffie-Hellman internal name (netname).
433  *
434  * The idea here is simply compare the name_type supplied with each
435  * name type that we know how to deal with. If we have a match we call
436  * the appropriate support routine form above. If we done't have a match
437  * we return GSS_S_BAD_NAMETYPE
438  */
439 OM_uint32
__dh_gss_import_name(void * ctx,OM_uint32 * minor,gss_buffer_t input,gss_OID name_type,gss_name_t * output)440 __dh_gss_import_name(void *ctx, /* Per mechanism context */
441 		    OM_uint32 *minor, /* Mechanism status */
442 		    gss_buffer_t input, /* The name to convert */
443 		    gss_OID name_type, /* of this name_type */
444 		    gss_name_t *output /* The converted name */)
445 {
446 	char *name;
447 	OM_uint32 stat;
448 
449 	if (minor == NULL || output == NULL)
450 		return (GSS_S_CALL_INACCESSIBLE_WRITE);
451 
452 	if (input == NULL || input->value == NULL)
453 		return (GSS_S_BAD_NAME | GSS_S_CALL_INACCESSIBLE_READ);
454 	if (name_type == GSS_C_NO_OID)
455 		return (GSS_S_BAD_NAMETYPE);
456 
457 	/* Set sane state */
458 	*minor = DH_SUCCESS;
459 	*output = GSS_C_NO_NAME;
460 
461 		/* UID in machine format */
462 	if (__OID_equal(name_type, GSS_C_NT_MACHINE_UID_NAME)) {
463 		uid_t uid;
464 		if (input->length != sizeof (uid_t))
465 			return (GSS_S_BAD_NAME);
466 		uid = *(uid_t *)input->value;
467 		/* Should we assume that the id is network byte order ??? */
468 		/* uid = htonl(uid); No, this should be the local orfering */
469 		return (do_uid_nametype(minor, uid, output));
470 
471 		/* Name that was exported with __dh_gss_export_name */
472 	} else if (__OID_equal(name_type, GSS_C_NT_EXPORT_NAME)) {
473 		stat = do_exported_netname((dh_context_t)ctx, minor,
474 		    input, output);
475 		return (stat);
476 	}
477 
478 	/* Null ternamte name so we can manipulate as a c-style string */
479 	name = malloc(input->length+1);
480 	if (name == NULL) {
481 		*minor = DH_NOMEM_FAILURE;
482 		return (GSS_S_FAILURE);
483 	}
484 	memcpy(name, input->value, input->length);
485 	name[input->length] = '\0';
486 
487 
488 		/* Diffie-Hellman (ONC RPC netname) */
489 	if (__OID_equal(name_type, __DH_GSS_C_NT_NETNAME)) {
490 		stat = do_netname_nametype(minor, name, output);
491 		free(name);
492 		return (stat);
493 		/* Host based service name (service@hostname) */
494 	} else if (__OID_equal(name_type, GSS_C_NT_HOSTBASED_SERVICE)) {
495 		stat = do_hostbase_nametype(minor, name, output);
496 		free(name);
497 		return (stat);
498 		/* Thus local OS user name */
499 	} else if (__OID_equal(name_type, GSS_C_NT_USER_NAME)) {
500 		stat = do_username_nametype(minor, name, output);
501 		free(name);
502 		return (stat);
503 		/* The os user id writen as a string */
504 	} else if (__OID_equal(name_type, GSS_C_NT_STRING_UID_NAME)) {
505 		char *p;
506 		/* Convert the name to a uid */
507 		uid_t uid = (uid_t)strtol(name, &p, 0);
508 		free(name);
509 		if (*p != '\0')
510 			return (GSS_S_BAD_NAME);
511 		return (do_uid_nametype(minor, uid, output));
512 	} else {
513 		/* Any thing else */
514 		free(name);
515 		return (GSS_S_BAD_NAMETYPE);
516 	}
517 }
518 
519 /*
520  * __dh_gss_release_name: DH entry point for gss_release_name.
521  * Release an internal DH name.
522  */
523 OM_uint32
__dh_gss_release_name(void * ctx,OM_uint32 * minor,gss_name_t * name)524 __dh_gss_release_name(void *ctx, OM_uint32 *minor, gss_name_t *name)
525 {
526 _NOTE(ARGUNUSED(ctx))
527 
528 	if (minor == 0 || name == 0)
529 		return (GSS_S_CALL_INACCESSIBLE_WRITE);
530 
531 	*minor = DH_SUCCESS;
532 
533 	free(*name);
534 	*name = GSS_C_NO_NAME;
535 
536 	return (GSS_S_COMPLETE);
537 }
538 
539 /* Lock for initializing oid_name_tab */
540 static mutex_t name_tab_lock = DEFAULTMUTEX;
541 
542 /* Table of name types that this mechanism understands */
543 static const gss_OID_desc * oid_name_tab[OID_MAX_NAME_ENTRIES];
544 
545 /*
546  * __dh_gss_inquire_names_for_mech: DH entry point for
547  * gss_inquire_names_for_mech.
548  *
549  * Return a set of OID name types that a mechanism can understand
550  */
551 OM_uint32
__dh_gss_inquire_names_for_mech(void * ctx,OM_uint32 * minor,gss_OID mech,gss_OID_set * names)552 __dh_gss_inquire_names_for_mech(void *ctx, OM_uint32 *minor,
553     gss_OID mech, gss_OID_set *names)
554 {
555 _NOTE(ARGUNUSED(ctx,mech))
556 
557 	/* See if we need to initialize the table */
558 	if (oid_name_tab[0] == 0) {
559 		mutex_lock(&name_tab_lock);
560 		/* If nobody sneaked in, initialize the table */
561 		if (oid_name_tab[0] == 0) {
562 			oid_name_tab[0] = __DH_GSS_C_NT_NETNAME;
563 			oid_name_tab[1] = GSS_C_NT_HOSTBASED_SERVICE;
564 			oid_name_tab[2] = GSS_C_NT_USER_NAME;
565 			oid_name_tab[3] = GSS_C_NT_MACHINE_UID_NAME;
566 			oid_name_tab[4] = GSS_C_NT_STRING_UID_NAME;
567 			oid_name_tab[5] = GSS_C_NT_EXPORT_NAME;
568 			/* oid_name_tab[6] = GSS_C_NT_ANONYMOUS_NAME; */
569 		}
570 		mutex_unlock(&name_tab_lock);
571 	}
572 
573 	/* Return the set of OIDS from the table */
574 	if ((*minor = __OID_copy_set_from_array(names,
575 	    oid_name_tab, 6)) != DH_SUCCESS)
576 		return (GSS_S_FAILURE);
577 
578 	return (GSS_S_COMPLETE);
579 }
580 
581 
582 /*
583  * Private libgss entry point to convert a principal name to uid.
584  */
585 OM_uint32
__dh_pname_to_uid(void * ctx,OM_uint32 * minor,const gss_name_t pname,uid_t * uid)586 __dh_pname_to_uid(void *ctx, /* DH mech context (not used) */
587 		OM_uint32 *minor, /* Mech status */
588 		const gss_name_t pname, /* principal */
589 		uid_t *uid  /* where to put the uid */)
590 {
591 _NOTE(ARGUNUSED(ctx))
592 
593 	gid_t gid;
594 	gid_t glist[NGRPS];
595 	int glen;
596 	/* Convert the principal name to a netname */
597 	char *netname = (char *)pname;
598 	char host_netname[MAXNETNAMELEN+1];
599 
600 	if (pname == 0)
601 		return (GSS_S_BAD_NAME | GSS_S_CALL_INACCESSIBLE_READ);
602 	if (minor == 0 || uid == 0)
603 		return (GSS_S_CALL_INACCESSIBLE_WRITE);
604 
605 	*minor = DH_SUCCESS;
606 	*uid = UID_NOBODY;
607 
608 	/* First try to convert as a user */
609 	if (netname2user(netname, uid, &gid, &glen, glist))
610 		return (GSS_S_COMPLETE);
611 	/* Get this hosts netname */
612 	else if (host2netname(host_netname, NULL, NULL)) {
613 		/*
614 		 * If the netname is this host's netname then we're root
615 		 * else we're nobody.
616 		 */
617 		if (strncmp(netname, host_netname, MAXNETNAMELEN) == 0)
618 			*uid = 0;
619 		return (GSS_S_COMPLETE);
620 	}
621 
622 	/* We could not get a netname */
623 	*minor = DH_NETNAME_FAILURE;
624 	return (GSS_S_FAILURE);
625 }
626 
627 /*
628  * __dh_gss_export_name: Diffie-Hellman support for gss_export_name.
629  * Given a Diffie-Hellman internal name return the GSS exported format.
630  */
631 OM_uint32
__dh_gss_export_name(void * ctx,OM_uint32 * minor,const gss_name_t input_name,gss_buffer_t exported_name)632 __dh_gss_export_name(void *ctx, /* Per mechanism context */
633 		    OM_uint32 *minor, /* Mechanism status */
634 		    const gss_name_t input_name, /* The name to export */
635 		    gss_buffer_t exported_name /* Exported name goes here */)
636 {
637 	/* input_name is dh principal name */
638 	dh_principal pname = (dh_principal)input_name;
639 	dh_context_t dc = (dh_context_t)ctx;
640 	/* Magic for exported blobs */
641 	const char tokid[] = "\x04\x01";
642 	const int tokid_len = 2;
643 	const int OIDlen_len = 2; /* Why did they do this? */
644 	const int namelen_len = 4;
645 	const int mechoid_tag_len = 1;
646 	unsigned char *p;
647 	OM_uint32 len;
648 	OM_uint32 namelen;
649 	OM_uint32 currlen;
650 	OM_uint32 oid_der_len = 0;
651 
652 	if (minor == 0 || exported_name == GSS_C_NO_BUFFER)
653 		return (GSS_S_CALL_INACCESSIBLE_WRITE);
654 	if (input_name == GSS_C_NO_NAME)
655 		return (GSS_S_CALL_INACCESSIBLE_READ);
656 
657 	/* Set sane outputs */
658 	*minor = DH_SUCCESS;
659 	exported_name->length = 0;
660 	exported_name->value = NULL;
661 
662 	/* Determine the length of the name */
663 	namelen = OIDlen_len + __DH_GSS_C_NT_NETNAME->length
664 	    + strlen(pname)+1;
665 	oid_der_len = der_length_size(dc->mech->length);
666 	/* Find the total length */
667 	len = tokid_len + OIDlen_len + mechoid_tag_len + oid_der_len
668 		+ dc->mech->length + namelen_len + namelen;
669 
670 	/* Allocate the blob */
671 	p = New(unsigned char, len);
672 	if (p == NULL) {
673 		*minor = DH_NOMEM_FAILURE;
674 		return (GSS_S_FAILURE);
675 	}
676 	/* Set the blob to the exported name */
677 	exported_name->length = len;
678 	exported_name->value = p;
679 
680 	/* Start with some magic */
681 	memcpy(p, tokid, tokid_len);
682 	p += tokid_len;
683 
684 	/*
685 	 * The spec only allows two bytes for the oid length.
686 	 * We are assuming here that the correct encodeing is MSB first as
687 	 * was done in libgss.
688 	 */
689 
690 	*p++ = ((mechoid_tag_len + oid_der_len + dc->mech->length)
691 			& 0xff00) >> 8;
692 	*p++ = ((mechoid_tag_len + oid_der_len + dc->mech->length)
693 			& 0x00ff);
694 
695 	/* Now the mechanism OID DER Encoding */
696 	*p++ = 0x06; /* Universal Tag for OID */
697 	currlen = len - tokid_len - OIDlen_len - mechoid_tag_len;
698 	if (!put_der_length(dc->mech->length, &p, currlen) == 0) {
699 		return (GSS_S_FAILURE);
700 	}
701 
702 	/* Now the mechanism OID elements */
703 	memcpy(p, dc->mech->elements, dc->mech->length);
704 	p += dc->mech->length;
705 
706 	/* The name length most MSB first */
707 	*p++ = (namelen & 0xff000000) >> 24;
708 	*p++ = (namelen & 0x00ff0000) >> 16;
709 	*p++ = (namelen & 0x0000ff00) >> 8;
710 	*p++ = (namelen & 0x000000ff);
711 
712 	/*
713 	 * We'll now encode the netname oid. Again we'll just use 2 bytes.
714 	 * This is the same encoding that the libgss implementor uses, so
715 	 * we'll just follow along.
716 	 */
717 
718 	*p++ = (__DH_GSS_C_NT_NETNAME->length & 0xff00) >> 8;
719 	*p++ = (__DH_GSS_C_NT_NETNAME->length &0x00ff);
720 
721 	/* The netname oid values */
722 	memcpy(p, __DH_GSS_C_NT_NETNAME->elements,
723 	    __DH_GSS_C_NT_NETNAME->length);
724 
725 	p += __DH_GSS_C_NT_NETNAME->length;
726 
727 	/* Now we copy the netname including the null byte to be safe */
728 	memcpy(p, pname, strlen(pname) + 1);
729 
730 	return (GSS_S_COMPLETE);
731 }
732 
733 /*
734  * Support routine for __dh_internal_release_oid. Return True if
735  * the supplied OID points to the reference OID or if the elements
736  * of the reference OID are the same as the supplied OID. In the
737  * latter case, just free the OID container and set the pointer to it
738  * to GSS_C_NO_OID. Otherwise return false
739  */
740 static int
release_oid(const gss_OID_desc * const ref,gss_OID * oid)741 release_oid(const gss_OID_desc * const ref, gss_OID *oid)
742 {
743 	gss_OID id = *oid;
744 
745 	if (id == ref)
746 		return (TRUE);
747 
748 	/*
749 	 * If some on create a shallow copy free, the structure point to
750 	 * id and set the pointer to it to GSS_C_NO_OID
751 	 */
752 	if (id->elements == ref->elements) {
753 		Free(id);
754 		*oid = GSS_C_NO_OID;
755 		return (TRUE);
756 	}
757 
758 	return (FALSE);
759 }
760 
761 /*
762  * __dh_gss_internal_release_oid: DH support for the gss_internal_relaese_oid
763  * entry. Check that the refence to an oid is one of our mechanisms static
764  * OIDS. If it is return true indicating to libgss that we have handled the
765  * release of that OID. Otherwise we return false and let libgss deal with it.
766  *
767  * The only OIDS we know are the calling mechanism found in the context
768  * and the shared DH_GSS_C_NT_NETNAME name type
769  */
770 OM_uint32
__dh_gss_internal_release_oid(void * ctx,OM_uint32 * minor,gss_OID * oid)771 __dh_gss_internal_release_oid(void *ctx, OM_uint32 *minor, gss_OID *oid)
772 {
773 	dh_context_t dhcxt = (dh_context_t)ctx;
774 
775 	if (minor == 0)
776 		return (GSS_S_CALL_INACCESSIBLE_WRITE);
777 
778 	*minor = DH_SUCCESS;
779 
780 	if (oid == NULL || *oid == NULL)
781 		return (GSS_S_COMPLETE);
782 
783 	if (release_oid(dhcxt->mech, oid))
784 		return (GSS_S_COMPLETE);
785 
786 	if (release_oid(__DH_GSS_C_NT_NETNAME, oid))
787 		return (GSS_S_COMPLETE);
788 
789 	return (GSS_S_FAILURE);
790 }
791