1/*
2 * Copyright (c) 2006-2010 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License").  You may not use this file except in compliance with the
9 * License.  Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
11 *
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22
23#include <libkern/OSAtomic.h>
24#include <mach/mach.h>
25#include <mach/mach_error.h>
26
27#include <servers/bootstrap.h>
28#include <GSS/gssapi.h>
29#include <GSS/gssapi_krb5.h>
30#include <GSS/gssapi_ntlm.h>
31#include <GSS/gssapi_spnego.h>
32
33#include <asl.h>
34#include <bsm/audit.h>
35#include <bsm/audit_session.h>
36#include <limits.h>
37#include <pthread.h>
38#include <pwd.h>
39#include <stdbool.h>
40#include <stdio.h>
41#include <stdlib.h>
42#include <string.h>
43#include <unistd.h>
44
45#include "gssd.h"
46#include "gssd_mach.h"
47
48/*
49 * OID table for supported mechs. This is index by the enumeration type mechtype
50 * found in gss_mach_types.h.
51 */
52static gss_OID  mechtab[] = {
53	NULL, /* Place holder for GSS_KRB5_MECHANISM */
54	NULL, /* Place holder for GSS_SPNEGO_MECHANISM */
55	NULL, /* Place holder for GSS_NTLM_MECHANISM */
56	NULL
57};
58
59int debug = 0;
60
61#define	MAXHOSTNAME 256
62#define	MAXRETRIES 3
63#define	TIMEOUT 100  // 100 microseconds.
64
65volatile int32_t gss_init_errors;
66volatile int32_t gss_accept_errors;
67volatile int32_t server_errors;
68volatile int32_t server_deaths;
69volatile int32_t key_mismatches;
70volatile int32_t successes;
71
72static void	waittime(void);
73void		*server(void *);
74void		*client(void *);
75static void	deallocate(void *, uint32_t);
76static void	server_done();
77static void	waitall(void);
78static void	report_errors(void);
79
80typedef struct s_channel {
81	int client;
82	int failure;
83	pthread_mutex_t lock[1];
84	pthread_cond_t cv[1];
85	gssd_byte_buffer ctoken;
86	mach_msg_type_number_t ctokenCnt;
87	gssd_byte_buffer stoken;
88	mach_msg_type_number_t stokenCnt;
89	gssd_byte_buffer clnt_skey;
90	mach_msg_type_number_t clnt_skeyCnt;
91} *channel_t;
92
93#define CHANNEL_CLOSED 0x1000000
94#define CHANNEL_FAILED(c) ((c)->failure & (~CHANNEL_CLOSED))
95
96int read_channel(int d, channel_t chan);
97int write_channel(int d, channel_t chan);
98int close_channel(int d, channel_t chan);
99
100static char *optstrs[] = {
101	"			if no host is specified, use the local host",
102	"[-b bootstrap label]	client bootstrap name",
103	"[-B bootstrap label]	server bootstrap name",
104	"[-C]			don't canonicalize the host name",
105	"[-d]			debugging",
106	"[-D]			don't use the default credential",
107	"[-e]			exit on mach rpc errors",
108	"[-f flags]		flags for init sec context",
109	"[-h]			print this usage message",
110	"[-H]			don't access home directory",
111	"[-i]			run interactively",
112	"[-k]			use kerberos service principal name, otherwise",
113	"			use host base service name",
114	"[-M retries]		max retries before giving up on server death",
115	"[-m krb5 | spnego |ntlm] mech to use, defaults to krb5",
116	"[-n n]			number of experiments to run",
117	"[-N uid | user | krb5 | ntlm]	name type for client principal",
118	"[-p principal]		use principal for client",
119	"[-R]			exercise  credential refcounting and exit",
120	"[-r realm]		use realm for kerberos",
121	"[-s n]			number of concurrent servers (and clients) to run",
122	"[-S Service principal] Service principal to use",
123	"[-t usecs]		average time to wait in the client",
124	"			This is a random time between 0 and 2*usecs",
125	"[-u user]		credentials to run as",
126	"[-V]			verbose flag. May be repeated",
127	"[-v version]		use version of the protocol",
128};
129
130
131static void
132Usage(void)
133{
134	unsigned int i;
135
136	Log("Usage: %s [options] [host]\n", getprogname());
137	for (i = 0; i < sizeof(optstrs)/sizeof(char *); i++)
138		Log("\t%s\n", optstrs[i]);
139
140	exit(EXIT_FAILURE);
141}
142
143int timeout = TIMEOUT;
144int verbose = 0;
145int max_retries = MAXRETRIES;
146int exitonerror = 0;
147int interactive = 0;
148int version = 0;
149uint32_t uid;
150uint32_t flags;
151uint32_t gssd_flags = GSSD_HOME_ACCESS_OK;
152char *principal="";
153char svcname[1024];
154mach_port_t server_mp = MACH_PORT_NULL;
155mach_port_t client_mp = MACH_PORT_NULL;
156pthread_cond_t num_servers_cv[1];
157pthread_mutex_t num_servers_lock[1];
158int num_servers;
159gssd_mechtype mech = GSSD_KRB5_MECH;
160gssd_nametype name_type = GSSD_MACHINE_UID;
161
162struct gss_name {
163	gssd_nametype nt;
164	gssd_byte_buffer name;
165	uint32_t len;
166} clientp, targetp;
167
168static mach_port_t
169get_gssd_port(void)
170{
171	mach_port_t mp, hgssdp;
172	kern_return_t kr;
173	auditinfo_addr_t ai;
174	au_asid_t asid;
175
176	if (getaudit_addr(&ai, sizeof(auditinfo_addr_t))) {
177		perror("getaudit_addr");
178		exit(EXIT_FAILURE);
179	}
180	asid = ai.ai_asid;
181
182	if (seteuid(0)) {
183		Log("Could not get privilege");
184		exit(EXIT_FAILURE);
185	}
186	kr = host_get_gssd_port(mach_host_self(), &hgssdp);
187	if (kr != KERN_SUCCESS) {
188		Log("host_get_gssd_port(): %s\n", mach_error_string(kr));
189		exit(EXIT_FAILURE);
190	}
191	if (seteuid(uid)) {
192		Log("Could not drop privilege");
193		exit(EXIT_FAILURE);
194	}
195	kr = mach_gss_lookup(hgssdp, uid, asid, &mp);
196	if (kr != KERN_SUCCESS) {
197		Log("Could not lookup port for asid = %d, uid = %d: %s\n",
198		    asid, uid, mach_error_string(kr));
199	}
200
201	mach_port_deallocate(mach_host_self(), hgssdp);
202	return (mp);
203
204}
205
206static int
207do_refcount(gssd_mechtype mt, gssd_nametype nt, char *princ)
208{
209	kern_return_t kret;
210	uint32_t M = 0, m = 0;
211
212	printf("trying to hold credential for %s\n", princ);
213	kret = mach_gss_hold_cred(client_mp, mt, nt, (uint8_t *)princ, (uint32_t)strlen(princ), &M, &m);
214	if (kret == KERN_SUCCESS && M == GSS_S_COMPLETE) {
215		printf("Held credential for %s\n", principal);
216		if (interactive) {
217			printf("Press return to release ...");
218			(void)getchar();
219		}
220		kret = mach_gss_unhold_cred(client_mp, mt, nt, (uint8_t *)princ, (uint32_t)strlen(princ), &M, &m);
221		if (kret == KERN_SUCCESS && M == GSS_S_COMPLETE)
222			printf("Unheld credential for %s\n", principal);
223		else {
224			Log("mach_gss_unhold_cred: kret = %d: %#K %#k", kret, M, mechtab[mt], m);
225			return 1;
226		}
227	} else {
228		Log("mach_gss_hold_cred: kret = %d: %#K %#k", kret, M, mechtab[mt], m);
229		return 1;
230	}
231	return 0;
232}
233
234int main(int argc, char *argv[])
235{
236	char *bname_server = NULL, *bname_client = NULL;
237	int i, j, ch;
238	int error;
239	int num = 1;
240	int Servers = 1;
241	int use_kerberos = 0;
242	int refcnt = 0;
243	pthread_t thread;
244	pthread_attr_t attr[1];
245	char hostbuf[MAXHOSTNAME];
246	char *host = hostbuf;
247	char *realm = NULL;
248	char *ServicePrincipal = NULL;
249	struct passwd *pent = NULL;
250	kern_return_t kr;
251
252	uid = getuid();
253	if (seteuid(uid)) {
254		Log("Could not drop privilege");
255		exit(EXIT_FAILURE);
256	}
257
258	setprogname(argv[0]);
259
260	/* Set up mech table */
261	mechtab[GSSD_KRB5_MECH] = GSS_KRB5_MECHANISM;
262	mechtab[GSSD_SPNEGO_MECH] = GSS_SPNEGO_MECHANISM;
263	mechtab[GSSD_NTLM_MECH] = GSS_NTLM_MECHANISM;
264
265	while ((ch = getopt(argc, argv, "b:B:CdDef:hHikN:n:M:m:p:r:Rs:S:t:u:v:V")) != -1) {
266		switch (ch) {
267		case 'b':
268			bname_client = optarg;
269			break;
270		case 'B':
271			bname_server = optarg;
272			break;
273		case 'C':
274			gssd_flags |= GSSD_NO_CANON;
275			break;
276		case 'd':
277			debug++;
278			break;
279		case 'D':
280			gssd_flags |= GSSD_NO_DEFAULT;
281			break;
282		case 'e':
283			exitonerror = 1;
284			break;
285		case 'f':
286			flags |= atoi(optarg);
287			break;
288		case 'H':
289			gssd_flags &= ~GSSD_HOME_ACCESS_OK;
290			break;
291		case 'i':
292			interactive = 1;
293			break;
294		case 'k':
295			use_kerberos = 1;
296			break;
297		case 'M':
298			max_retries = atoi(optarg);
299			break;
300		case 'm':
301			if (strcmp(optarg, "krb5") == 0)
302				mech = GSSD_KRB5_MECH;
303			else if (strcmp(optarg, "spnego") == 0)
304				mech = GSSD_SPNEGO_MECH;
305			else if (strcmp(optarg, "ntlm") == 0)
306				mech = GSSD_NTLM_MECH;
307			else {
308				Log("Unavailable gss mechanism %s\n", optarg);
309				exit(EXIT_FAILURE);
310			}
311			break;
312		case 'n':
313			num = atoi(optarg);
314			break;
315
316		case 'N':
317			if (strcmp(optarg, "uid") == 0)
318				name_type = GSSD_MACHINE_UID;
319			else if (strcmp(optarg, "suid") == 0)
320				name_type = GSSD_STRING_UID;
321			else if (strcmp(optarg, "user") == 0)
322				name_type = GSSD_USER;
323			else if (strcmp(optarg, "krb5") == 0)
324				name_type = GSSD_KRB5_PRINCIPAL;
325			else if (strcmp(optarg, "ntlm") == 0)
326				name_type = GSSD_NTLM_PRINCIPAL;
327			else {
328				Log("Unsupported name type %s\n", optarg);
329				exit(EXIT_FAILURE);
330			}
331			break;
332		case 'p':
333			principal = optarg;
334			break;
335		case 'r':
336			realm = optarg;
337			break;
338		case 'R':
339			refcnt = 1;
340			break;
341		case 's':
342			Servers = atoi(optarg);
343			break;
344		case 'S':
345			ServicePrincipal = optarg;
346			break;
347		case 't':
348			timeout = atoi(optarg);
349			break;
350		case 'u':
351			pent = getpwnam(optarg);
352			if (pent)
353				uid = pent->pw_uid;
354			else
355				Log("Could no find user %s\n", optarg);
356			break;
357		case 'V':
358			verbose++;
359			break;
360		case 'v':
361			version = atoi(optarg);
362			break;
363		default:
364			Usage();
365			break;
366		}
367	}
368	argc -= optind;
369	argv += optind;
370
371	if (argc == 0) {
372		gethostname(hostbuf, MAXHOSTNAME);
373	} else if (argc == 1) {
374		host = argv[0];
375	} else {
376		Usage();
377	}
378
379
380	if (principal == NULL || *principal == '\0') {
381		if (pent == NULL)
382			pent = getpwuid(uid);
383		principal = pent->pw_name;
384		name_type = GSSD_USER;
385	}
386
387	clientp.nt = name_type;
388	switch (name_type) {
389		case GSSD_USER:
390		case GSSD_STRING_UID:
391		case GSSD_KRB5_PRINCIPAL:
392		case GSSD_NTLM_PRINCIPAL:
393			clientp.name = (gssd_byte_buffer) principal;
394			clientp.len = (uint32_t) strlen(principal);
395			break;
396		default:
397			Log("Unsupported name type for principal %s\n", principal);
398			exit(EXIT_FAILURE);
399			break;
400	}
401	printf("Using creds for %s  host=%s\n", principal, host);
402
403	if (bname_client) {
404		kr = bootstrap_look_up(bootstrap_port, bname_client, &client_mp);
405		if (kr != KERN_SUCCESS) {
406			Log("bootstrap_look_up(): %s\n", bootstrap_strerror(kr));
407			exit(EXIT_FAILURE);
408		}
409	} else {
410		client_mp = get_gssd_port();
411	}
412
413	if (!MACH_PORT_VALID(client_mp)) {
414		Log("Could not get a valid client port (%d)\n", client_mp);
415		exit(EXIT_FAILURE);
416	}
417
418	if (refcnt)
419		return do_refcount(mech, name_type, principal);
420
421	if (ServicePrincipal)
422		strlcpy(svcname, ServicePrincipal, sizeof(svcname));
423	else if (use_kerberos) {
424		strlcpy(svcname, "nfs/", sizeof(svcname));
425		strlcat(svcname, host, sizeof(svcname));
426		if (realm) {
427			strlcat(svcname, "@", sizeof(svcname));
428			strlcat(svcname, realm, sizeof(svcname));
429		}
430	} else {
431		strlcpy(svcname, "nfs@", sizeof(svcname));
432		strlcat(svcname, host, sizeof(svcname));
433	}
434
435	if (!use_kerberos) {
436		targetp.nt = GSSD_HOSTBASED;
437		targetp.name = (gssd_byte_buffer)svcname;
438		targetp.len = (uint32_t) strlen(svcname);
439	}
440	printf("Service name = %s\n", svcname);
441
442	if (bname_server) {
443		kr = bootstrap_look_up(bootstrap_port, bname_server, &server_mp);
444		if (kr != KERN_SUCCESS) {
445			Log("bootstrap_look_up(): %s\n", bootstrap_strerror(kr));
446			exit(EXIT_FAILURE);
447		}
448	} else {
449		server_mp = get_gssd_port();
450	}
451
452	if (!MACH_PORT_VALID(server_mp)) {
453		Log("Could not get a valid server port (%d)\n", server_mp);
454		exit(EXIT_FAILURE);
455	}
456
457	if (interactive) {
458		printf("Hit enter to start ");
459		(void) getchar();
460	}
461
462	pthread_attr_init(attr);
463	pthread_attr_setdetachstate(attr, PTHREAD_CREATE_DETACHED);
464
465	pthread_mutex_init(num_servers_lock, NULL);
466	pthread_cond_init(num_servers_cv, NULL);
467
468	for (i = 0; i < num; i++) {
469		num_servers = Servers;
470		for (j = 0; j < num_servers; j++) {
471			error = pthread_create(&thread, attr, server, NULL);
472			if (error)
473				Log("Could not start server: %s\n",
474						strerror(error));
475		}
476		waitall();
477	}
478	report_errors();
479
480	pthread_attr_destroy(attr);
481
482	kr = mach_port_deallocate(mach_task_self(), client_mp);
483	if (kr != KERN_SUCCESS) {
484		Log("Could not delete send right!\n");
485	}
486
487	kr = mach_port_deallocate(mach_task_self(), server_mp);
488	if (kr != KERN_SUCCESS) {
489		Log("Could not delete send right!\n");
490	}
491
492	if (interactive) {
493		printf("Hit enter to stop\n");
494		(void) getchar();
495	}
496
497	return (0);
498}
499
500static void
501waittime(void)
502{
503	struct timespec to;
504
505	if (timeout == 0)
506		return;
507
508	to.tv_sec = 0;
509	to.tv_nsec = (random() % (2*1000*timeout));
510
511	nanosleep(&to, NULL);
512}
513
514
515static void
516report_errors(void)
517{
518	printf("gss_init_errors %d\n", gss_init_errors);
519	printf("gss_accept_errors %d\n", gss_accept_errors);
520	printf("server_errors %d\n", server_errors);
521	printf("server_deaths %d\n", server_deaths);
522}
523
524static void
525server_done(void)
526{
527	pthread_mutex_lock(num_servers_lock);
528	num_servers-- ;
529	if (num_servers == 0)
530		pthread_cond_signal(num_servers_cv);
531	pthread_mutex_unlock(num_servers_lock);
532}
533
534static void
535waitall(void)
536{
537	pthread_mutex_lock(num_servers_lock);
538	while (num_servers > 0)
539		pthread_cond_wait(num_servers_cv, num_servers_lock);
540	pthread_mutex_unlock(num_servers_lock);
541}
542
543static void
544deallocate(void *addr, uint32_t size)
545{
546	if (addr == NULL || size == 0)
547		return;
548
549	(void) vm_deallocate(mach_task_self(), (vm_address_t)addr, (vm_size_t)size);
550}
551
552int read_channel(int d, channel_t chan)
553{
554	pthread_mutex_lock(chan->lock);
555	while (chan->client != d && !chan->failure)
556		pthread_cond_wait(chan->cv, chan->lock);
557
558	waittime();
559
560	if (chan->failure) {
561		pthread_mutex_unlock(chan->lock);
562		return (-1);
563	}
564
565	return (0);
566}
567
568int write_channel(int d, channel_t chan)
569{
570	if (chan->client != d)
571		Log("Writing out of turn\n");
572
573	chan->client = !d;
574	pthread_cond_signal(chan->cv);
575	pthread_mutex_unlock(chan->lock);
576
577	return (0);
578}
579
580int close_channel(int d, channel_t chan)
581{
582	int rc;
583
584	pthread_mutex_lock(chan->lock);
585	while (chan->client != d && !chan->failure)
586		pthread_cond_wait(chan->cv, chan->lock);
587
588	rc = chan->failure;
589
590	chan->failure |= CHANNEL_CLOSED;
591	chan->client = d;
592	pthread_cond_signal(chan->cv);
593	pthread_mutex_unlock(chan->lock);
594
595	return (rc);
596}
597
598void *client(void *arg)
599{
600	channel_t channel = (channel_t)arg;
601	uint32_t major_stat;
602	uint32_t minor_stat;
603	uint32_t rflags;
604	uint32_t inout_gssd_flags;
605	gssd_cred cred_handle = (gssd_cred) (uintptr_t)GSS_C_NO_CREDENTIAL;
606	gssd_ctx gss_context = (gssd_ctx) (uintptr_t)GSS_C_NO_CONTEXT;
607	kern_return_t kr;
608	int gss_error = 0;
609	int retry_count = 0;
610	char display_name[128];
611
612	do {
613		if (read_channel(1, channel)) {
614			Log("Bad read from server\n");
615			return (NULL);
616		}
617
618		if (verbose)
619			Debug("Calling mach_gss_init_sec_context from %p\n",
620				(void *) pthread_self());
621
622		deallocate(channel->ctoken, channel->ctokenCnt);
623		channel->ctoken = (gssd_byte_buffer)GSS_C_NO_BUFFER;
624		channel->ctokenCnt = 0;
625retry:
626		switch (version) {
627		case 0:
628		case 1:
629			kr = mach_gss_init_sec_context(
630						       client_mp,
631						       mech,
632						       channel->stoken, channel->stokenCnt,
633						       uid,
634						       principal,
635						       svcname,
636						       flags,
637						       gssd_flags,
638						       &gss_context,
639						       &cred_handle,
640						       &rflags,
641						       &channel->clnt_skey, &channel->clnt_skeyCnt,
642						       &channel->ctoken, &channel->ctokenCnt,
643						       &major_stat,
644						       &minor_stat);
645			break;
646		case 2:
647			inout_gssd_flags = gssd_flags;
648			kr = mach_gss_init_sec_context_v2(
649							  client_mp,
650							  mech,
651							  channel->stoken, channel->stokenCnt,
652							  uid,
653							  clientp.nt,
654							  clientp.name,
655							  clientp.len,
656							  targetp.nt,
657							  targetp.name,
658							  targetp.len,
659							  flags,
660							  &inout_gssd_flags,
661							  &gss_context,
662							  &cred_handle,
663							  &rflags,
664							  &channel->clnt_skey, &channel->clnt_skeyCnt,
665							  &channel->ctoken, &channel->ctokenCnt,
666							  display_name,
667							  &major_stat,
668							  &minor_stat);
669			if (verbose && kr == KERN_SUCCESS && major_stat ==  GSS_S_COMPLETE)
670				Debug("Got client identity of '%s'\n", display_name);
671			break;
672		default:
673			Log("Unsupported version %d\n", version);
674			exit(1);
675			break;
676		}
677
678		if (kr != KERN_SUCCESS) {
679			OSAtomicIncrement32(&server_errors);
680			Log("gsstest client: %s\n", mach_error_string(kr));
681			if (exitonerror)
682				exit(1);
683			if (kr == MIG_SERVER_DIED) {
684				OSAtomicIncrement32(&server_deaths);
685				if (gss_context == (uint32_t)(uintptr_t)GSS_C_NO_CONTEXT &&
686					retry_count < max_retries) {
687					retry_count++;
688					goto retry;
689				}
690			}
691
692			channel->failure = 1;
693			write_channel(1, channel);
694			return (NULL);
695		}
696
697		gss_error = (major_stat != GSS_S_COMPLETE &&
698					major_stat != GSS_S_CONTINUE_NEEDED);
699		if (verbose > 1) {
700			Debug("\tcred = 0x%0x\n", (int) cred_handle);
701			Debug("\tclnt_gss_context = 0x%0x\n",
702				(int) gss_context);
703			Debug("\ttokenCnt = %d\n", (int) channel->ctokenCnt);
704			if (verbose > 2)
705				HexDump((char *) channel->ctoken,
706					 (uint32_t) channel->ctokenCnt);
707		}
708
709		channel->failure = gss_error;
710		write_channel(1, channel);
711	} while (major_stat == GSS_S_CONTINUE_NEEDED);
712
713
714	if (gss_error) {
715		OSAtomicIncrement32(&gss_init_errors);
716		Log("mach_gss_int_sec_context: %#K %#k\n", major_stat, mechtab[mech], minor_stat);
717	}
718
719	close_channel(1, channel);
720	return (NULL);
721}
722
723void *server(void *arg __attribute__((unused)))
724{
725	struct s_channel args;
726	channel_t channel = &args;
727	pthread_t client_thr;
728	int error;
729	uint32_t major_stat = GSS_S_FAILURE;
730	uint32_t minor_stat;
731	uint32_t rflags;
732	uint32_t inout_gssd_flags;
733	gssd_cred cred_handle = (gssd_cred) (uintptr_t)GSS_C_NO_CREDENTIAL;
734	gssd_ctx gss_context = (gssd_ctx) (uintptr_t)GSS_C_NO_CONTEXT;
735	uint32_t clnt_uid;
736	uint32_t clnt_gids[NGROUPS_MAX];
737	uint32_t clnt_ngroups;
738	gssd_byte_buffer svc_skey = NULL;
739	mach_msg_type_number_t svc_skeyCnt = 0;
740	kern_return_t kr;
741	int retry_count = 0;
742
743	channel->client = 1;
744	channel->failure = 0;
745	pthread_mutex_init(channel->lock, NULL);
746	pthread_cond_init(channel->cv, NULL);
747	channel->ctoken = (gssd_byte_buffer) GSS_C_NO_BUFFER;
748	channel->ctokenCnt = 0;
749	channel->stoken = (gssd_byte_buffer) GSS_C_NO_BUFFER;
750	channel->stokenCnt = 0;
751	channel->clnt_skey = (gssd_byte_buffer) GSS_C_NO_BUFFER;
752	channel->clnt_skeyCnt = 0;
753
754	// Kick off a client;
755	error = pthread_create(&client_thr, NULL, client, channel);
756	if (error) {
757		Log("Could not start client: %s\n", strerror(error));
758		return NULL;
759	}
760
761
762	do {
763		if (read_channel(0, channel) == -1) {
764			Log("Bad read from client\n");
765			goto out;
766		}
767
768		deallocate(channel->stoken, channel->stokenCnt);
769		channel->stoken = (gssd_byte_buffer)GSS_C_NO_BUFFER;
770		channel->stokenCnt = 0;
771
772		if (verbose)
773			Debug("Calling mach_gss_accept_sec_contex %p\n",
774				(void *) pthread_self());
775
776retry:		switch (version) {
777		case 0:
778		case 1:
779			kr = mach_gss_accept_sec_context(
780				server_mp,
781				channel->ctoken, channel->ctokenCnt,
782				svcname,
783				gssd_flags,
784				&gss_context,
785				&cred_handle,
786				&rflags,
787				&clnt_uid,
788				clnt_gids,
789				&clnt_ngroups,
790				&svc_skey, &svc_skeyCnt,
791				&channel->stoken, &channel->stokenCnt,
792				&major_stat,
793				&minor_stat);
794			break;
795		case 2:
796			inout_gssd_flags = gssd_flags;
797			kr = mach_gss_accept_sec_context_v2(
798				server_mp,
799				channel->ctoken, channel->ctokenCnt,
800				GSSD_STRING_NAME,
801				(uint8_t *)svcname,
802				(uint32_t) strlen(svcname)+1,
803				&inout_gssd_flags,
804				&gss_context,
805				&cred_handle,
806				&rflags,
807				&clnt_uid,
808				clnt_gids,
809				&clnt_ngroups,
810				&svc_skey, &svc_skeyCnt,
811				&channel->stoken, &channel->stokenCnt,
812				&major_stat,
813				&minor_stat);
814			break;
815		default:
816			Log("Unsupported version %d\n", version);
817			exit(1);
818			break;
819		}
820
821		if (kr != KERN_SUCCESS) {
822			OSAtomicIncrement32(&server_errors);
823			Log("gsstest server: %s\n", mach_error_string(kr));
824			if (exitonerror)
825				exit(1);
826			if (kr == MIG_SERVER_DIED) {
827				OSAtomicIncrement32(&server_deaths);
828				if (gss_context == (uint32_t)(uintptr_t)GSS_C_NO_CONTEXT &&
829					retry_count < max_retries) {
830					retry_count++;
831					goto retry;
832				}
833			}
834
835			channel->failure = 1;
836			write_channel(0, channel);
837			goto out;
838		}
839
840		error = (major_stat != GSS_S_COMPLETE &&
841					major_stat != GSS_S_CONTINUE_NEEDED);
842
843		channel->failure = error;
844		write_channel(0, channel);
845	} while (major_stat == GSS_S_CONTINUE_NEEDED);
846
847	if (error) {
848		OSAtomicIncrement32(&gss_accept_errors);
849		Log("mach_gss_accept_sec_context: %#K %#k", major_stat, mechtab[mech], minor_stat);
850	}
851out:
852	close_channel(0, channel);
853
854	pthread_join(client_thr, NULL);
855
856	if (major_stat == GSS_S_COMPLETE && !CHANNEL_FAILED(channel)) {
857		if (svc_skeyCnt != channel->clnt_skeyCnt ||
858			memcmp(svc_skey, channel->clnt_skey, svc_skeyCnt)) {
859			Log("Session keys don't match!\n");
860			Log("\tClient key length = %d\n",
861				 channel->clnt_skeyCnt);
862			HexDump((char *) channel->clnt_skey,
863				(uint32_t) channel->clnt_skeyCnt);
864			Log("\tServer key length = %d\n", svc_skeyCnt);
865			HexDump((char *) svc_skey, (uint32_t) svc_skeyCnt);
866			if (uid != clnt_uid)
867				Log("Wrong uid. got %d expected %d\n",
868					clnt_uid, uid);
869		}
870		else if (verbose) {
871			Debug("\tSession key length = %d\n", svc_skeyCnt);
872			HexDump((char *) svc_skey, (uint32_t) svc_skeyCnt);
873			Debug("\tReturned uid = %d\n", uid);
874		}
875	} else if (verbose > 1) {
876		Debug("Failed major status = %d\n", major_stat);
877		Debug("Channel failure = %x\n", channel->failure);
878	}
879
880	deallocate(svc_skey, svc_skeyCnt);
881	deallocate(channel->ctoken, channel->ctokenCnt);
882	deallocate(channel->stoken, channel->stokenCnt);
883	deallocate(channel->clnt_skey, channel->clnt_skeyCnt);
884	pthread_mutex_destroy(channel->lock);
885	pthread_cond_destroy(channel->cv);
886
887	server_done();
888
889	return (NULL);
890}
891