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