Merge commit '42a3762d0138d6dd0c0f96964be98d0b21ab6bef'
[unleashed.git] / usr / src / cmd / smbsrv / smbd / smbd_authsvc.c
blobb507148b72e46e58c81bb81c7123a161d10fa7f3
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.
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.
13 * Copyright 2017 Nexenta Systems, Inc. All rights reserved.
17 * SMB authentication service
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.
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>
46 #include "smbd.h"
47 #include "smbd_authsvc.h"
49 /* Arbitrary value outside the (small) range of valid OIDs */
50 #define special_mech_raw_NTLMSSP (spnego_mech_oid_NTLMSSP + 100)
52 static struct sockaddr_un smbauth_sockname = {
53 AF_UNIX, SMB_AUTHSVC_SOCKNAME };
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;
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);
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 *);
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.
82 int smbd_authsvc_bufsize = 65000;
84 static mutex_t smbd_authsvc_mutex = DEFAULTMUTEX;
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.
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
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 ommit Kerberos.
108 static const spnego_mech_handler_t
109 mech_table[] = {
111 spnego_mech_oid_Kerberos_V5,
112 smbd_krb5ssp_init,
113 smbd_krb5ssp_work,
114 smbd_krb5ssp_fini
117 spnego_mech_oid_Kerberos_V5_Legacy,
118 smbd_krb5ssp_init,
119 smbd_krb5ssp_work,
120 smbd_krb5ssp_fini
122 #define MECH_TBL_IDX_NTLMSSP 2
124 spnego_mech_oid_NTLMSSP,
125 smbd_ntlmssp_init,
126 smbd_ntlmssp_work,
127 smbd_ntlmssp_fini
130 /* end marker */
131 spnego_mech_oid_NotUsed,
132 NULL, NULL, NULL
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
146 * Start the authentication service.
147 * Returns non-zero on error.
150 smbd_authsvc_start(void)
152 pthread_attr_t attr;
153 pthread_t tid;
154 int rc;
156 rc = smbd_authsock_create();
157 if (rc)
158 return (rc);
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);
169 smbd.s_authsvc_tid = tid;
170 return (0);
173 void
174 smbd_authsvc_stop(void)
177 if (smbd.s_authsvc_tid != 0) {
178 (void) pthread_kill(smbd.s_authsvc_tid, SIGTERM);
179 smbd.s_authsvc_tid = 0;
183 static int
184 smbd_authsock_create(void)
186 int sock = -1;
188 sock = socket(AF_UNIX, SOCK_STREAM, 0);
189 if (sock < 0) {
190 smbd_report("authsvc, socket create failed, %d", errno);
191 return (errno);
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);
202 if (listen(sock, SOMAXCONN) < 0) {
203 smbd_report("authsvc, socket listen failed, %d", errno);
204 (void) close(sock);
205 return (errno);
208 smbd.s_authsvc_sock = sock;
209 return (0);
212 static void
213 smbd_authsock_destroy(void)
215 int fid;
217 if ((fid = smbd.s_authsvc_sock) != -1) {
218 smbd.s_authsvc_sock = -1;
219 (void) close(fid);
223 static void *
224 smbd_authsvc_listen(void *arg)
226 authsvc_context_t *ctx;
227 pthread_attr_t attr;
228 pthread_t tid;
229 socklen_t slen;
230 int ls, ns, rc;
232 _NOTE(ARGUNUSED(arg))
234 (void) pthread_attr_init(&attr);
235 (void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
237 ls = smbd.s_authsvc_sock;
238 for (;;) {
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;
257 * Limit the number of auth. sockets
258 * (and the threads that service them).
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;
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);
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 goto out;
281 ctx->ctx_socket = ns;
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 goto out;
292 ctx = NULL; /* given to the new thread */
295 out:
296 (void) pthread_attr_destroy(&attr);
297 smbd_authsock_destroy();
298 return (NULL);
301 static void
302 smbd_authsvc_flood(void)
304 static uint_t count;
305 static time_t last_report;
306 time_t now = time(NULL);
308 count++;
309 if (last_report + 60 < now) {
310 last_report = now;
311 smbd_report("authsvc: flooded %u", count);
312 count = 0;
316 authsvc_context_t *
317 smbd_authctx_create(void)
319 authsvc_context_t *ctx;
321 ctx = malloc(sizeof (*ctx));
322 if (ctx == NULL)
323 return (NULL);
324 bzero(ctx, sizeof (*ctx));
326 ctx->ctx_irawlen = smbd_authsvc_bufsize;
327 ctx->ctx_irawbuf = malloc(ctx->ctx_irawlen);
328 ctx->ctx_orawlen = smbd_authsvc_bufsize;
329 ctx->ctx_orawbuf = malloc(ctx->ctx_orawlen);
330 if (ctx->ctx_irawbuf == NULL || ctx->ctx_orawbuf == NULL)
331 goto errout;
333 ctx->ctx_ibodylen = smbd_authsvc_bufsize;
334 ctx->ctx_ibodybuf = malloc(ctx->ctx_ibodylen);
335 ctx->ctx_obodylen = smbd_authsvc_bufsize;
336 ctx->ctx_obodybuf = malloc(ctx->ctx_obodylen);
337 if (ctx->ctx_ibodybuf == NULL || ctx->ctx_obodybuf == NULL)
338 goto errout;
340 return (ctx);
342 errout:
343 smbd_authctx_destroy(ctx);
344 return (NULL);
347 void
348 smbd_authctx_destroy(authsvc_context_t *ctx)
350 if (ctx->ctx_socket != -1) {
351 (void) close(ctx->ctx_socket);
352 ctx->ctx_socket = -1;
355 if (ctx->ctx_token != NULL)
356 smb_token_destroy(ctx->ctx_token);
358 if (ctx->ctx_itoken != NULL)
359 spnegoFreeData(ctx->ctx_itoken);
360 if (ctx->ctx_otoken != NULL)
361 spnegoFreeData(ctx->ctx_otoken);
363 free(ctx->ctx_irawbuf);
364 free(ctx->ctx_orawbuf);
365 free(ctx->ctx_ibodybuf);
366 free(ctx->ctx_obodybuf);
368 free(ctx);
372 * Limit how long smbd_authsvc_work will wait for the client to
373 * send us the next part of the authentication sequence.
375 static struct timeval recv_tmo = { 30, 0 };
378 * Also set a timeout for send, where we're sending a response to
379 * the client side (in smbsrv). That should always be waiting in
380 * recv by the time we send, so a short timeout is OK.
382 static struct timeval send_tmo = { 15, 0 };
384 static void *
385 smbd_authsvc_work(void *arg)
387 authsvc_context_t *ctx = arg;
388 smb_lsa_msg_hdr_t hdr;
389 int sock = ctx->ctx_socket;
390 int len, rc;
392 if (setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO,
393 (char *)&send_tmo, sizeof (send_tmo)) != 0) {
394 smbd_report("authsvc_work: set set timeout: %m");
395 goto out;
398 if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO,
399 (char *)&recv_tmo, sizeof (recv_tmo)) != 0) {
400 smbd_report("authsvc_work: set recv timeout: %m");
401 goto out;
404 for (;;) {
406 len = recv(sock, &hdr, sizeof (hdr), MSG_WAITALL);
407 if (len <= 0) {
408 /* normal termination */
409 break;
411 if (len != sizeof (hdr)) {
412 smbd_report("authsvc_work: read header failed");
413 break;
416 if (hdr.lmh_msglen > smbd_authsvc_bufsize) {
417 smbd_report("authsvc_work: msg too large");
418 break;
421 if (hdr.lmh_msglen > 0) {
422 len = recv(sock, ctx->ctx_irawbuf, hdr.lmh_msglen,
423 MSG_WAITALL);
424 if (len != hdr.lmh_msglen) {
425 smbd_report("authsvc_work: read mesg failed");
426 break;
429 ctx->ctx_irawtype = hdr.lmh_msgtype;
430 ctx->ctx_irawlen = hdr.lmh_msglen;
431 ctx->ctx_orawlen = smbd_authsvc_bufsize;
432 ctx->ctx_ibodylen = smbd_authsvc_bufsize;
433 ctx->ctx_obodylen = smbd_authsvc_bufsize;
436 * The real work happens here.
438 rc = smbd_authsvc_dispatch(ctx);
439 if (rc)
440 break;
442 hdr.lmh_msgtype = ctx->ctx_orawtype;
443 hdr.lmh_msglen = ctx->ctx_orawlen;
444 len = send(sock, &hdr, sizeof (hdr), 0);
445 if (len != sizeof (hdr)) {
446 smbd_report("authsvc_work: send failed");
447 break;
450 if (ctx->ctx_orawlen > 0) {
451 len = send(sock, ctx->ctx_orawbuf,
452 ctx->ctx_orawlen, 0);
453 if (len != ctx->ctx_orawlen) {
454 smbd_report("authsvc_work: send failed");
455 break;
460 out:
461 if (ctx->ctx_mh_fini)
462 (ctx->ctx_mh_fini)(ctx);
464 smbd_authctx_destroy(ctx);
466 (void) mutex_lock(&smbd_authsvc_mutex);
467 smbd_authsvc_thrcnt--;
468 (void) mutex_unlock(&smbd_authsvc_mutex);
470 return (NULL); /* implied pthread_exit() */
474 * Dispatch based on message type LSA_MTYPE_...
475 * Non-zero return here ends the conversation.
478 smbd_authsvc_dispatch(authsvc_context_t *ctx)
480 int rc;
482 switch (ctx->ctx_irawtype) {
484 case LSA_MTYPE_OLDREQ:
485 #ifdef DEBUG
486 if (smbd_authsvc_slowdown)
487 (void) sleep(smbd_authsvc_slowdown);
488 #endif
489 rc = smbd_authsvc_oldreq(ctx);
490 break;
492 case LSA_MTYPE_CLINFO:
493 rc = smbd_authsvc_clinfo(ctx);
494 break;
496 case LSA_MTYPE_ESFIRST:
497 rc = smbd_authsvc_esfirst(ctx);
498 break;
500 case LSA_MTYPE_ESNEXT:
501 #ifdef DEBUG
502 if (smbd_authsvc_slowdown)
503 (void) sleep(smbd_authsvc_slowdown);
504 #endif
505 rc = smbd_authsvc_esnext(ctx);
506 break;
508 case LSA_MTYPE_GETTOK:
509 rc = smbd_authsvc_gettoken(ctx);
510 break;
512 /* response types */
513 case LSA_MTYPE_OK:
514 case LSA_MTYPE_ERROR:
515 case LSA_MTYPE_TOKEN:
516 case LSA_MTYPE_ES_CONT:
517 case LSA_MTYPE_ES_DONE:
518 default:
519 return (-1);
522 if (rc != 0) {
523 smb_lsa_eresp_t *er = ctx->ctx_orawbuf;
524 ctx->ctx_orawtype = LSA_MTYPE_ERROR;
525 ctx->ctx_orawlen = sizeof (*er);
526 er->ler_ntstatus = rc;
527 er->ler_errclass = 0;
528 er->ler_errcode = 0;
530 return (0);
533 static int
534 smbd_authsvc_oldreq(authsvc_context_t *ctx)
536 smb_logon_t user_info;
537 XDR xdrs;
538 smb_token_t *token = NULL;
539 int rc = 0;
541 bzero(&user_info, sizeof (user_info));
542 xdrmem_create(&xdrs, ctx->ctx_irawbuf, ctx->ctx_irawlen,
543 XDR_DECODE);
544 if (!smb_logon_xdr(&xdrs, &user_info)) {
545 xdr_destroy(&xdrs);
546 return (NT_STATUS_INVALID_PARAMETER);
548 xdr_destroy(&xdrs);
550 token = smbd_user_auth_logon(&user_info);
551 xdr_free(smb_logon_xdr, (char *)&user_info);
552 if (token == NULL) {
553 rc = user_info.lg_status;
554 if (rc == 0) /* should not happen */
555 rc = NT_STATUS_INTERNAL_ERROR;
556 return (rc);
559 ctx->ctx_token = token;
561 return (rc);
564 static int
565 smbd_authsvc_clinfo(authsvc_context_t *ctx)
568 if (ctx->ctx_irawlen != sizeof (smb_lsa_clinfo_t))
569 return (NT_STATUS_INTERNAL_ERROR);
570 (void) memcpy(&ctx->ctx_clinfo, ctx->ctx_irawbuf,
571 sizeof (smb_lsa_clinfo_t));
573 ctx->ctx_orawtype = LSA_MTYPE_OK;
574 ctx->ctx_orawlen = 0;
575 return (0);
579 * Handle a security blob we've received from the client.
580 * Incoming type: LSA_MTYPE_ESFIRST
581 * Outgoing types: LSA_MTYPE_ES_CONT, LSA_MTYPE_ES_DONE,
582 * LSA_MTYPE_ERROR
584 static int
585 smbd_authsvc_esfirst(authsvc_context_t *ctx)
587 const spnego_mech_handler_t *mh;
588 int idx, pref, rc;
589 int best_pref = 1000;
590 int best_mhidx = -1;
593 * NTLMSSP header is 8+, SPNEGO is 10+
595 if (ctx->ctx_irawlen < 8) {
596 smbd_report("authsvc: short blob");
597 return (NT_STATUS_INVALID_PARAMETER);
601 * We could have "Raw NTLMSSP" here intead of SPNEGO.
603 if (bcmp(ctx->ctx_irawbuf, "NTLMSSP", 8) == 0) {
604 rc = smbd_raw_ntlmssp_esfirst(ctx);
605 return (rc);
609 * Parse the SPNEGO token, check its type.
611 rc = spnegoInitFromBinary(ctx->ctx_irawbuf,
612 ctx->ctx_irawlen, &ctx->ctx_itoken);
613 if (rc != 0) {
614 smbd_report("authsvc: spnego parse failed");
615 return (NT_STATUS_INVALID_PARAMETER);
618 rc = spnegoGetTokenType(ctx->ctx_itoken, &ctx->ctx_itoktype);
619 if (rc != 0) {
620 smbd_report("authsvc: spnego get token type failed");
621 return (NT_STATUS_INVALID_PARAMETER);
624 if (ctx->ctx_itoktype != SPNEGO_TOKEN_INIT) {
625 smbd_report("authsvc: spnego wrong token type %d",
626 ctx->ctx_itoktype);
627 return (NT_STATUS_INVALID_PARAMETER);
631 * Figure out which mech type to use. We want to use the
632 * first of the client's supported mechanisms that we also
633 * support. Unfortunately, the spnego code does not have an
634 * interface to walk the token's mech list, so we have to
635 * ask about each mech type we know and keep track of which
636 * was earliest in the token's mech list.
638 * Also, skip the Kerberos mechanisms in workgroup mode.
640 idx = 0;
641 mh = mech_table;
642 if (smb_config_get_secmode() != SMB_SECMODE_DOMAIN) {
643 idx = MECH_TBL_IDX_NTLMSSP;
644 mh = &mech_table[idx];
646 for (; mh->mh_init != NULL; idx++, mh++) {
648 if (spnegoIsMechTypeAvailable(ctx->ctx_itoken,
649 mh->mh_oid, &pref) != 0)
650 continue;
652 if (pref < best_pref) {
653 best_pref = pref;
654 best_mhidx = idx;
657 if (best_mhidx == -1) {
658 smbd_report("authsvc: no supported spnego mechanism");
659 return (NT_STATUS_INVALID_PARAMETER);
662 /* Found a mutually agreeable mech. */
663 mh = &mech_table[best_mhidx];
664 ctx->ctx_mech_oid = mh->mh_oid;
665 ctx->ctx_mh_work = mh->mh_work;
666 ctx->ctx_mh_fini = mh->mh_fini;
667 rc = mh->mh_init(ctx);
668 if (rc != 0) {
669 smbd_report("authsvc: mech init failed");
670 return (rc);
674 * Common to LSA_MTYPE_ESFIRST, LSA_MTYPE_ESNEXT
676 rc = smbd_authsvc_escmn(ctx);
677 return (rc);
681 * Handle a security blob we've received from the client.
682 * Incoming type: LSA_MTYPE_ESNEXT
683 * Outgoing types: LSA_MTYPE_ES_CONT, LSA_MTYPE_ES_DONE,
684 * LSA_MTYPE_ERROR
686 static int
687 smbd_authsvc_esnext(authsvc_context_t *ctx)
689 int rc;
692 * Make sure LSA_MTYPE_ESFIRST was handled
693 * previously, so we have a work function.
695 if (ctx->ctx_mh_work == NULL)
696 return (NT_STATUS_INVALID_PARAMETER);
698 if (ctx->ctx_mech_oid == special_mech_raw_NTLMSSP) {
699 rc = smbd_raw_ntlmssp_esnext(ctx);
700 return (rc);
704 * Cleanup state from previous calls.
706 if (ctx->ctx_itoken != NULL) {
707 spnegoFreeData(ctx->ctx_itoken);
708 ctx->ctx_itoken = NULL;
712 * Parse the SPNEGO token, check its type.
714 rc = spnegoInitFromBinary(ctx->ctx_irawbuf,
715 ctx->ctx_irawlen, &ctx->ctx_itoken);
716 if (rc != 0)
717 return (NT_STATUS_INVALID_PARAMETER);
719 rc = spnegoGetTokenType(ctx->ctx_itoken, &ctx->ctx_itoktype);
720 if (rc != 0)
721 return (NT_STATUS_INVALID_PARAMETER);
723 if (ctx->ctx_itoktype != SPNEGO_TOKEN_TARG)
724 return (NT_STATUS_INVALID_PARAMETER);
726 rc = smbd_authsvc_escmn(ctx);
727 return (rc);
730 static int
731 smbd_authsvc_escmn(authsvc_context_t *ctx)
733 SPNEGO_MECH_OID oid;
734 ulong_t toklen;
735 int rc;
738 * Cleanup state from previous calls.
740 if (ctx->ctx_otoken != NULL) {
741 spnegoFreeData(ctx->ctx_otoken);
742 ctx->ctx_otoken = NULL;
746 * Extract the payload (mech token).
748 toklen = ctx->ctx_ibodylen;
749 rc = spnegoGetMechToken(ctx->ctx_itoken,
750 ctx->ctx_ibodybuf, &toklen);
751 switch (rc) {
752 case SPNEGO_E_SUCCESS:
753 break;
754 case SPNEGO_E_ELEMENT_UNAVAILABLE:
755 toklen = 0;
756 break;
757 case SPNEGO_E_BUFFER_TOO_SMALL:
758 return (NT_STATUS_BUFFER_TOO_SMALL);
759 default:
760 return (NT_STATUS_INTERNAL_ERROR);
762 ctx->ctx_ibodylen = toklen;
765 * Now that we have the incoming "body" (mech. token),
766 * call the back-end mech-specific work function to
767 * create the outgoing "body" (mech. token).
769 * The worker must fill in: ctx->ctx_negresult,
770 * and: ctx->ctx_obodylen, but ctx->ctx_obodybuf
771 * is optional, and is typically NULL after the
772 * final message of an auth sequence, where
773 * negresult == spnego_negresult_complete.
775 rc = ctx->ctx_mh_work(ctx);
776 if (rc != 0)
777 return (rc);
780 * Wrap the outgoing body in a negTokenTarg SPNEGO token.
781 * The selected mech. OID is returned only when the
782 * incoming token was of type SPNEGO_TOKEN_INIT.
784 if (ctx->ctx_itoktype == SPNEGO_TOKEN_INIT) {
785 /* tell the client the selected mech. */
786 oid = ctx->ctx_mech_oid;
787 } else {
788 /* Ommit the "supported mech." field. */
789 oid = spnego_mech_oid_NotUsed;
793 * Determine the spnego "negresult" from the
794 * reply message type (from the work func).
796 switch (ctx->ctx_orawtype) {
797 case LSA_MTYPE_ERROR:
798 ctx->ctx_negresult = spnego_negresult_rejected;
799 break;
800 case LSA_MTYPE_ES_DONE:
801 ctx->ctx_negresult = spnego_negresult_success;
802 break;
803 case LSA_MTYPE_ES_CONT:
804 ctx->ctx_negresult = spnego_negresult_incomplete;
805 break;
806 default:
807 return (-1);
810 rc = spnegoCreateNegTokenTarg(
811 oid,
812 ctx->ctx_negresult,
813 ctx->ctx_obodybuf, /* may be NULL */
814 ctx->ctx_obodylen,
815 NULL, 0,
816 &ctx->ctx_otoken);
819 * Convert the SPNEGO token into binary form,
820 * writing it to the output buffer.
822 toklen = smbd_authsvc_bufsize;
823 rc = spnegoTokenGetBinary(ctx->ctx_otoken,
824 (uchar_t *)ctx->ctx_orawbuf, &toklen);
825 if (rc)
826 rc = NT_STATUS_INTERNAL_ERROR;
827 ctx->ctx_orawlen = (uint_t)toklen;
829 return (rc);
833 * Wrapper for "Raw NTLMSSP", which is exactly like the
834 * normal (SPNEGO-wrapped) NTLMSSP but without SPNEGO.
835 * Setup back-end handler for: special_mech_raw_NTLMSSP
836 * Compare with smbd_authsvc_esfirst().
838 static int
839 smbd_raw_ntlmssp_esfirst(authsvc_context_t *ctx)
841 const spnego_mech_handler_t *mh;
842 int rc;
844 mh = &smbd_auth_mech_raw_ntlmssp;
845 rc = mh->mh_init(ctx);
846 if (rc != 0)
847 return (rc);
849 ctx->ctx_mech_oid = mh->mh_oid;
850 ctx->ctx_mh_work = mh->mh_work;
851 ctx->ctx_mh_fini = mh->mh_fini;
853 rc = smbd_raw_ntlmssp_esnext(ctx);
855 return (rc);
860 * Wrapper for "Raw NTLMSSP", which is exactly like the
861 * normal (SPNEGO-wrapped) NTLMSSP but without SPNEGO.
862 * Just copy "raw" to "body", and vice versa.
863 * Compare with smbd_authsvc_esnext, smbd_authsvc_escmn
865 static int
866 smbd_raw_ntlmssp_esnext(authsvc_context_t *ctx)
868 int rc;
870 ctx->ctx_ibodylen = ctx->ctx_irawlen;
871 (void) memcpy(ctx->ctx_ibodybuf,
872 ctx->ctx_irawbuf, ctx->ctx_irawlen);
874 rc = ctx->ctx_mh_work(ctx);
876 ctx->ctx_orawlen = ctx->ctx_obodylen;
877 (void) memcpy(ctx->ctx_orawbuf,
878 ctx->ctx_obodybuf, ctx->ctx_obodylen);
880 return (rc);
885 * After a successful authentication, request the access token.
887 static int
888 smbd_authsvc_gettoken(authsvc_context_t *ctx)
890 XDR xdrs;
891 smb_token_t *token = NULL;
892 int rc = 0;
893 int len;
895 if ((token = ctx->ctx_token) == NULL)
896 return (NT_STATUS_ACCESS_DENIED);
899 * Encode the token response
901 len = xdr_sizeof(smb_token_xdr, token);
902 if (len > ctx->ctx_orawlen) {
903 if ((ctx->ctx_orawbuf = realloc(ctx->ctx_orawbuf, len)) ==
904 NULL) {
905 return (NT_STATUS_INTERNAL_ERROR);
909 ctx->ctx_orawtype = LSA_MTYPE_TOKEN;
910 ctx->ctx_orawlen = len;
911 xdrmem_create(&xdrs, ctx->ctx_orawbuf, len, XDR_ENCODE);
912 if (!smb_token_xdr(&xdrs, token))
913 rc = NT_STATUS_INTERNAL_ERROR;
914 xdr_destroy(&xdrs);
916 return (rc);
920 * Initialization time code to figure out what mechanisms we support.
921 * Careful with this table; the code below knows its format and may
922 * skip the fist two entries to ommit Kerberos.
924 static SPNEGO_MECH_OID MechTypeList[] = {
925 spnego_mech_oid_Kerberos_V5,
926 spnego_mech_oid_Kerberos_V5_Legacy,
927 #define MECH_OID_IDX_NTLMSSP 2
928 spnego_mech_oid_NTLMSSP,
930 static int MechTypeCnt = sizeof (MechTypeList) /
931 sizeof (MechTypeList[0]);
933 /* This string is just like Windows. */
934 static char IgnoreSPN[] = "not_defined_in_RFC4178@please_ignore";
937 * Build the SPNEGO "hint" token based on the
938 * configured authentication mechanisms.
939 * (NTLMSSP, and maybe Kerberos)
941 void
942 smbd_get_authconf(smb_kmod_cfg_t *kcfg)
944 SPNEGO_MECH_OID *mechList = MechTypeList;
945 int mechCnt = MechTypeCnt;
946 SPNEGO_TOKEN_HANDLE hSpnegoToken = NULL;
947 uchar_t *pBuf = kcfg->skc_negtok;
948 uint32_t *pBufLen = &kcfg->skc_negtok_len;
949 ulong_t tLen = sizeof (kcfg->skc_negtok);
950 int rc;
953 * In workgroup mode, skip Kerberos.
955 if (smb_config_get_secmode() != SMB_SECMODE_DOMAIN) {
956 mechList += MECH_OID_IDX_NTLMSSP;
957 mechCnt -= MECH_OID_IDX_NTLMSSP;
960 rc = spnegoCreateNegTokenHint(mechList, mechCnt,
961 (uchar_t *)IgnoreSPN, &hSpnegoToken);
962 if (rc != SPNEGO_E_SUCCESS) {
963 syslog(LOG_DEBUG, "smb_config_get_negtok: "
964 "spnegoCreateNegTokenHint, rc=%d", rc);
965 *pBufLen = 0;
966 return;
968 rc = spnegoTokenGetBinary(hSpnegoToken, pBuf, &tLen);
969 if (rc != SPNEGO_E_SUCCESS) {
970 syslog(LOG_DEBUG, "smb_config_get_negtok: "
971 "spnegoTokenGetBinary, rc=%d", rc);
972 *pBufLen = 0;
973 } else {
974 *pBufLen = (uint32_t)tLen;
976 spnegoFreeData(hSpnegoToken);