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