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