xref: /illumos-gate/usr/src/cmd/smbsrv/smbd/smbd_authsvc.c (revision 533affcbc7fc4d0c8132976ea454aaa715fe2307)
1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright 2017 Nexenta Systems, Inc.  All rights reserved.
14  * Copyright 2022-2023 RackTop Systems, Inc.
15  */
16 
17 /*
18  * SMB authentication service
19  *
20  * This service listens on a local AF_UNIX socket, spawning a
21  * thread to service each connection.  The client-side of such
22  * connections is the in-kernel SMB service, with an open and
23  * connect done in the SMB session setup handler.
24  */
25 
26 #include <sys/types.h>
27 #include <stdlib.h>
28 #include <errno.h>
29 #include <string.h>
30 #include <strings.h>
31 #include <unistd.h>
32 #include <signal.h>
33 #include <stdio.h>
34 #include <note.h>
35 #include <net/if.h>
36 #include <net/route.h>
37 #include <sys/sockio.h>
38 #include <sys/socket.h>
39 #include <sys/un.h>
40 #include <netinet/in.h>
41 #include <fcntl.h>
42 #include <pthread.h>
43 #include <syslog.h>
44 #include <ucred.h>
45 #include <priv.h>
46 
47 #include <smbsrv/libsmb.h>
48 #include <netsmb/spnego.h>
49 
50 #include "smbd.h"
51 #include "smbd_authsvc.h"
52 
53 /* Arbitrary value outside the (small) range of valid OIDs */
54 #define	special_mech_raw_NTLMSSP	(spnego_mech_oid_NTLMSSP + 100)
55 
56 static struct sockaddr_un smbauth_sockname = {
57 	AF_UNIX, SMB_AUTHSVC_SOCKNAME };
58 
59 typedef struct spnego_mech_handler {
60 	int mh_oid; /* SPNEGO_MECH_OID */
61 	int (*mh_init)(authsvc_context_t *);
62 	int (*mh_work)(authsvc_context_t *);
63 	void (*mh_fini)(authsvc_context_t *);
64 } spnego_mech_handler_t;
65 
66 static int smbd_authsock_create(void);
67 static void smbd_authsock_destroy(void);
68 static void *smbd_authsvc_listen(void *);
69 static void *smbd_authsvc_work(void *);
70 static void smbd_authsvc_flood(void);
71 
72 static int smbd_authsvc_oldreq(authsvc_context_t *);
73 static int smbd_authsvc_clinfo(authsvc_context_t *);
74 static int smbd_authsvc_esfirst(authsvc_context_t *);
75 static int smbd_authsvc_esnext(authsvc_context_t *);
76 static int smbd_authsvc_escmn(authsvc_context_t *);
77 static int smbd_authsvc_newmech(authsvc_context_t *);
78 static int smbd_authsvc_gettoken(authsvc_context_t *);
79 static int smbd_raw_ntlmssp_esfirst(authsvc_context_t *);
80 static int smbd_raw_ntlmssp_esnext(authsvc_context_t *);
81 
82 /*
83  * We can get relatively large tokens now, thanks to krb5 PAC.
84  * Might be better to size these buffers dynamically, but these
85  * are all short-lived so not bothering with that for now.
86  */
87 int smbd_authsvc_bufsize = 65000;
88 
89 static mutex_t smbd_authsvc_mutex = DEFAULTMUTEX;
90 
91 /*
92  * The maximum number of authentication thread is limited by the
93  * smbsrv smb_threshold_...(->sv_ssetup_ct) mechanism.  However,
94  * due to occasional delays closing these auth. sockets, we need
95  * a little "slack" on the number of threads we'll allow, as
96  * compared with the in-kernel limit.  We could perhaps just
97  * remove this limit now, but want it for extra safety.
98  */
99 int smbd_authsvc_maxthread = SMB_AUTHSVC_MAXTHREAD + 32;
100 int smbd_authsvc_thrcnt = 0;	/* current thrcnt */
101 int smbd_authsvc_hiwat = 0;	/* largest thrcnt seen */
102 #ifdef DEBUG
103 int smbd_authsvc_slowdown = 0;
104 #endif
105 
106 /*
107  * These are the mechanisms we support, in order of preference.
108  * But note: it's really the _client's_ preference that matters.
109  * See &pref in the spnegoIsMechTypeAvailable() calls below.
110  * Careful with this table; the code below knows its format and
111  * may skip the fist two entries to omit Kerberos.
112  */
113 static const spnego_mech_handler_t
114 mech_table[] = {
115 	{
116 		spnego_mech_oid_Kerberos_V5,
117 		smbd_krb5ssp_init,
118 		smbd_krb5ssp_work,
119 		smbd_krb5ssp_fini
120 	},
121 	{
122 		spnego_mech_oid_Kerberos_V5_Legacy,
123 		smbd_krb5ssp_init,
124 		smbd_krb5ssp_work,
125 		smbd_krb5ssp_fini
126 	},
127 #define	MECH_TBL_IDX_NTLMSSP	2
128 	{
129 		spnego_mech_oid_NTLMSSP,
130 		smbd_ntlmssp_init,
131 		smbd_ntlmssp_work,
132 		smbd_ntlmssp_fini
133 	},
134 	{
135 		/* end marker */
136 		spnego_mech_oid_NotUsed,
137 		NULL, NULL, NULL
138 	},
139 };
140 
141 static const spnego_mech_handler_t
142 smbd_auth_mech_raw_ntlmssp = {
143 	special_mech_raw_NTLMSSP,
144 	smbd_ntlmssp_init,
145 	smbd_ntlmssp_work,
146 	smbd_ntlmssp_fini
147 };
148 
149 
150 /*
151  * Start the authentication service.
152  * Returns non-zero on error.
153  */
154 int
155 smbd_authsvc_start(void)
156 {
157 	pthread_attr_t	attr;
158 	pthread_t	tid;
159 	int		rc;
160 
161 	rc = smbd_authsock_create();
162 	if (rc)
163 		return (rc);
164 
165 	(void) pthread_attr_init(&attr);
166 	(void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
167 	rc = pthread_create(&tid, &attr, smbd_authsvc_listen, &smbd);
168 	(void) pthread_attr_destroy(&attr);
169 	if (rc) {
170 		smbd_authsock_destroy();
171 		return (rc);
172 	}
173 
174 	smbd.s_authsvc_tid = tid;
175 	return (0);
176 }
177 
178 void
179 smbd_authsvc_stop(void)
180 {
181 
182 	if (smbd.s_authsvc_tid != 0) {
183 		(void) pthread_kill(smbd.s_authsvc_tid, SIGTERM);
184 		smbd.s_authsvc_tid = 0;
185 	}
186 }
187 
188 static int
189 smbd_authsock_create(void)
190 {
191 	int sock = -1;
192 
193 	sock = socket(AF_UNIX, SOCK_STREAM, 0);
194 	if (sock < 0) {
195 		smbd_report("authsvc, socket create failed, %d", errno);
196 		return (errno);
197 	}
198 
199 	(void) unlink(smbauth_sockname.sun_path);
200 	if (bind(sock, (struct sockaddr *)&smbauth_sockname,
201 	    sizeof (smbauth_sockname)) < 0) {
202 		smbd_report("authsvc, socket bind failed, %d", errno);
203 		(void) close(sock);
204 		return (errno);
205 	}
206 
207 	if (listen(sock, SOMAXCONN) < 0) {
208 		smbd_report("authsvc, socket listen failed, %d", errno);
209 		(void) close(sock);
210 		return (errno);
211 	}
212 
213 	smbd.s_authsvc_sock = sock;
214 	return (0);
215 }
216 
217 static void
218 smbd_authsock_destroy(void)
219 {
220 	int fid;
221 
222 	if ((fid = smbd.s_authsvc_sock) != -1) {
223 		smbd.s_authsvc_sock = -1;
224 		(void) close(fid);
225 	}
226 }
227 
228 #ifndef FKSMBD
229 /*
230  * Decide whether to communicate with the client on this AF_UNIX socket.
231  * Normally the caller should be the (in-kernel) SMB service which has
232  * (typically) all privileges.  We test for PRIV_SYS_SMB here, which
233  * only the SMB service should have.
234  */
235 static boolean_t
236 authsock_has_priv(int sock)
237 {
238 	ucred_t *uc = NULL;
239 	const priv_set_t *ps = NULL;
240 	boolean_t ret = B_FALSE;
241 	pid_t  clpid;
242 
243 	if (getpeerucred(sock, &uc) != 0) {
244 		smbd_report("authsvc: getpeerucred err %d", errno);
245 		return (B_FALSE);
246 	}
247 	clpid = ucred_getpid(uc);
248 	ps = ucred_getprivset(uc, PRIV_EFFECTIVE);
249 	if (ps == NULL) {
250 		smbd_report("authsvc: ucred_getprivset failed");
251 		goto out;
252 	}
253 
254 	/*
255 	 * Require sys_smb priv.
256 	 */
257 	if (priv_ismember(ps, PRIV_SYS_SMB)) {
258 		ret = B_TRUE;
259 		goto out;
260 	}
261 
262 	if (smbd.s_debug) {
263 		smbd_report("authsvc: non-privileged client "
264 		    "PID = %d UID = %d",
265 		    (int)clpid, ucred_getruid(uc));
266 	}
267 
268 out:
269 	/* ps is free'd with the ucred */
270 	if (uc != NULL)
271 		ucred_free(uc);
272 
273 	return (ret);
274 }
275 #endif
276 
277 
278 static void *
279 smbd_authsvc_listen(void *arg)
280 {
281 	authsvc_context_t *ctx;
282 	pthread_attr_t	attr;
283 	pthread_t	tid;
284 	socklen_t	slen;
285 	int		ls, ns, rc;
286 
287 	_NOTE(ARGUNUSED(arg))
288 
289 	(void) pthread_attr_init(&attr);
290 	(void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
291 
292 	ls = smbd.s_authsvc_sock;
293 	for (;;) {
294 
295 		slen = 0;
296 		ns = accept(ls, NULL, &slen);
297 		if (ns < 0) {
298 			switch (errno) {
299 			case ECONNABORTED:
300 				continue;
301 			case EINTR:
302 				/* normal termination */
303 				goto out;
304 			default:
305 				smbd_report("authsvc, socket accept failed,"
306 				    " %d", errno);
307 				goto out;
308 			}
309 		}
310 
311 #ifndef FKSMBD
312 		if (!authsock_has_priv(ns)) {
313 			close(ns);
314 			continue;
315 		}
316 #endif
317 
318 		/*
319 		 * Limit the number of auth. sockets
320 		 * (and the threads that service them).
321 		 */
322 		(void) mutex_lock(&smbd_authsvc_mutex);
323 		if (smbd_authsvc_thrcnt >= smbd_authsvc_maxthread) {
324 			(void) mutex_unlock(&smbd_authsvc_mutex);
325 			(void) close(ns);
326 			smbd_authsvc_flood();
327 			continue;
328 		}
329 		smbd_authsvc_thrcnt++;
330 		if (smbd_authsvc_hiwat < smbd_authsvc_thrcnt)
331 			smbd_authsvc_hiwat = smbd_authsvc_thrcnt;
332 		(void) mutex_unlock(&smbd_authsvc_mutex);
333 
334 		ctx = smbd_authctx_create();
335 		if (ctx == NULL) {
336 			smbd_report("authsvc, can't allocate context");
337 			(void) mutex_lock(&smbd_authsvc_mutex);
338 			smbd_authsvc_thrcnt--;
339 			(void) mutex_unlock(&smbd_authsvc_mutex);
340 			(void) close(ns);
341 			smbd_nomem();
342 		}
343 		ctx->ctx_socket = ns;
344 
345 		rc = pthread_create(&tid, &attr, smbd_authsvc_work, ctx);
346 		if (rc) {
347 			smbd_report("authsvc, thread create failed, %d", rc);
348 			(void) mutex_lock(&smbd_authsvc_mutex);
349 			smbd_authsvc_thrcnt--;
350 			(void) mutex_unlock(&smbd_authsvc_mutex);
351 			smbd_authctx_destroy(ctx);
352 			ctx = NULL;
353 			smbd_nomem();
354 		}
355 		ctx = NULL; /* given to the new thread or destroyed */
356 		(void) pthread_detach(tid);
357 	}
358 
359 out:
360 	(void) pthread_attr_destroy(&attr);
361 	smbd_authsock_destroy();
362 	return (NULL);
363 }
364 
365 static void
366 smbd_authsvc_flood(void)
367 {
368 	static uint_t count;
369 	static time_t last_report;
370 	time_t now = time(NULL);
371 
372 	count++;
373 	if (last_report + 60 < now) {
374 		last_report = now;
375 		smbd_report("authsvc: flooded %u", count);
376 		count = 0;
377 	}
378 }
379 
380 authsvc_context_t *
381 smbd_authctx_create(void)
382 {
383 	authsvc_context_t *ctx;
384 
385 	ctx = malloc(sizeof (*ctx));
386 	if (ctx == NULL)
387 		return (NULL);
388 	bzero(ctx, sizeof (*ctx));
389 
390 	ctx->ctx_irawlen = smbd_authsvc_bufsize;
391 	ctx->ctx_irawbuf = malloc(ctx->ctx_irawlen);
392 	ctx->ctx_orawlen = smbd_authsvc_bufsize;
393 	ctx->ctx_orawbuf = malloc(ctx->ctx_orawlen);
394 	if (ctx->ctx_irawbuf == NULL || ctx->ctx_orawbuf == NULL)
395 		goto errout;
396 
397 	ctx->ctx_ibodylen = smbd_authsvc_bufsize;
398 	ctx->ctx_ibodybuf = malloc(ctx->ctx_ibodylen);
399 	ctx->ctx_obodylen = smbd_authsvc_bufsize;
400 	ctx->ctx_obodybuf = malloc(ctx->ctx_obodylen);
401 	if (ctx->ctx_ibodybuf == NULL || ctx->ctx_obodybuf == NULL)
402 		goto errout;
403 
404 	return (ctx);
405 
406 errout:
407 	smbd_authctx_destroy(ctx);
408 	return (NULL);
409 }
410 
411 void
412 smbd_authctx_destroy(authsvc_context_t *ctx)
413 {
414 	if (ctx->ctx_socket != -1) {
415 		(void) close(ctx->ctx_socket);
416 		ctx->ctx_socket = -1;
417 	}
418 
419 	if (ctx->ctx_token != NULL)
420 		smb_token_destroy(ctx->ctx_token);
421 
422 	if (ctx->ctx_itoken != NULL)
423 		spnegoFreeData(ctx->ctx_itoken);
424 	if (ctx->ctx_otoken != NULL)
425 		spnegoFreeData(ctx->ctx_otoken);
426 
427 	free(ctx->ctx_irawbuf);
428 	free(ctx->ctx_orawbuf);
429 	free(ctx->ctx_ibodybuf);
430 	free(ctx->ctx_obodybuf);
431 
432 	free(ctx);
433 }
434 
435 /*
436  * Limit how long smbd_authsvc_work will wait for the client to
437  * send us the next part of the authentication sequence.
438  */
439 static struct timeval recv_tmo = { 30, 0 };
440 
441 /*
442  * Also set a timeout for send, where we're sending a response to
443  * the client side (in smbsrv).  That should always be waiting in
444  * recv by the time we send, so a short timeout is OK.
445  */
446 static struct timeval send_tmo = { 15, 0 };
447 
448 static void *
449 smbd_authsvc_work(void *arg)
450 {
451 	authsvc_context_t *ctx = arg;
452 	smb_lsa_msg_hdr_t	hdr;
453 	int sock = ctx->ctx_socket;
454 	int len, rc;
455 
456 	if (setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO,
457 	    (char *)&send_tmo,  sizeof (send_tmo)) != 0) {
458 		smbd_report("authsvc_work: set set timeout: %m");
459 		goto out;
460 	}
461 
462 	if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO,
463 	    (char *)&recv_tmo,  sizeof (recv_tmo)) != 0) {
464 		smbd_report("authsvc_work: set recv timeout: %m");
465 		goto out;
466 	}
467 
468 	for (;;) {
469 
470 		len = recv(sock, &hdr, sizeof (hdr), MSG_WAITALL);
471 		if (len <= 0) {
472 			/* normal termination */
473 			break;
474 		}
475 		if (len != sizeof (hdr)) {
476 			smbd_report("authsvc_work: read header failed");
477 			break;
478 		}
479 
480 		if (hdr.lmh_msglen > smbd_authsvc_bufsize) {
481 			smbd_report("authsvc_work: msg too large");
482 			break;
483 		}
484 
485 		if (hdr.lmh_msglen > 0) {
486 			len = recv(sock, ctx->ctx_irawbuf, hdr.lmh_msglen,
487 			    MSG_WAITALL);
488 			if (len != hdr.lmh_msglen) {
489 				smbd_report("authsvc_work: read mesg failed");
490 				break;
491 			}
492 		}
493 		ctx->ctx_irawtype = hdr.lmh_msgtype;
494 		ctx->ctx_irawlen = hdr.lmh_msglen;
495 		ctx->ctx_orawlen = smbd_authsvc_bufsize;
496 		ctx->ctx_ibodylen = smbd_authsvc_bufsize;
497 		ctx->ctx_obodylen = smbd_authsvc_bufsize;
498 
499 		/*
500 		 * The real work happens here.
501 		 */
502 		rc = smbd_authsvc_dispatch(ctx);
503 		if (rc)
504 			break;
505 
506 		hdr.lmh_msgtype = ctx->ctx_orawtype;
507 		hdr.lmh_msglen = ctx->ctx_orawlen;
508 		len = send(sock, &hdr, sizeof (hdr), 0);
509 		if (len != sizeof (hdr)) {
510 			smbd_report("authsvc_work: send failed");
511 			break;
512 		}
513 
514 		if (ctx->ctx_orawlen > 0) {
515 			len = send(sock, ctx->ctx_orawbuf,
516 			    ctx->ctx_orawlen, 0);
517 			if (len != ctx->ctx_orawlen) {
518 				smbd_report("authsvc_work: send failed");
519 				break;
520 			}
521 		}
522 	}
523 
524 out:
525 	if (ctx->ctx_mh_fini)
526 		(ctx->ctx_mh_fini)(ctx);
527 
528 	smbd_authctx_destroy(ctx);
529 
530 	(void) mutex_lock(&smbd_authsvc_mutex);
531 	smbd_authsvc_thrcnt--;
532 	(void) mutex_unlock(&smbd_authsvc_mutex);
533 
534 	return (NULL);	/* implied pthread_exit() */
535 }
536 
537 /*
538  * Dispatch based on message type LSA_MTYPE_...
539  * Non-zero return here ends the conversation.
540  */
541 int
542 smbd_authsvc_dispatch(authsvc_context_t *ctx)
543 {
544 	int rc;
545 
546 	switch (ctx->ctx_irawtype) {
547 
548 	case LSA_MTYPE_OLDREQ:
549 #ifdef DEBUG
550 		if (smbd_authsvc_slowdown)
551 			(void) sleep(smbd_authsvc_slowdown);
552 #endif
553 		rc = smbd_authsvc_oldreq(ctx);
554 		break;
555 
556 	case LSA_MTYPE_CLINFO:
557 		rc = smbd_authsvc_clinfo(ctx);
558 		break;
559 
560 	case LSA_MTYPE_ESFIRST:
561 		rc = smbd_authsvc_esfirst(ctx);
562 		break;
563 
564 	case LSA_MTYPE_ESNEXT:
565 #ifdef DEBUG
566 		if (smbd_authsvc_slowdown)
567 			(void) sleep(smbd_authsvc_slowdown);
568 #endif
569 		rc = smbd_authsvc_esnext(ctx);
570 		break;
571 
572 	case LSA_MTYPE_GETTOK:
573 		rc = smbd_authsvc_gettoken(ctx);
574 		break;
575 
576 		/* response types */
577 	case LSA_MTYPE_OK:
578 	case LSA_MTYPE_ERROR:
579 	case LSA_MTYPE_TOKEN:
580 	case LSA_MTYPE_ES_CONT:
581 	case LSA_MTYPE_ES_DONE:
582 	default:
583 		return (-1);
584 	}
585 
586 	if (rc == NT_STATUS_NO_MEMORY)
587 		smbd_nomem();
588 
589 	if (rc != 0) {
590 		smb_lsa_eresp_t *er = ctx->ctx_orawbuf;
591 		ctx->ctx_orawtype = LSA_MTYPE_ERROR;
592 		ctx->ctx_orawlen = sizeof (*er);
593 		er->ler_ntstatus = rc;
594 		er->ler_errclass = 0;
595 		er->ler_errcode = 0;
596 	}
597 	return (0);
598 }
599 
600 static int
601 smbd_authsvc_oldreq(authsvc_context_t *ctx)
602 {
603 	smb_logon_t	user_info;
604 	XDR		xdrs;
605 	smb_token_t	*token = NULL;
606 	int		rc = 0;
607 
608 	bzero(&user_info, sizeof (user_info));
609 	xdrmem_create(&xdrs, ctx->ctx_irawbuf, ctx->ctx_irawlen,
610 	    XDR_DECODE);
611 	if (!smb_logon_xdr(&xdrs, &user_info)) {
612 		xdr_destroy(&xdrs);
613 		return (NT_STATUS_INVALID_PARAMETER);
614 	}
615 	xdr_destroy(&xdrs);
616 
617 	token = smbd_user_auth_logon(&user_info);
618 	xdr_free(smb_logon_xdr, (char *)&user_info);
619 	if (token == NULL) {
620 		rc = user_info.lg_status;
621 		if (rc == 0) /* should not happen */
622 			rc = NT_STATUS_INTERNAL_ERROR;
623 		return (rc);
624 	}
625 
626 	ctx->ctx_token = token;
627 
628 	return (rc);
629 }
630 
631 static int
632 smbd_authsvc_clinfo(authsvc_context_t *ctx)
633 {
634 
635 	if (ctx->ctx_irawlen != sizeof (smb_lsa_clinfo_t))
636 		return (NT_STATUS_INTERNAL_ERROR);
637 	(void) memcpy(&ctx->ctx_clinfo, ctx->ctx_irawbuf,
638 	    sizeof (smb_lsa_clinfo_t));
639 
640 	ctx->ctx_orawtype = LSA_MTYPE_OK;
641 	ctx->ctx_orawlen = 0;
642 	return (0);
643 }
644 
645 /*
646  * Handle a security blob we've received from the client.
647  * Incoming type: LSA_MTYPE_ESFIRST
648  * Outgoing types: LSA_MTYPE_ES_CONT, LSA_MTYPE_ES_DONE,
649  *   LSA_MTYPE_ERROR
650  */
651 static int
652 smbd_authsvc_esfirst(authsvc_context_t *ctx)
653 {
654 	const spnego_mech_handler_t *mh;
655 	int idx, pref, rc;
656 	int best_pref = 1000;
657 	int best_mhidx = -1;
658 
659 	/*
660 	 * NTLMSSP header is 8+, SPNEGO is 10+
661 	 */
662 	if (ctx->ctx_irawlen < 8) {
663 		smbd_report("authsvc: short blob");
664 		return (NT_STATUS_INVALID_PARAMETER);
665 	}
666 
667 	/*
668 	 * We could have "Raw NTLMSSP" here intead of SPNEGO.
669 	 */
670 	if (bcmp(ctx->ctx_irawbuf, "NTLMSSP", 8) == 0) {
671 		rc = smbd_raw_ntlmssp_esfirst(ctx);
672 		return (rc);
673 	}
674 
675 	/*
676 	 * Parse the SPNEGO token, check its type.
677 	 */
678 	rc = spnegoInitFromBinary(ctx->ctx_irawbuf,
679 	    ctx->ctx_irawlen, &ctx->ctx_itoken);
680 	if (rc != 0) {
681 		smbd_report("authsvc: spnego parse failed");
682 		return (NT_STATUS_INVALID_PARAMETER);
683 	}
684 
685 	rc = spnegoGetTokenType(ctx->ctx_itoken, &ctx->ctx_itoktype);
686 	if (rc != 0) {
687 		smbd_report("authsvc: spnego get token type failed");
688 		return (NT_STATUS_INVALID_PARAMETER);
689 	}
690 
691 	if (ctx->ctx_itoktype != SPNEGO_TOKEN_INIT) {
692 		smbd_report("authsvc: spnego wrong token type %d",
693 		    ctx->ctx_itoktype);
694 		return (NT_STATUS_INVALID_PARAMETER);
695 	}
696 
697 	/*
698 	 * Figure out which mech type to use.  We want to use the
699 	 * first of the client's supported mechanisms that we also
700 	 * support.  Unfortunately, the spnego code does not have an
701 	 * interface to walk the token's mech list, so we have to
702 	 * ask about each mech type we know and keep track of which
703 	 * was earliest in the token's mech list.
704 	 *
705 	 * Also, skip the Kerberos mechanisms in workgroup mode.
706 	 */
707 	idx = 0;
708 	mh = mech_table;
709 	if (smb_config_get_secmode() != SMB_SECMODE_DOMAIN) {
710 		idx = MECH_TBL_IDX_NTLMSSP;
711 		mh = &mech_table[idx];
712 	}
713 	for (; mh->mh_init != NULL; idx++, mh++) {
714 
715 		if (spnegoIsMechTypeAvailable(ctx->ctx_itoken,
716 		    mh->mh_oid, &pref) != 0)
717 			continue;
718 
719 		if (pref < best_pref) {
720 			best_pref = pref;
721 			best_mhidx = idx;
722 		}
723 	}
724 	if (best_mhidx == -1) {
725 		smbd_report("authsvc: no supported spnego mechanism");
726 		return (NT_STATUS_INVALID_PARAMETER);
727 	}
728 
729 	/* Found a mutually agreeable mech. */
730 	mh = &mech_table[best_mhidx];
731 	ctx->ctx_mech_oid = mh->mh_oid;
732 	ctx->ctx_mh_work = mh->mh_work;
733 	ctx->ctx_mh_fini = mh->mh_fini;
734 	rc = mh->mh_init(ctx);
735 	if (rc != 0) {
736 		smbd_report("authsvc: mech init failed");
737 		return (rc);
738 	}
739 
740 	/*
741 	 * If the best supported mech was not the first in the list,
742 	 * we need to ask the client to use a different one, and
743 	 * skip (ignore) the provided token body.
744 	 */
745 	if (best_pref != 0) {
746 		rc = smbd_authsvc_newmech(ctx);
747 	} else {
748 		/*
749 		 * Common to LSA_MTYPE_ESFIRST, LSA_MTYPE_ESNEXT
750 		 */
751 		rc = smbd_authsvc_escmn(ctx);
752 	}
753 	return (rc);
754 }
755 
756 /*
757  * Handle a security blob we've received from the client.
758  * Incoming type: LSA_MTYPE_ESNEXT
759  * Outgoing types: LSA_MTYPE_ES_CONT, LSA_MTYPE_ES_DONE,
760  *   LSA_MTYPE_ERROR
761  */
762 static int
763 smbd_authsvc_esnext(authsvc_context_t *ctx)
764 {
765 	int rc;
766 
767 	/*
768 	 * Make sure LSA_MTYPE_ESFIRST was handled
769 	 * previously, so we have a work function.
770 	 */
771 	if (ctx->ctx_mh_work == NULL)
772 		return (NT_STATUS_INVALID_PARAMETER);
773 
774 	if (ctx->ctx_mech_oid == special_mech_raw_NTLMSSP) {
775 		rc = smbd_raw_ntlmssp_esnext(ctx);
776 		return (rc);
777 	}
778 
779 	/*
780 	 * Cleanup state from previous calls.
781 	 */
782 	if (ctx->ctx_itoken != NULL) {
783 		spnegoFreeData(ctx->ctx_itoken);
784 		ctx->ctx_itoken = NULL;
785 	}
786 
787 	/*
788 	 * Parse the SPNEGO token, check its type.
789 	 */
790 	rc = spnegoInitFromBinary(ctx->ctx_irawbuf,
791 	    ctx->ctx_irawlen, &ctx->ctx_itoken);
792 	if (rc != 0)
793 		return (NT_STATUS_INVALID_PARAMETER);
794 
795 	rc = spnegoGetTokenType(ctx->ctx_itoken, &ctx->ctx_itoktype);
796 	if (rc != 0)
797 		return (NT_STATUS_INVALID_PARAMETER);
798 
799 	if (ctx->ctx_itoktype != SPNEGO_TOKEN_TARG)
800 		return (NT_STATUS_INVALID_PARAMETER);
801 
802 	rc = smbd_authsvc_escmn(ctx);
803 	return (rc);
804 }
805 
806 static int
807 smbd_authsvc_escmn(authsvc_context_t *ctx)
808 {
809 	SPNEGO_MECH_OID oid;
810 	ulong_t toklen;
811 	int rc;
812 
813 	/*
814 	 * Cleanup state from previous calls.
815 	 */
816 	if (ctx->ctx_otoken != NULL) {
817 		spnegoFreeData(ctx->ctx_otoken);
818 		ctx->ctx_otoken = NULL;
819 	}
820 
821 	/*
822 	 * Extract the payload (mech token).
823 	 */
824 	toklen = ctx->ctx_ibodylen;
825 	rc = spnegoGetMechToken(ctx->ctx_itoken,
826 	    ctx->ctx_ibodybuf, &toklen);
827 	switch (rc) {
828 	case SPNEGO_E_SUCCESS:
829 		break;
830 	case SPNEGO_E_ELEMENT_UNAVAILABLE:
831 		toklen = 0;
832 		break;
833 	case SPNEGO_E_BUFFER_TOO_SMALL:
834 		return (NT_STATUS_BUFFER_TOO_SMALL);
835 	default:
836 		return (NT_STATUS_INTERNAL_ERROR);
837 	}
838 	ctx->ctx_ibodylen = toklen;
839 
840 	/*
841 	 * Now that we have the incoming "body" (mech. token),
842 	 * call the back-end mech-specific work function to
843 	 * create the outgoing "body" (mech. token).
844 	 *
845 	 * The worker must fill in:  ctx->ctx_negresult,
846 	 * and: ctx->ctx_obodylen, but ctx->ctx_obodybuf
847 	 * is optional, and is typically NULL after the
848 	 * final message of an auth sequence, where
849 	 * negresult == spnego_negresult_complete.
850 	 */
851 	rc = ctx->ctx_mh_work(ctx);
852 	if (rc != 0)
853 		return (rc);
854 
855 	/*
856 	 * Wrap the outgoing body in a negTokenTarg SPNEGO token.
857 	 * The selected mech. OID is returned only when the
858 	 * incoming token was of type SPNEGO_TOKEN_INIT.
859 	 */
860 	if (ctx->ctx_itoktype == SPNEGO_TOKEN_INIT) {
861 		/* tell the client the selected mech. */
862 		oid = ctx->ctx_mech_oid;
863 	} else {
864 		/* Omit the "supported mech." field. */
865 		oid = spnego_mech_oid_NotUsed;
866 	}
867 
868 	/*
869 	 * Determine the spnego "negresult" from the
870 	 * reply message type (from the work func).
871 	 */
872 	switch (ctx->ctx_orawtype) {
873 	case LSA_MTYPE_ERROR:
874 		ctx->ctx_negresult = spnego_negresult_rejected;
875 		break;
876 	case LSA_MTYPE_ES_DONE:
877 		ctx->ctx_negresult = spnego_negresult_success;
878 		break;
879 	case LSA_MTYPE_ES_CONT:
880 		ctx->ctx_negresult = spnego_negresult_incomplete;
881 		break;
882 	default:
883 		return (-1);
884 	}
885 
886 	rc = spnegoCreateNegTokenTarg(
887 	    oid,
888 	    ctx->ctx_negresult,
889 	    ctx->ctx_obodybuf, /* may be NULL */
890 	    ctx->ctx_obodylen,
891 	    NULL, 0,
892 	    &ctx->ctx_otoken);
893 	if (rc != 0)
894 		return (NT_STATUS_INTERNAL_ERROR);
895 
896 	/*
897 	 * Convert the SPNEGO token into binary form,
898 	 * writing it to the output buffer.
899 	 */
900 	toklen = smbd_authsvc_bufsize;
901 	rc = spnegoTokenGetBinary(ctx->ctx_otoken,
902 	    (uchar_t *)ctx->ctx_orawbuf, &toklen);
903 	if (rc != 0)
904 		rc = NT_STATUS_INTERNAL_ERROR;
905 	ctx->ctx_orawlen = (uint_t)toklen;
906 
907 	return (rc);
908 }
909 
910 /*
911  * The first NegTokenInit we receive, handled in smbd_authsvc_esfirst,
912  * contains a list of supported mechanisms, in order from the client's
913  * most preferred to least preferred. The token also contains a body
914  * for the first mechanism listed, which is used immediately if the
915  * first mechanism is mutually agreeable.  If the first mechanism is
916  * not supported on our side, we must "propose a new mechanism" from
917  * the list.  Our caller has selected a mech and initialized the ctx
918  * mech functions.  Here compose a reply with an empty body and the
919  * proposed new mechanism OID.  The token body received is for some
920  * other mech, so we skip calling the work function with that token.
921  *
922  * Just send a NegTokenTarg with an empty body and the OID for the
923  * proposed mechanism.  The next message should be the real first
924  * token for this mechanism, handled in smbd_authsvc_exnext.
925  */
926 static int
927 smbd_authsvc_newmech(authsvc_context_t *ctx)
928 {
929 	ulong_t toklen;
930 	int rc;
931 
932 	/*
933 	 * Don't call mh_work here.
934 	 * Just tell the clint the selected mech.
935 	 */
936 	ctx->ctx_ibodylen = 0;
937 	ctx->ctx_orawtype = LSA_MTYPE_ES_CONT;
938 	ctx->ctx_obodylen = 0;
939 	ctx->ctx_negresult = spnego_negresult_request_mic;
940 
941 	rc = spnegoCreateNegTokenTarg(
942 	    ctx->ctx_mech_oid,
943 	    ctx->ctx_negresult,
944 	    NULL, 0,
945 	    NULL, 0,
946 	    &ctx->ctx_otoken);
947 	if (rc != 0)
948 		return (NT_STATUS_INTERNAL_ERROR);
949 
950 	/*
951 	 * Convert the SPNEGO token into binary form,
952 	 * writing it to the output buffer.
953 	 */
954 	toklen = smbd_authsvc_bufsize;
955 	rc = spnegoTokenGetBinary(ctx->ctx_otoken,
956 	    (uchar_t *)ctx->ctx_orawbuf, &toklen);
957 	if (rc)
958 		rc = NT_STATUS_INTERNAL_ERROR;
959 	ctx->ctx_orawlen = (uint_t)toklen;
960 
961 	return (rc);
962 }
963 
964 /*
965  * Wrapper for "Raw NTLMSSP", which is exactly like the
966  * normal (SPNEGO-wrapped) NTLMSSP but without SPNEGO.
967  * Setup back-end handler for: special_mech_raw_NTLMSSP
968  * Compare with smbd_authsvc_esfirst().
969  */
970 static int
971 smbd_raw_ntlmssp_esfirst(authsvc_context_t *ctx)
972 {
973 	const spnego_mech_handler_t *mh;
974 	int rc;
975 
976 	mh = &smbd_auth_mech_raw_ntlmssp;
977 	rc = mh->mh_init(ctx);
978 	if (rc != 0)
979 		return (rc);
980 
981 	ctx->ctx_mech_oid = mh->mh_oid;
982 	ctx->ctx_mh_work = mh->mh_work;
983 	ctx->ctx_mh_fini = mh->mh_fini;
984 
985 	rc = smbd_raw_ntlmssp_esnext(ctx);
986 
987 	return (rc);
988 }
989 
990 
991 /*
992  * Wrapper for "Raw NTLMSSP", which is exactly like the
993  * normal (SPNEGO-wrapped) NTLMSSP but without SPNEGO.
994  * Just copy "raw" to "body", and vice versa.
995  * Compare with smbd_authsvc_esnext, smbd_authsvc_escmn
996  */
997 static int
998 smbd_raw_ntlmssp_esnext(authsvc_context_t *ctx)
999 {
1000 	int rc;
1001 
1002 	ctx->ctx_ibodylen = ctx->ctx_irawlen;
1003 	(void) memcpy(ctx->ctx_ibodybuf,
1004 	    ctx->ctx_irawbuf, ctx->ctx_irawlen);
1005 
1006 	rc = ctx->ctx_mh_work(ctx);
1007 
1008 	ctx->ctx_orawlen = ctx->ctx_obodylen;
1009 	(void) memcpy(ctx->ctx_orawbuf,
1010 	    ctx->ctx_obodybuf, ctx->ctx_obodylen);
1011 
1012 	return (rc);
1013 }
1014 
1015 
1016 /*
1017  * After a successful authentication, request the access token.
1018  */
1019 static int
1020 smbd_authsvc_gettoken(authsvc_context_t *ctx)
1021 {
1022 	XDR		xdrs;
1023 	smb_token_t	*token = NULL;
1024 	int		rc = 0;
1025 	int		len;
1026 
1027 	if ((token = ctx->ctx_token) == NULL)
1028 		return (NT_STATUS_ACCESS_DENIED);
1029 
1030 	/*
1031 	 * Encode the token response
1032 	 */
1033 	len = xdr_sizeof(smb_token_xdr, token);
1034 	if (len > ctx->ctx_orawlen) {
1035 		if ((ctx->ctx_orawbuf = realloc(ctx->ctx_orawbuf, len)) ==
1036 		    NULL) {
1037 			return (NT_STATUS_NO_MEMORY);
1038 		}
1039 	}
1040 
1041 	ctx->ctx_orawtype = LSA_MTYPE_TOKEN;
1042 	ctx->ctx_orawlen = len;
1043 	xdrmem_create(&xdrs, ctx->ctx_orawbuf, len, XDR_ENCODE);
1044 	if (!smb_token_xdr(&xdrs, token))
1045 		rc = NT_STATUS_INTERNAL_ERROR;
1046 	xdr_destroy(&xdrs);
1047 
1048 	return (rc);
1049 }
1050 
1051 /*
1052  * Initialization time code to figure out what mechanisms we support.
1053  * Careful with this table; the code below knows its format and may
1054  * skip the fist two entries to omit Kerberos.
1055  */
1056 static SPNEGO_MECH_OID MechTypeList[] = {
1057 	spnego_mech_oid_Kerberos_V5,
1058 	spnego_mech_oid_Kerberos_V5_Legacy,
1059 #define	MECH_OID_IDX_NTLMSSP	2
1060 	spnego_mech_oid_NTLMSSP,
1061 };
1062 static int MechTypeCnt = sizeof (MechTypeList) /
1063 	sizeof (MechTypeList[0]);
1064 
1065 /* This string is just like Windows. */
1066 static char IgnoreSPN[] = "not_defined_in_RFC4178@please_ignore";
1067 
1068 /*
1069  * Build the SPNEGO "hint" token based on the
1070  * configured authentication mechanisms.
1071  * (NTLMSSP, and maybe Kerberos)
1072  */
1073 void
1074 smbd_get_authconf(smb_kmod_cfg_t *kcfg)
1075 {
1076 	SPNEGO_MECH_OID *mechList = MechTypeList;
1077 	int mechCnt = MechTypeCnt;
1078 	SPNEGO_TOKEN_HANDLE hSpnegoToken = NULL;
1079 	uchar_t *pBuf = kcfg->skc_negtok;
1080 	uint32_t *pBufLen = &kcfg->skc_negtok_len;
1081 	ulong_t tLen = sizeof (kcfg->skc_negtok);
1082 	int rc;
1083 
1084 	/*
1085 	 * In workgroup mode, skip Kerberos.
1086 	 */
1087 	if (smb_config_get_secmode() != SMB_SECMODE_DOMAIN) {
1088 		mechList += MECH_OID_IDX_NTLMSSP;
1089 		mechCnt  -= MECH_OID_IDX_NTLMSSP;
1090 	}
1091 
1092 	rc = spnegoCreateNegTokenHint(mechList, mechCnt,
1093 	    (uchar_t *)IgnoreSPN, &hSpnegoToken);
1094 	if (rc != SPNEGO_E_SUCCESS) {
1095 		syslog(LOG_DEBUG, "smb_config_get_negtok: "
1096 		    "spnegoCreateNegTokenHint, rc=%d", rc);
1097 		*pBufLen = 0;
1098 		return;
1099 	}
1100 	rc = spnegoTokenGetBinary(hSpnegoToken, pBuf, &tLen);
1101 	if (rc != SPNEGO_E_SUCCESS) {
1102 		syslog(LOG_DEBUG, "smb_config_get_negtok: "
1103 		    "spnegoTokenGetBinary, rc=%d", rc);
1104 		*pBufLen = 0;
1105 	} else {
1106 		*pBufLen = (uint32_t)tLen;
1107 	}
1108 	spnegoFreeData(hSpnegoToken);
1109 }
1110