112b65585SGordon Ross /*
212b65585SGordon Ross  * This file and its contents are supplied under the terms of the
312b65585SGordon Ross  * Common Development and Distribution License ("CDDL"), version 1.0.
412b65585SGordon Ross  * You may only use this file in accordance with the terms of version
512b65585SGordon Ross  * 1.0 of the CDDL.
612b65585SGordon Ross  *
712b65585SGordon Ross  * A full copy of the text of the CDDL should have accompanied this
812b65585SGordon Ross  * source.  A copy of the CDDL is also available via the Internet at
912b65585SGordon Ross  * http://www.illumos.org/license/CDDL.
1012b65585SGordon Ross  */
1112b65585SGordon Ross 
1212b65585SGordon Ross /*
13975041ddSGordon Ross  * Copyright 2017 Nexenta Systems, Inc.  All rights reserved.
14*5d8538b6SGordon Ross  * Copyright 2022-2023 RackTop Systems, Inc.
1512b65585SGordon Ross  */
1612b65585SGordon Ross 
1712b65585SGordon Ross /*
1812b65585SGordon Ross  * SMB authentication service
1912b65585SGordon Ross  *
2012b65585SGordon Ross  * This service listens on a local AF_UNIX socket, spawning a
2112b65585SGordon Ross  * thread to service each connection.  The client-side of such
2212b65585SGordon Ross  * connections is the in-kernel SMB service, with an open and
2312b65585SGordon Ross  * connect done in the SMB session setup handler.
2412b65585SGordon Ross  */
2512b65585SGordon Ross 
2612b65585SGordon Ross #include <sys/types.h>
2712b65585SGordon Ross #include <stdlib.h>
2812b65585SGordon Ross #include <errno.h>
2912b65585SGordon Ross #include <string.h>
3012b65585SGordon Ross #include <strings.h>
3112b65585SGordon Ross #include <unistd.h>
3212b65585SGordon Ross #include <signal.h>
3312b65585SGordon Ross #include <stdio.h>
3412b65585SGordon Ross #include <note.h>
3512b65585SGordon Ross #include <net/if.h>
3612b65585SGordon Ross #include <net/route.h>
3712b65585SGordon Ross #include <sys/sockio.h>
3812b65585SGordon Ross #include <sys/socket.h>
3912b65585SGordon Ross #include <sys/un.h>
4012b65585SGordon Ross #include <netinet/in.h>
4112b65585SGordon Ross #include <fcntl.h>
4212b65585SGordon Ross #include <pthread.h>
4312b65585SGordon Ross #include <syslog.h>
44b6b7639aSGordon Ross #include <ucred.h>
45b6b7639aSGordon Ross #include <priv.h>
46b6b7639aSGordon Ross 
4712b65585SGordon Ross #include <smbsrv/libsmb.h>
4812b65585SGordon Ross #include <netsmb/spnego.h>
4912b65585SGordon Ross 
5012b65585SGordon Ross #include "smbd.h"
5112b65585SGordon Ross #include "smbd_authsvc.h"
5212b65585SGordon Ross 
5312b65585SGordon Ross /* Arbitrary value outside the (small) range of valid OIDs */
5412b65585SGordon Ross #define	special_mech_raw_NTLMSSP	(spnego_mech_oid_NTLMSSP + 100)
5512b65585SGordon Ross 
5612b65585SGordon Ross static struct sockaddr_un smbauth_sockname = {
5712b65585SGordon Ross 	AF_UNIX, SMB_AUTHSVC_SOCKNAME };
5812b65585SGordon Ross 
5912b65585SGordon Ross typedef struct spnego_mech_handler {
6012b65585SGordon Ross 	int mh_oid; /* SPNEGO_MECH_OID */
6112b65585SGordon Ross 	int (*mh_init)(authsvc_context_t *);
6212b65585SGordon Ross 	int (*mh_work)(authsvc_context_t *);
6312b65585SGordon Ross 	void (*mh_fini)(authsvc_context_t *);
6412b65585SGordon Ross } spnego_mech_handler_t;
6512b65585SGordon Ross 
6612b65585SGordon Ross static int smbd_authsock_create(void);
6712b65585SGordon Ross static void smbd_authsock_destroy(void);
6812b65585SGordon Ross static void *smbd_authsvc_listen(void *);
6912b65585SGordon Ross static void *smbd_authsvc_work(void *);
7012b65585SGordon Ross static void smbd_authsvc_flood(void);
7112b65585SGordon Ross 
7212b65585SGordon Ross static int smbd_authsvc_oldreq(authsvc_context_t *);
7312b65585SGordon Ross static int smbd_authsvc_clinfo(authsvc_context_t *);
7412b65585SGordon Ross static int smbd_authsvc_esfirst(authsvc_context_t *);
7512b65585SGordon Ross static int smbd_authsvc_esnext(authsvc_context_t *);
7612b65585SGordon Ross static int smbd_authsvc_escmn(authsvc_context_t *);
77cc86afeeSGordon Ross static int smbd_authsvc_newmech(authsvc_context_t *);
7812b65585SGordon Ross static int smbd_authsvc_gettoken(authsvc_context_t *);
7912b65585SGordon Ross static int smbd_raw_ntlmssp_esfirst(authsvc_context_t *);
8012b65585SGordon Ross static int smbd_raw_ntlmssp_esnext(authsvc_context_t *);
8112b65585SGordon Ross 
8212b65585SGordon Ross /*
8312b65585SGordon Ross  * We can get relatively large tokens now, thanks to krb5 PAC.
8412b65585SGordon Ross  * Might be better to size these buffers dynamically, but these
8512b65585SGordon Ross  * are all short-lived so not bothering with that for now.
8612b65585SGordon Ross  */
8712b65585SGordon Ross int smbd_authsvc_bufsize = 65000;
8812b65585SGordon Ross 
8912b65585SGordon Ross static mutex_t smbd_authsvc_mutex = DEFAULTMUTEX;
9012b65585SGordon Ross 
9112b65585SGordon Ross /*
9212b65585SGordon Ross  * The maximum number of authentication thread is limited by the
9312b65585SGordon Ross  * smbsrv smb_threshold_...(->sv_ssetup_ct) mechanism.  However,
9412b65585SGordon Ross  * due to occasional delays closing these auth. sockets, we need
9512b65585SGordon Ross  * a little "slack" on the number of threads we'll allow, as
9612b65585SGordon Ross  * compared with the in-kernel limit.  We could perhaps just
9712b65585SGordon Ross  * remove this limit now, but want it for extra safety.
9812b65585SGordon Ross  */
9912b65585SGordon Ross int smbd_authsvc_maxthread = SMB_AUTHSVC_MAXTHREAD + 32;
10012b65585SGordon Ross int smbd_authsvc_thrcnt = 0;	/* current thrcnt */
10112b65585SGordon Ross int smbd_authsvc_hiwat = 0;	/* largest thrcnt seen */
10212b65585SGordon Ross #ifdef DEBUG
10312b65585SGordon Ross int smbd_authsvc_slowdown = 0;
10412b65585SGordon Ross #endif
10512b65585SGordon Ross 
10612b65585SGordon Ross /*
10712b65585SGordon Ross  * These are the mechanisms we support, in order of preference.
10812b65585SGordon Ross  * But note: it's really the _client's_ preference that matters.
10912b65585SGordon Ross  * See &pref in the spnegoIsMechTypeAvailable() calls below.
11012b65585SGordon Ross  * Careful with this table; the code below knows its format and
111a44e7c2cSGordon Ross  * may skip the fist two entries to omit Kerberos.
11212b65585SGordon Ross  */
11312b65585SGordon Ross static const spnego_mech_handler_t
11412b65585SGordon Ross mech_table[] = {
11512b65585SGordon Ross 	{
11612b65585SGordon Ross 		spnego_mech_oid_Kerberos_V5,
11712b65585SGordon Ross 		smbd_krb5ssp_init,
11812b65585SGordon Ross 		smbd_krb5ssp_work,
11912b65585SGordon Ross 		smbd_krb5ssp_fini
12012b65585SGordon Ross 	},
12112b65585SGordon Ross 	{
12212b65585SGordon Ross 		spnego_mech_oid_Kerberos_V5_Legacy,
12312b65585SGordon Ross 		smbd_krb5ssp_init,
12412b65585SGordon Ross 		smbd_krb5ssp_work,
12512b65585SGordon Ross 		smbd_krb5ssp_fini
12612b65585SGordon Ross 	},
12712b65585SGordon Ross #define	MECH_TBL_IDX_NTLMSSP	2
12812b65585SGordon Ross 	{
12912b65585SGordon Ross 		spnego_mech_oid_NTLMSSP,
13012b65585SGordon Ross 		smbd_ntlmssp_init,
13112b65585SGordon Ross 		smbd_ntlmssp_work,
13212b65585SGordon Ross 		smbd_ntlmssp_fini
13312b65585SGordon Ross 	},
13412b65585SGordon Ross 	{
13512b65585SGordon Ross 		/* end marker */
13612b65585SGordon Ross 		spnego_mech_oid_NotUsed,
13712b65585SGordon Ross 		NULL, NULL, NULL
13812b65585SGordon Ross 	},
13912b65585SGordon Ross };
14012b65585SGordon Ross 
14112b65585SGordon Ross static const spnego_mech_handler_t
14212b65585SGordon Ross smbd_auth_mech_raw_ntlmssp = {
14312b65585SGordon Ross 	special_mech_raw_NTLMSSP,
14412b65585SGordon Ross 	smbd_ntlmssp_init,
14512b65585SGordon Ross 	smbd_ntlmssp_work,
14612b65585SGordon Ross 	smbd_ntlmssp_fini
14712b65585SGordon Ross };
14812b65585SGordon Ross 
14912b65585SGordon Ross 
15012b65585SGordon Ross /*
15112b65585SGordon Ross  * Start the authentication service.
15212b65585SGordon Ross  * Returns non-zero on error.
15312b65585SGordon Ross  */
15412b65585SGordon Ross int
smbd_authsvc_start(void)15512b65585SGordon Ross smbd_authsvc_start(void)
15612b65585SGordon Ross {
15712b65585SGordon Ross 	pthread_attr_t	attr;
15812b65585SGordon Ross 	pthread_t	tid;
15912b65585SGordon Ross 	int		rc;
16012b65585SGordon Ross 
16112b65585SGordon Ross 	rc = smbd_authsock_create();
16212b65585SGordon Ross 	if (rc)
16312b65585SGordon Ross 		return (rc);
16412b65585SGordon Ross 
16512b65585SGordon Ross 	(void) pthread_attr_init(&attr);
16612b65585SGordon Ross 	(void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
16712b65585SGordon Ross 	rc = pthread_create(&tid, &attr, smbd_authsvc_listen, &smbd);
16812b65585SGordon Ross 	(void) pthread_attr_destroy(&attr);
16912b65585SGordon Ross 	if (rc) {
17012b65585SGordon Ross 		smbd_authsock_destroy();
17112b65585SGordon Ross 		return (rc);
17212b65585SGordon Ross 	}
17312b65585SGordon Ross 
17412b65585SGordon Ross 	smbd.s_authsvc_tid = tid;
17512b65585SGordon Ross 	return (0);
17612b65585SGordon Ross }
17712b65585SGordon Ross 
17812b65585SGordon Ross void
smbd_authsvc_stop(void)17912b65585SGordon Ross smbd_authsvc_stop(void)
18012b65585SGordon Ross {
18112b65585SGordon Ross 
18212b65585SGordon Ross 	if (smbd.s_authsvc_tid != 0) {
18312b65585SGordon Ross 		(void) pthread_kill(smbd.s_authsvc_tid, SIGTERM);
18412b65585SGordon Ross 		smbd.s_authsvc_tid = 0;
18512b65585SGordon Ross 	}
18612b65585SGordon Ross }
18712b65585SGordon Ross 
18812b65585SGordon Ross static int
smbd_authsock_create(void)18912b65585SGordon Ross smbd_authsock_create(void)
19012b65585SGordon Ross {
19112b65585SGordon Ross 	int sock = -1;
19212b65585SGordon Ross 
19312b65585SGordon Ross 	sock = socket(AF_UNIX, SOCK_STREAM, 0);
19412b65585SGordon Ross 	if (sock < 0) {
19512b65585SGordon Ross 		smbd_report("authsvc, socket create failed, %d", errno);
19612b65585SGordon Ross 		return (errno);
19712b65585SGordon Ross 	}
19812b65585SGordon Ross 
19912b65585SGordon Ross 	(void) unlink(smbauth_sockname.sun_path);
20012b65585SGordon Ross 	if (bind(sock, (struct sockaddr *)&smbauth_sockname,
20112b65585SGordon Ross 	    sizeof (smbauth_sockname)) < 0) {
20212b65585SGordon Ross 		smbd_report("authsvc, socket bind failed, %d", errno);
20312b65585SGordon Ross 		(void) close(sock);
20412b65585SGordon Ross 		return (errno);
20512b65585SGordon Ross 	}
20612b65585SGordon Ross 
20712b65585SGordon Ross 	if (listen(sock, SOMAXCONN) < 0) {
20812b65585SGordon Ross 		smbd_report("authsvc, socket listen failed, %d", errno);
20912b65585SGordon Ross 		(void) close(sock);
21012b65585SGordon Ross 		return (errno);
21112b65585SGordon Ross 	}
21212b65585SGordon Ross 
21312b65585SGordon Ross 	smbd.s_authsvc_sock = sock;
21412b65585SGordon Ross 	return (0);
21512b65585SGordon Ross }
21612b65585SGordon Ross 
21712b65585SGordon Ross static void
smbd_authsock_destroy(void)21812b65585SGordon Ross smbd_authsock_destroy(void)
21912b65585SGordon Ross {
22012b65585SGordon Ross 	int fid;
22112b65585SGordon Ross 
22212b65585SGordon Ross 	if ((fid = smbd.s_authsvc_sock) != -1) {
22312b65585SGordon Ross 		smbd.s_authsvc_sock = -1;
22412b65585SGordon Ross 		(void) close(fid);
22512b65585SGordon Ross 	}
22612b65585SGordon Ross }
22712b65585SGordon Ross 
228b6b7639aSGordon Ross #ifndef FKSMBD
229*5d8538b6SGordon Ross /*
230*5d8538b6SGordon Ross  * Decide whether to communicate with the client on this AF_UNIX socket.
231*5d8538b6SGordon Ross  * Normally the caller should be the (in-kernel) SMB service which has
232*5d8538b6SGordon Ross  * (typically) all privileges.  We test for PRIV_SYS_SMB here, which
233*5d8538b6SGordon Ross  * only the SMB service should have.
234*5d8538b6SGordon Ross  */
235b6b7639aSGordon Ross static boolean_t
authsock_has_priv(int sock)236b6b7639aSGordon Ross authsock_has_priv(int sock)
237b6b7639aSGordon Ross {
238b6b7639aSGordon Ross 	ucred_t *uc = NULL;
239b6b7639aSGordon Ross 	const priv_set_t *ps = NULL;
240b6b7639aSGordon Ross 	boolean_t ret = B_FALSE;
241b6b7639aSGordon Ross 	pid_t  clpid;
242b6b7639aSGordon Ross 
243b6b7639aSGordon Ross 	if (getpeerucred(sock, &uc) != 0) {
244b6b7639aSGordon Ross 		smbd_report("authsvc: getpeerucred err %d", errno);
245b6b7639aSGordon Ross 		return (B_FALSE);
246b6b7639aSGordon Ross 	}
247b6b7639aSGordon Ross 	clpid = ucred_getpid(uc);
248b6b7639aSGordon Ross 	ps = ucred_getprivset(uc, PRIV_EFFECTIVE);
249b6b7639aSGordon Ross 	if (ps == NULL) {
250b6b7639aSGordon Ross 		smbd_report("authsvc: ucred_getprivset failed");
251b6b7639aSGordon Ross 		goto out;
252b6b7639aSGordon Ross 	}
253b6b7639aSGordon Ross 
254b6b7639aSGordon Ross 	/*
255*5d8538b6SGordon Ross 	 * Require sys_smb priv.
256b6b7639aSGordon Ross 	 */
257b6b7639aSGordon Ross 	if (priv_ismember(ps, PRIV_SYS_SMB)) {
258b6b7639aSGordon Ross 		ret = B_TRUE;
259b6b7639aSGordon Ross 		goto out;
260b6b7639aSGordon Ross 	}
261b6b7639aSGordon Ross 
262b6b7639aSGordon Ross 	if (smbd.s_debug) {
263b6b7639aSGordon Ross 		smbd_report("authsvc: non-privileged client "
264b6b7639aSGordon Ross 		    "PID = %d UID = %d",
265b6b7639aSGordon Ross 		    (int)clpid, ucred_getruid(uc));
266b6b7639aSGordon Ross 	}
267b6b7639aSGordon Ross 
268b6b7639aSGordon Ross out:
269b6b7639aSGordon Ross 	/* ps is free'd with the ucred */
270b6b7639aSGordon Ross 	if (uc != NULL)
271b6b7639aSGordon Ross 		ucred_free(uc);
272b6b7639aSGordon Ross 
273b6b7639aSGordon Ross 	return (ret);
274b6b7639aSGordon Ross }
275b6b7639aSGordon Ross #endif
276b6b7639aSGordon Ross 
277b6b7639aSGordon Ross 
27812b65585SGordon Ross static void *
smbd_authsvc_listen(void * arg)27912b65585SGordon Ross smbd_authsvc_listen(void *arg)
28012b65585SGordon Ross {
28112b65585SGordon Ross 	authsvc_context_t *ctx;
28212b65585SGordon Ross 	pthread_attr_t	attr;
28312b65585SGordon Ross 	pthread_t	tid;
28412b65585SGordon Ross 	socklen_t	slen;
28512b65585SGordon Ross 	int		ls, ns, rc;
28612b65585SGordon Ross 
28712b65585SGordon Ross 	_NOTE(ARGUNUSED(arg))
28812b65585SGordon Ross 
28912b65585SGordon Ross 	(void) pthread_attr_init(&attr);
29012b65585SGordon Ross 	(void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
29112b65585SGordon Ross 
29212b65585SGordon Ross 	ls = smbd.s_authsvc_sock;
29312b65585SGordon Ross 	for (;;) {
29412b65585SGordon Ross 
29512b65585SGordon Ross 		slen = 0;
29612b65585SGordon Ross 		ns = accept(ls, NULL, &slen);
29712b65585SGordon Ross 		if (ns < 0) {
29812b65585SGordon Ross 			switch (errno) {
29912b65585SGordon Ross 			case ECONNABORTED:
30012b65585SGordon Ross 				continue;
30112b65585SGordon Ross 			case EINTR:
30212b65585SGordon Ross 				/* normal termination */
30312b65585SGordon Ross 				goto out;
30412b65585SGordon Ross 			default:
30512b65585SGordon Ross 				smbd_report("authsvc, socket accept failed,"
30612b65585SGordon Ross 				    " %d", errno);
30712b65585SGordon Ross 				goto out;
30812b65585SGordon Ross 			}
30912b65585SGordon Ross 		}
31012b65585SGordon Ross 
311b6b7639aSGordon Ross #ifndef FKSMBD
312b6b7639aSGordon Ross 		if (!authsock_has_priv(ns)) {
313b6b7639aSGordon Ross 			close(ns);
314b6b7639aSGordon Ross 			continue;
315b6b7639aSGordon Ross 		}
316b6b7639aSGordon Ross #endif
317b6b7639aSGordon Ross 
31812b65585SGordon Ross 		/*
31912b65585SGordon Ross 		 * Limit the number of auth. sockets
32012b65585SGordon Ross 		 * (and the threads that service them).
32112b65585SGordon Ross 		 */
32212b65585SGordon Ross 		(void) mutex_lock(&smbd_authsvc_mutex);
32312b65585SGordon Ross 		if (smbd_authsvc_thrcnt >= smbd_authsvc_maxthread) {
32412b65585SGordon Ross 			(void) mutex_unlock(&smbd_authsvc_mutex);
32512b65585SGordon Ross 			(void) close(ns);
32612b65585SGordon Ross 			smbd_authsvc_flood();
32712b65585SGordon Ross 			continue;
32812b65585SGordon Ross 		}
32912b65585SGordon Ross 		smbd_authsvc_thrcnt++;
33012b65585SGordon Ross 		if (smbd_authsvc_hiwat < smbd_authsvc_thrcnt)
33112b65585SGordon Ross 			smbd_authsvc_hiwat = smbd_authsvc_thrcnt;
33212b65585SGordon Ross 		(void) mutex_unlock(&smbd_authsvc_mutex);
33312b65585SGordon Ross 
33412b65585SGordon Ross 		ctx = smbd_authctx_create();
33512b65585SGordon Ross 		if (ctx == NULL) {
33612b65585SGordon Ross 			smbd_report("authsvc, can't allocate context");
33712b65585SGordon Ross 			(void) mutex_lock(&smbd_authsvc_mutex);
33812b65585SGordon Ross 			smbd_authsvc_thrcnt--;
33912b65585SGordon Ross 			(void) mutex_unlock(&smbd_authsvc_mutex);
34012b65585SGordon Ross 			(void) close(ns);
3416926de2eSGordon Ross 			smbd_nomem();
34212b65585SGordon Ross 		}
34312b65585SGordon Ross 		ctx->ctx_socket = ns;
34412b65585SGordon Ross 
34512b65585SGordon Ross 		rc = pthread_create(&tid, &attr, smbd_authsvc_work, ctx);
34612b65585SGordon Ross 		if (rc) {
34712b65585SGordon Ross 			smbd_report("authsvc, thread create failed, %d", rc);
34812b65585SGordon Ross 			(void) mutex_lock(&smbd_authsvc_mutex);
34912b65585SGordon Ross 			smbd_authsvc_thrcnt--;
35012b65585SGordon Ross 			(void) mutex_unlock(&smbd_authsvc_mutex);
35112b65585SGordon Ross 			smbd_authctx_destroy(ctx);
3526926de2eSGordon Ross 			ctx = NULL;
3536926de2eSGordon Ross 			smbd_nomem();
35412b65585SGordon Ross 		}
3552a2a3aabSGordon Ross 		ctx = NULL; /* given to the new thread or destroyed */
3566926de2eSGordon Ross 		(void) pthread_detach(tid);
35712b65585SGordon Ross 	}
35812b65585SGordon Ross 
35912b65585SGordon Ross out:
36012b65585SGordon Ross 	(void) pthread_attr_destroy(&attr);
36112b65585SGordon Ross 	smbd_authsock_destroy();
36212b65585SGordon Ross 	return (NULL);
36312b65585SGordon Ross }
36412b65585SGordon Ross 
36512b65585SGordon Ross static void
smbd_authsvc_flood(void)36612b65585SGordon Ross smbd_authsvc_flood(void)
36712b65585SGordon Ross {
36812b65585SGordon Ross 	static uint_t count;
36912b65585SGordon Ross 	static time_t last_report;
37012b65585SGordon Ross 	time_t now = time(NULL);
37112b65585SGordon Ross 
37212b65585SGordon Ross 	count++;
37312b65585SGordon Ross 	if (last_report + 60 < now) {
37412b65585SGordon Ross 		last_report = now;
37512b65585SGordon Ross 		smbd_report("authsvc: flooded %u", count);
37612b65585SGordon Ross 		count = 0;
37712b65585SGordon Ross 	}
37812b65585SGordon Ross }
37912b65585SGordon Ross 
38012b65585SGordon Ross authsvc_context_t *
smbd_authctx_create(void)38112b65585SGordon Ross smbd_authctx_create(void)
38212b65585SGordon Ross {
38312b65585SGordon Ross 	authsvc_context_t *ctx;
38412b65585SGordon Ross 
38512b65585SGordon Ross 	ctx = malloc(sizeof (*ctx));
38612b65585SGordon Ross 	if (ctx == NULL)
38712b65585SGordon Ross 		return (NULL);
38812b65585SGordon Ross 	bzero(ctx, sizeof (*ctx));
38912b65585SGordon Ross 
39012b65585SGordon Ross 	ctx->ctx_irawlen = smbd_authsvc_bufsize;
39112b65585SGordon Ross 	ctx->ctx_irawbuf = malloc(ctx->ctx_irawlen);
39212b65585SGordon Ross 	ctx->ctx_orawlen = smbd_authsvc_bufsize;
39312b65585SGordon Ross 	ctx->ctx_orawbuf = malloc(ctx->ctx_orawlen);
39412b65585SGordon Ross 	if (ctx->ctx_irawbuf == NULL || ctx->ctx_orawbuf == NULL)
39512b65585SGordon Ross 		goto errout;
39612b65585SGordon Ross 
39712b65585SGordon Ross 	ctx->ctx_ibodylen = smbd_authsvc_bufsize;
39812b65585SGordon Ross 	ctx->ctx_ibodybuf = malloc(ctx->ctx_ibodylen);
39912b65585SGordon Ross 	ctx->ctx_obodylen = smbd_authsvc_bufsize;
40012b65585SGordon Ross 	ctx->ctx_obodybuf = malloc(ctx->ctx_obodylen);
40112b65585SGordon Ross 	if (ctx->ctx_ibodybuf == NULL || ctx->ctx_obodybuf == NULL)
40212b65585SGordon Ross 		goto errout;
40312b65585SGordon Ross 
40412b65585SGordon Ross 	return (ctx);
40512b65585SGordon Ross 
40612b65585SGordon Ross errout:
40712b65585SGordon Ross 	smbd_authctx_destroy(ctx);
40812b65585SGordon Ross 	return (NULL);
40912b65585SGordon Ross }
41012b65585SGordon Ross 
41112b65585SGordon Ross void
smbd_authctx_destroy(authsvc_context_t * ctx)41212b65585SGordon Ross smbd_authctx_destroy(authsvc_context_t *ctx)
41312b65585SGordon Ross {
41412b65585SGordon Ross 	if (ctx->ctx_socket != -1) {
41512b65585SGordon Ross 		(void) close(ctx->ctx_socket);
41612b65585SGordon Ross 		ctx->ctx_socket = -1;
41712b65585SGordon Ross 	}
41812b65585SGordon Ross 
41912b65585SGordon Ross 	if (ctx->ctx_token != NULL)
42012b65585SGordon Ross 		smb_token_destroy(ctx->ctx_token);
42112b65585SGordon Ross 
42212b65585SGordon Ross 	if (ctx->ctx_itoken != NULL)
42312b65585SGordon Ross 		spnegoFreeData(ctx->ctx_itoken);
42412b65585SGordon Ross 	if (ctx->ctx_otoken != NULL)
42512b65585SGordon Ross 		spnegoFreeData(ctx->ctx_otoken);
42612b65585SGordon Ross 
42712b65585SGordon Ross 	free(ctx->ctx_irawbuf);
42812b65585SGordon Ross 	free(ctx->ctx_orawbuf);
42912b65585SGordon Ross 	free(ctx->ctx_ibodybuf);
43012b65585SGordon Ross 	free(ctx->ctx_obodybuf);
43112b65585SGordon Ross 
43212b65585SGordon Ross 	free(ctx);
43312b65585SGordon Ross }
43412b65585SGordon Ross 
43512b65585SGordon Ross /*
43612b65585SGordon Ross  * Limit how long smbd_authsvc_work will wait for the client to
43712b65585SGordon Ross  * send us the next part of the authentication sequence.
43812b65585SGordon Ross  */
43912b65585SGordon Ross static struct timeval recv_tmo = { 30, 0 };
44012b65585SGordon Ross 
44112b65585SGordon Ross /*
44212b65585SGordon Ross  * Also set a timeout for send, where we're sending a response to
44312b65585SGordon Ross  * the client side (in smbsrv).  That should always be waiting in
44412b65585SGordon Ross  * recv by the time we send, so a short timeout is OK.
44512b65585SGordon Ross  */
44612b65585SGordon Ross static struct timeval send_tmo = { 15, 0 };
44712b65585SGordon Ross 
44812b65585SGordon Ross static void *
smbd_authsvc_work(void * arg)44912b65585SGordon Ross smbd_authsvc_work(void *arg)
45012b65585SGordon Ross {
45112b65585SGordon Ross 	authsvc_context_t *ctx = arg;
45212b65585SGordon Ross 	smb_lsa_msg_hdr_t	hdr;
45312b65585SGordon Ross 	int sock = ctx->ctx_socket;
45412b65585SGordon Ross 	int len, rc;
45512b65585SGordon Ross 
45612b65585SGordon Ross 	if (setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO,
45712b65585SGordon Ross 	    (char *)&send_tmo,  sizeof (send_tmo)) != 0) {
45812b65585SGordon Ross 		smbd_report("authsvc_work: set set timeout: %m");
45912b65585SGordon Ross 		goto out;
46012b65585SGordon Ross 	}
46112b65585SGordon Ross 
46212b65585SGordon Ross 	if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO,
46312b65585SGordon Ross 	    (char *)&recv_tmo,  sizeof (recv_tmo)) != 0) {
46412b65585SGordon Ross 		smbd_report("authsvc_work: set recv timeout: %m");
46512b65585SGordon Ross 		goto out;
46612b65585SGordon Ross 	}
46712b65585SGordon Ross 
46812b65585SGordon Ross 	for (;;) {
46912b65585SGordon Ross 
47012b65585SGordon Ross 		len = recv(sock, &hdr, sizeof (hdr), MSG_WAITALL);
47112b65585SGordon Ross 		if (len <= 0) {
47212b65585SGordon Ross 			/* normal termination */
47312b65585SGordon Ross 			break;
47412b65585SGordon Ross 		}
47512b65585SGordon Ross 		if (len != sizeof (hdr)) {
47612b65585SGordon Ross 			smbd_report("authsvc_work: read header failed");
47712b65585SGordon Ross 			break;
47812b65585SGordon Ross 		}
47912b65585SGordon Ross 
48012b65585SGordon Ross 		if (hdr.lmh_msglen > smbd_authsvc_bufsize) {
48112b65585SGordon Ross 			smbd_report("authsvc_work: msg too large");
48212b65585SGordon Ross 			break;
48312b65585SGordon Ross 		}
48412b65585SGordon Ross 
48512b65585SGordon Ross 		if (hdr.lmh_msglen > 0) {
48612b65585SGordon Ross 			len = recv(sock, ctx->ctx_irawbuf, hdr.lmh_msglen,
48712b65585SGordon Ross 			    MSG_WAITALL);
48812b65585SGordon Ross 			if (len != hdr.lmh_msglen) {
48912b65585SGordon Ross 				smbd_report("authsvc_work: read mesg failed");
49012b65585SGordon Ross 				break;
49112b65585SGordon Ross 			}
49212b65585SGordon Ross 		}
49312b65585SGordon Ross 		ctx->ctx_irawtype = hdr.lmh_msgtype;
49412b65585SGordon Ross 		ctx->ctx_irawlen = hdr.lmh_msglen;
49512b65585SGordon Ross 		ctx->ctx_orawlen = smbd_authsvc_bufsize;
49612b65585SGordon Ross 		ctx->ctx_ibodylen = smbd_authsvc_bufsize;
49712b65585SGordon Ross 		ctx->ctx_obodylen = smbd_authsvc_bufsize;
49812b65585SGordon Ross 
49912b65585SGordon Ross 		/*
50012b65585SGordon Ross 		 * The real work happens here.
50112b65585SGordon Ross 		 */
50212b65585SGordon Ross 		rc = smbd_authsvc_dispatch(ctx);
50312b65585SGordon Ross 		if (rc)
50412b65585SGordon Ross 			break;
50512b65585SGordon Ross 
50612b65585SGordon Ross 		hdr.lmh_msgtype = ctx->ctx_orawtype;
50712b65585SGordon Ross 		hdr.lmh_msglen = ctx->ctx_orawlen;
50812b65585SGordon Ross 		len = send(sock, &hdr, sizeof (hdr), 0);
50912b65585SGordon Ross 		if (len != sizeof (hdr)) {
51012b65585SGordon Ross 			smbd_report("authsvc_work: send failed");
51112b65585SGordon Ross 			break;
51212b65585SGordon Ross 		}
51312b65585SGordon Ross 
51412b65585SGordon Ross 		if (ctx->ctx_orawlen > 0) {
51512b65585SGordon Ross 			len = send(sock, ctx->ctx_orawbuf,
51612b65585SGordon Ross 			    ctx->ctx_orawlen, 0);
51712b65585SGordon Ross 			if (len != ctx->ctx_orawlen) {
51812b65585SGordon Ross 				smbd_report("authsvc_work: send failed");
51912b65585SGordon Ross 				break;
52012b65585SGordon Ross 			}
52112b65585SGordon Ross 		}
52212b65585SGordon Ross 	}
52312b65585SGordon Ross 
52412b65585SGordon Ross out:
52512b65585SGordon Ross 	if (ctx->ctx_mh_fini)
52612b65585SGordon Ross 		(ctx->ctx_mh_fini)(ctx);
52712b65585SGordon Ross 
52812b65585SGordon Ross 	smbd_authctx_destroy(ctx);
52912b65585SGordon Ross 
53012b65585SGordon Ross 	(void) mutex_lock(&smbd_authsvc_mutex);
53112b65585SGordon Ross 	smbd_authsvc_thrcnt--;
53212b65585SGordon Ross 	(void) mutex_unlock(&smbd_authsvc_mutex);
53312b65585SGordon Ross 
53412b65585SGordon Ross 	return (NULL);	/* implied pthread_exit() */
53512b65585SGordon Ross }
53612b65585SGordon Ross 
53712b65585SGordon Ross /*
53812b65585SGordon Ross  * Dispatch based on message type LSA_MTYPE_...
53912b65585SGordon Ross  * Non-zero return here ends the conversation.
54012b65585SGordon Ross  */
54112b65585SGordon Ross int
smbd_authsvc_dispatch(authsvc_context_t * ctx)54212b65585SGordon Ross smbd_authsvc_dispatch(authsvc_context_t *ctx)
54312b65585SGordon Ross {
54412b65585SGordon Ross 	int rc;
54512b65585SGordon Ross 
54612b65585SGordon Ross 	switch (ctx->ctx_irawtype) {
54712b65585SGordon Ross 
54812b65585SGordon Ross 	case LSA_MTYPE_OLDREQ:
54912b65585SGordon Ross #ifdef DEBUG
55012b65585SGordon Ross 		if (smbd_authsvc_slowdown)
55112b65585SGordon Ross 			(void) sleep(smbd_authsvc_slowdown);
55212b65585SGordon Ross #endif
55312b65585SGordon Ross 		rc = smbd_authsvc_oldreq(ctx);
55412b65585SGordon Ross 		break;
55512b65585SGordon Ross 
55612b65585SGordon Ross 	case LSA_MTYPE_CLINFO:
55712b65585SGordon Ross 		rc = smbd_authsvc_clinfo(ctx);
55812b65585SGordon Ross 		break;
55912b65585SGordon Ross 
56012b65585SGordon Ross 	case LSA_MTYPE_ESFIRST:
56112b65585SGordon Ross 		rc = smbd_authsvc_esfirst(ctx);
56212b65585SGordon Ross 		break;
56312b65585SGordon Ross 
56412b65585SGordon Ross 	case LSA_MTYPE_ESNEXT:
56512b65585SGordon Ross #ifdef DEBUG
56612b65585SGordon Ross 		if (smbd_authsvc_slowdown)
56712b65585SGordon Ross 			(void) sleep(smbd_authsvc_slowdown);
56812b65585SGordon Ross #endif
56912b65585SGordon Ross 		rc = smbd_authsvc_esnext(ctx);
57012b65585SGordon Ross 		break;
57112b65585SGordon Ross 
57212b65585SGordon Ross 	case LSA_MTYPE_GETTOK:
57312b65585SGordon Ross 		rc = smbd_authsvc_gettoken(ctx);
57412b65585SGordon Ross 		break;
57512b65585SGordon Ross 
57612b65585SGordon Ross 		/* response types */
57712b65585SGordon Ross 	case LSA_MTYPE_OK:
57812b65585SGordon Ross 	case LSA_MTYPE_ERROR:
57912b65585SGordon Ross 	case LSA_MTYPE_TOKEN:
58012b65585SGordon Ross 	case LSA_MTYPE_ES_CONT:
58112b65585SGordon Ross 	case LSA_MTYPE_ES_DONE:
58212b65585SGordon Ross 	default:
58312b65585SGordon Ross 		return (-1);
58412b65585SGordon Ross 	}
58512b65585SGordon Ross 
5866926de2eSGordon Ross 	if (rc == NT_STATUS_NO_MEMORY)
5876926de2eSGordon Ross 		smbd_nomem();
5886926de2eSGordon Ross 
58912b65585SGordon Ross 	if (rc != 0) {
59012b65585SGordon Ross 		smb_lsa_eresp_t *er = ctx->ctx_orawbuf;
59112b65585SGordon Ross 		ctx->ctx_orawtype = LSA_MTYPE_ERROR;
59212b65585SGordon Ross 		ctx->ctx_orawlen = sizeof (*er);
59312b65585SGordon Ross 		er->ler_ntstatus = rc;
59412b65585SGordon Ross 		er->ler_errclass = 0;
59512b65585SGordon Ross 		er->ler_errcode = 0;
59612b65585SGordon Ross 	}
59712b65585SGordon Ross 	return (0);
59812b65585SGordon Ross }
59912b65585SGordon Ross 
60012b65585SGordon Ross static int
smbd_authsvc_oldreq(authsvc_context_t * ctx)60112b65585SGordon Ross smbd_authsvc_oldreq(authsvc_context_t *ctx)
60212b65585SGordon Ross {
60312b65585SGordon Ross 	smb_logon_t	user_info;
60412b65585SGordon Ross 	XDR		xdrs;
60512b65585SGordon Ross 	smb_token_t	*token = NULL;
60612b65585SGordon Ross 	int		rc = 0;
60712b65585SGordon Ross 
60812b65585SGordon Ross 	bzero(&user_info, sizeof (user_info));
60912b65585SGordon Ross 	xdrmem_create(&xdrs, ctx->ctx_irawbuf, ctx->ctx_irawlen,
61012b65585SGordon Ross 	    XDR_DECODE);
61112b65585SGordon Ross 	if (!smb_logon_xdr(&xdrs, &user_info)) {
61212b65585SGordon Ross 		xdr_destroy(&xdrs);
61312b65585SGordon Ross 		return (NT_STATUS_INVALID_PARAMETER);
61412b65585SGordon Ross 	}
61512b65585SGordon Ross 	xdr_destroy(&xdrs);
61612b65585SGordon Ross 
61712b65585SGordon Ross 	token = smbd_user_auth_logon(&user_info);
61812b65585SGordon Ross 	xdr_free(smb_logon_xdr, (char *)&user_info);
619975041ddSGordon Ross 	if (token == NULL) {
620975041ddSGordon Ross 		rc = user_info.lg_status;
621975041ddSGordon Ross 		if (rc == 0) /* should not happen */
622975041ddSGordon Ross 			rc = NT_STATUS_INTERNAL_ERROR;
623975041ddSGordon Ross 		return (rc);
624975041ddSGordon Ross 	}
62512b65585SGordon Ross 
62612b65585SGordon Ross 	ctx->ctx_token = token;
62712b65585SGordon Ross 
62812b65585SGordon Ross 	return (rc);
62912b65585SGordon Ross }
63012b65585SGordon Ross 
63112b65585SGordon Ross static int
smbd_authsvc_clinfo(authsvc_context_t * ctx)63212b65585SGordon Ross smbd_authsvc_clinfo(authsvc_context_t *ctx)
63312b65585SGordon Ross {
63412b65585SGordon Ross 
63512b65585SGordon Ross 	if (ctx->ctx_irawlen != sizeof (smb_lsa_clinfo_t))
63612b65585SGordon Ross 		return (NT_STATUS_INTERNAL_ERROR);
63712b65585SGordon Ross 	(void) memcpy(&ctx->ctx_clinfo, ctx->ctx_irawbuf,
63812b65585SGordon Ross 	    sizeof (smb_lsa_clinfo_t));
63912b65585SGordon Ross 
64012b65585SGordon Ross 	ctx->ctx_orawtype = LSA_MTYPE_OK;
64112b65585SGordon Ross 	ctx->ctx_orawlen = 0;
64212b65585SGordon Ross 	return (0);
64312b65585SGordon Ross }
64412b65585SGordon Ross 
64512b65585SGordon Ross /*
64612b65585SGordon Ross  * Handle a security blob we've received from the client.
64712b65585SGordon Ross  * Incoming type: LSA_MTYPE_ESFIRST
64812b65585SGordon Ross  * Outgoing types: LSA_MTYPE_ES_CONT, LSA_MTYPE_ES_DONE,
64912b65585SGordon Ross  *   LSA_MTYPE_ERROR
65012b65585SGordon Ross  */
65112b65585SGordon Ross static int
smbd_authsvc_esfirst(authsvc_context_t * ctx)65212b65585SGordon Ross smbd_authsvc_esfirst(authsvc_context_t *ctx)
65312b65585SGordon Ross {
65412b65585SGordon Ross 	const spnego_mech_handler_t *mh;
65512b65585SGordon Ross 	int idx, pref, rc;
65612b65585SGordon Ross 	int best_pref = 1000;
65712b65585SGordon Ross 	int best_mhidx = -1;
65812b65585SGordon Ross 
65912b65585SGordon Ross 	/*
66012b65585SGordon Ross 	 * NTLMSSP header is 8+, SPNEGO is 10+
66112b65585SGordon Ross 	 */
66212b65585SGordon Ross 	if (ctx->ctx_irawlen < 8) {
66312b65585SGordon Ross 		smbd_report("authsvc: short blob");
66412b65585SGordon Ross 		return (NT_STATUS_INVALID_PARAMETER);
66512b65585SGordon Ross 	}
66612b65585SGordon Ross 
66712b65585SGordon Ross 	/*
66812b65585SGordon Ross 	 * We could have "Raw NTLMSSP" here intead of SPNEGO.
66912b65585SGordon Ross 	 */
67012b65585SGordon Ross 	if (bcmp(ctx->ctx_irawbuf, "NTLMSSP", 8) == 0) {
67112b65585SGordon Ross 		rc = smbd_raw_ntlmssp_esfirst(ctx);
67212b65585SGordon Ross 		return (rc);
67312b65585SGordon Ross 	}
67412b65585SGordon Ross 
67512b65585SGordon Ross 	/*
67612b65585SGordon Ross 	 * Parse the SPNEGO token, check its type.
67712b65585SGordon Ross 	 */
67812b65585SGordon Ross 	rc = spnegoInitFromBinary(ctx->ctx_irawbuf,
67912b65585SGordon Ross 	    ctx->ctx_irawlen, &ctx->ctx_itoken);
68012b65585SGordon Ross 	if (rc != 0) {
68112b65585SGordon Ross 		smbd_report("authsvc: spnego parse failed");
68212b65585SGordon Ross 		return (NT_STATUS_INVALID_PARAMETER);
68312b65585SGordon Ross 	}
68412b65585SGordon Ross 
68512b65585SGordon Ross 	rc = spnegoGetTokenType(ctx->ctx_itoken, &ctx->ctx_itoktype);
68612b65585SGordon Ross 	if (rc != 0) {
68712b65585SGordon Ross 		smbd_report("authsvc: spnego get token type failed");
68812b65585SGordon Ross 		return (NT_STATUS_INVALID_PARAMETER);
68912b65585SGordon Ross 	}
69012b65585SGordon Ross 
69112b65585SGordon Ross 	if (ctx->ctx_itoktype != SPNEGO_TOKEN_INIT) {
69212b65585SGordon Ross 		smbd_report("authsvc: spnego wrong token type %d",
69312b65585SGordon Ross 		    ctx->ctx_itoktype);
69412b65585SGordon Ross 		return (NT_STATUS_INVALID_PARAMETER);
69512b65585SGordon Ross 	}
69612b65585SGordon Ross 
69712b65585SGordon Ross 	/*
69812b65585SGordon Ross 	 * Figure out which mech type to use.  We want to use the
69912b65585SGordon Ross 	 * first of the client's supported mechanisms that we also
70012b65585SGordon Ross 	 * support.  Unfortunately, the spnego code does not have an
70112b65585SGordon Ross 	 * interface to walk the token's mech list, so we have to
70212b65585SGordon Ross 	 * ask about each mech type we know and keep track of which
70312b65585SGordon Ross 	 * was earliest in the token's mech list.
70412b65585SGordon Ross 	 *
70512b65585SGordon Ross 	 * Also, skip the Kerberos mechanisms in workgroup mode.
70612b65585SGordon Ross 	 */
70712b65585SGordon Ross 	idx = 0;
70812b65585SGordon Ross 	mh = mech_table;
70912b65585SGordon Ross 	if (smb_config_get_secmode() != SMB_SECMODE_DOMAIN) {
71012b65585SGordon Ross 		idx = MECH_TBL_IDX_NTLMSSP;
71112b65585SGordon Ross 		mh = &mech_table[idx];
71212b65585SGordon Ross 	}
71312b65585SGordon Ross 	for (; mh->mh_init != NULL; idx++, mh++) {
71412b65585SGordon Ross 
71512b65585SGordon Ross 		if (spnegoIsMechTypeAvailable(ctx->ctx_itoken,
71612b65585SGordon Ross 		    mh->mh_oid, &pref) != 0)
71712b65585SGordon Ross 			continue;
71812b65585SGordon Ross 
71912b65585SGordon Ross 		if (pref < best_pref) {
72012b65585SGordon Ross 			best_pref = pref;
72112b65585SGordon Ross 			best_mhidx = idx;
72212b65585SGordon Ross 		}
72312b65585SGordon Ross 	}
72412b65585SGordon Ross 	if (best_mhidx == -1) {
72512b65585SGordon Ross 		smbd_report("authsvc: no supported spnego mechanism");
72612b65585SGordon Ross 		return (NT_STATUS_INVALID_PARAMETER);
72712b65585SGordon Ross 	}
72812b65585SGordon Ross 
72912b65585SGordon Ross 	/* Found a mutually agreeable mech. */
73012b65585SGordon Ross 	mh = &mech_table[best_mhidx];
73112b65585SGordon Ross 	ctx->ctx_mech_oid = mh->mh_oid;
73212b65585SGordon Ross 	ctx->ctx_mh_work = mh->mh_work;
73312b65585SGordon Ross 	ctx->ctx_mh_fini = mh->mh_fini;
73412b65585SGordon Ross 	rc = mh->mh_init(ctx);
73512b65585SGordon Ross 	if (rc != 0) {
73612b65585SGordon Ross 		smbd_report("authsvc: mech init failed");
73712b65585SGordon Ross 		return (rc);
73812b65585SGordon Ross 	}
73912b65585SGordon Ross 
74012b65585SGordon Ross 	/*
741cc86afeeSGordon Ross 	 * If the best supported mech was not the first in the list,
742cc86afeeSGordon Ross 	 * we need to ask the client to use a different one, and
743cc86afeeSGordon Ross 	 * skip (ignore) the provided token body.
74412b65585SGordon Ross 	 */
745cc86afeeSGordon Ross 	if (best_pref != 0) {
746cc86afeeSGordon Ross 		rc = smbd_authsvc_newmech(ctx);
747cc86afeeSGordon Ross 	} else {
748cc86afeeSGordon Ross 		/*
749cc86afeeSGordon Ross 		 * Common to LSA_MTYPE_ESFIRST, LSA_MTYPE_ESNEXT
750cc86afeeSGordon Ross 		 */
751cc86afeeSGordon Ross 		rc = smbd_authsvc_escmn(ctx);
752cc86afeeSGordon Ross 	}
75312b65585SGordon Ross 	return (rc);
75412b65585SGordon Ross }
75512b65585SGordon Ross 
75612b65585SGordon Ross /*
75712b65585SGordon Ross  * Handle a security blob we've received from the client.
75812b65585SGordon Ross  * Incoming type: LSA_MTYPE_ESNEXT
75912b65585SGordon Ross  * Outgoing types: LSA_MTYPE_ES_CONT, LSA_MTYPE_ES_DONE,
76012b65585SGordon Ross  *   LSA_MTYPE_ERROR
76112b65585SGordon Ross  */
76212b65585SGordon Ross static int
smbd_authsvc_esnext(authsvc_context_t * ctx)76312b65585SGordon Ross smbd_authsvc_esnext(authsvc_context_t *ctx)
76412b65585SGordon Ross {
76512b65585SGordon Ross 	int rc;
76612b65585SGordon Ross 
76712b65585SGordon Ross 	/*
76812b65585SGordon Ross 	 * Make sure LSA_MTYPE_ESFIRST was handled
76912b65585SGordon Ross 	 * previously, so we have a work function.
77012b65585SGordon Ross 	 */
77112b65585SGordon Ross 	if (ctx->ctx_mh_work == NULL)
77212b65585SGordon Ross 		return (NT_STATUS_INVALID_PARAMETER);
77312b65585SGordon Ross 
77412b65585SGordon Ross 	if (ctx->ctx_mech_oid == special_mech_raw_NTLMSSP) {
77512b65585SGordon Ross 		rc = smbd_raw_ntlmssp_esnext(ctx);
77612b65585SGordon Ross 		return (rc);
77712b65585SGordon Ross 	}
77812b65585SGordon Ross 
77912b65585SGordon Ross 	/*
78012b65585SGordon Ross 	 * Cleanup state from previous calls.
78112b65585SGordon Ross 	 */
78212b65585SGordon Ross 	if (ctx->ctx_itoken != NULL) {
78312b65585SGordon Ross 		spnegoFreeData(ctx->ctx_itoken);
78412b65585SGordon Ross 		ctx->ctx_itoken = NULL;
78512b65585SGordon Ross 	}
78612b65585SGordon Ross 
78712b65585SGordon Ross 	/*
78812b65585SGordon Ross 	 * Parse the SPNEGO token, check its type.
78912b65585SGordon Ross 	 */
79012b65585SGordon Ross 	rc = spnegoInitFromBinary(ctx->ctx_irawbuf,
79112b65585SGordon Ross 	    ctx->ctx_irawlen, &ctx->ctx_itoken);
79212b65585SGordon Ross 	if (rc != 0)
79312b65585SGordon Ross 		return (NT_STATUS_INVALID_PARAMETER);
79412b65585SGordon Ross 
79512b65585SGordon Ross 	rc = spnegoGetTokenType(ctx->ctx_itoken, &ctx->ctx_itoktype);
79612b65585SGordon Ross 	if (rc != 0)
79712b65585SGordon Ross 		return (NT_STATUS_INVALID_PARAMETER);
79812b65585SGordon Ross 
79912b65585SGordon Ross 	if (ctx->ctx_itoktype != SPNEGO_TOKEN_TARG)
80012b65585SGordon Ross 		return (NT_STATUS_INVALID_PARAMETER);
80112b65585SGordon Ross 
80212b65585SGordon Ross 	rc = smbd_authsvc_escmn(ctx);
80312b65585SGordon Ross 	return (rc);
80412b65585SGordon Ross }
80512b65585SGordon Ross 
80612b65585SGordon Ross static int
smbd_authsvc_escmn(authsvc_context_t * ctx)80712b65585SGordon Ross smbd_authsvc_escmn(authsvc_context_t *ctx)
80812b65585SGordon Ross {
80912b65585SGordon Ross 	SPNEGO_MECH_OID oid;
81012b65585SGordon Ross 	ulong_t toklen;
81112b65585SGordon Ross 	int rc;
81212b65585SGordon Ross 
81312b65585SGordon Ross 	/*
81412b65585SGordon Ross 	 * Cleanup state from previous calls.
81512b65585SGordon Ross 	 */
81612b65585SGordon Ross 	if (ctx->ctx_otoken != NULL) {
81712b65585SGordon Ross 		spnegoFreeData(ctx->ctx_otoken);
81812b65585SGordon Ross 		ctx->ctx_otoken = NULL;
81912b65585SGordon Ross 	}
82012b65585SGordon Ross 
82112b65585SGordon Ross 	/*
82212b65585SGordon Ross 	 * Extract the payload (mech token).
82312b65585SGordon Ross 	 */
82412b65585SGordon Ross 	toklen = ctx->ctx_ibodylen;
82512b65585SGordon Ross 	rc = spnegoGetMechToken(ctx->ctx_itoken,
82612b65585SGordon Ross 	    ctx->ctx_ibodybuf, &toklen);
82712b65585SGordon Ross 	switch (rc) {
82812b65585SGordon Ross 	case SPNEGO_E_SUCCESS:
82912b65585SGordon Ross 		break;
83012b65585SGordon Ross 	case SPNEGO_E_ELEMENT_UNAVAILABLE:
83112b65585SGordon Ross 		toklen = 0;
83212b65585SGordon Ross 		break;
83312b65585SGordon Ross 	case SPNEGO_E_BUFFER_TOO_SMALL:
83412b65585SGordon Ross 		return (NT_STATUS_BUFFER_TOO_SMALL);
83512b65585SGordon Ross 	default:
83612b65585SGordon Ross 		return (NT_STATUS_INTERNAL_ERROR);
83712b65585SGordon Ross 	}
83812b65585SGordon Ross 	ctx->ctx_ibodylen = toklen;
83912b65585SGordon Ross 
84012b65585SGordon Ross 	/*
84112b65585SGordon Ross 	 * Now that we have the incoming "body" (mech. token),
84212b65585SGordon Ross 	 * call the back-end mech-specific work function to
84312b65585SGordon Ross 	 * create the outgoing "body" (mech. token).
84412b65585SGordon Ross 	 *
84512b65585SGordon Ross 	 * The worker must fill in:  ctx->ctx_negresult,
84612b65585SGordon Ross 	 * and: ctx->ctx_obodylen, but ctx->ctx_obodybuf
84712b65585SGordon Ross 	 * is optional, and is typically NULL after the
84812b65585SGordon Ross 	 * final message of an auth sequence, where
84912b65585SGordon Ross 	 * negresult == spnego_negresult_complete.
85012b65585SGordon Ross 	 */
85112b65585SGordon Ross 	rc = ctx->ctx_mh_work(ctx);
85212b65585SGordon Ross 	if (rc != 0)
85312b65585SGordon Ross 		return (rc);
85412b65585SGordon Ross 
85512b65585SGordon Ross 	/*
85612b65585SGordon Ross 	 * Wrap the outgoing body in a negTokenTarg SPNEGO token.
85712b65585SGordon Ross 	 * The selected mech. OID is returned only when the
85812b65585SGordon Ross 	 * incoming token was of type SPNEGO_TOKEN_INIT.
85912b65585SGordon Ross 	 */
86012b65585SGordon Ross 	if (ctx->ctx_itoktype == SPNEGO_TOKEN_INIT) {
86112b65585SGordon Ross 		/* tell the client the selected mech. */
86212b65585SGordon Ross 		oid = ctx->ctx_mech_oid;
86312b65585SGordon Ross 	} else {
864a44e7c2cSGordon Ross 		/* Omit the "supported mech." field. */
86512b65585SGordon Ross 		oid = spnego_mech_oid_NotUsed;
86612b65585SGordon Ross 	}
86712b65585SGordon Ross 
86812b65585SGordon Ross 	/*
86912b65585SGordon Ross 	 * Determine the spnego "negresult" from the
87012b65585SGordon Ross 	 * reply message type (from the work func).
87112b65585SGordon Ross 	 */
87212b65585SGordon Ross 	switch (ctx->ctx_orawtype) {
87312b65585SGordon Ross 	case LSA_MTYPE_ERROR:
87412b65585SGordon Ross 		ctx->ctx_negresult = spnego_negresult_rejected;
87512b65585SGordon Ross 		break;
87612b65585SGordon Ross 	case LSA_MTYPE_ES_DONE:
87712b65585SGordon Ross 		ctx->ctx_negresult = spnego_negresult_success;
87812b65585SGordon Ross 		break;
87912b65585SGordon Ross 	case LSA_MTYPE_ES_CONT:
88012b65585SGordon Ross 		ctx->ctx_negresult = spnego_negresult_incomplete;
88112b65585SGordon Ross 		break;
88212b65585SGordon Ross 	default:
88312b65585SGordon Ross 		return (-1);
88412b65585SGordon Ross 	}
88512b65585SGordon Ross 
88612b65585SGordon Ross 	rc = spnegoCreateNegTokenTarg(
88712b65585SGordon Ross 	    oid,
88812b65585SGordon Ross 	    ctx->ctx_negresult,
88912b65585SGordon Ross 	    ctx->ctx_obodybuf, /* may be NULL */
89012b65585SGordon Ross 	    ctx->ctx_obodylen,
89112b65585SGordon Ross 	    NULL, 0,
89212b65585SGordon Ross 	    &ctx->ctx_otoken);
893cc86afeeSGordon Ross 	if (rc != 0)
894cc86afeeSGordon Ross 		return (NT_STATUS_INTERNAL_ERROR);
895cc86afeeSGordon Ross 
896cc86afeeSGordon Ross 	/*
897cc86afeeSGordon Ross 	 * Convert the SPNEGO token into binary form,
898cc86afeeSGordon Ross 	 * writing it to the output buffer.
899cc86afeeSGordon Ross 	 */
900cc86afeeSGordon Ross 	toklen = smbd_authsvc_bufsize;
901cc86afeeSGordon Ross 	rc = spnegoTokenGetBinary(ctx->ctx_otoken,
902cc86afeeSGordon Ross 	    (uchar_t *)ctx->ctx_orawbuf, &toklen);
903cc86afeeSGordon Ross 	if (rc != 0)
904cc86afeeSGordon Ross 		rc = NT_STATUS_INTERNAL_ERROR;
905cc86afeeSGordon Ross 	ctx->ctx_orawlen = (uint_t)toklen;
906cc86afeeSGordon Ross 
907cc86afeeSGordon Ross 	return (rc);
908cc86afeeSGordon Ross }
909cc86afeeSGordon Ross 
910cc86afeeSGordon Ross /*
911cc86afeeSGordon Ross  * The first NegTokenInit we receive, handled in smbd_authsvc_esfirst,
912cc86afeeSGordon Ross  * contains a list of supported mechanisms, in order from the client's
913cc86afeeSGordon Ross  * most preferred to least preferred. The token also contains a body
914cc86afeeSGordon Ross  * for the first mechanism listed, which is used immediately if the
915cc86afeeSGordon Ross  * first mechanism is mutually agreeable.  If the first mechanism is
916cc86afeeSGordon Ross  * not supported on our side, we must "propose a new mechanism" from
917cc86afeeSGordon Ross  * the list.  Our caller has selected a mech and initialized the ctx
918cc86afeeSGordon Ross  * mech functions.  Here compose a reply with an empty body and the
919cc86afeeSGordon Ross  * proposed new mechanism OID.  The token body received is for some
920cc86afeeSGordon Ross  * other mech, so we skip calling the work function with that token.
921cc86afeeSGordon Ross  *
922cc86afeeSGordon Ross  * Just send a NegTokenTarg with an empty body and the OID for the
923cc86afeeSGordon Ross  * proposed mechanism.  The next message should be the real first
924cc86afeeSGordon Ross  * token for this mechanism, handled in smbd_authsvc_exnext.
925cc86afeeSGordon Ross  */
926cc86afeeSGordon Ross static int
smbd_authsvc_newmech(authsvc_context_t * ctx)927cc86afeeSGordon Ross smbd_authsvc_newmech(authsvc_context_t *ctx)
928cc86afeeSGordon Ross {
929cc86afeeSGordon Ross 	ulong_t toklen;
930cc86afeeSGordon Ross 	int rc;
931cc86afeeSGordon Ross 
932cc86afeeSGordon Ross 	/*
933cc86afeeSGordon Ross 	 * Don't call mh_work here.
934cc86afeeSGordon Ross 	 * Just tell the clint the selected mech.
935cc86afeeSGordon Ross 	 */
936cc86afeeSGordon Ross 	ctx->ctx_ibodylen = 0;
937cc86afeeSGordon Ross 	ctx->ctx_orawtype = LSA_MTYPE_ES_CONT;
938cc86afeeSGordon Ross 	ctx->ctx_obodylen = 0;
939cc86afeeSGordon Ross 	ctx->ctx_negresult = spnego_negresult_request_mic;
940cc86afeeSGordon Ross 
941cc86afeeSGordon Ross 	rc = spnegoCreateNegTokenTarg(
942cc86afeeSGordon Ross 	    ctx->ctx_mech_oid,
943cc86afeeSGordon Ross 	    ctx->ctx_negresult,
944cc86afeeSGordon Ross 	    NULL, 0,
945cc86afeeSGordon Ross 	    NULL, 0,
946cc86afeeSGordon Ross 	    &ctx->ctx_otoken);
947cc86afeeSGordon Ross 	if (rc != 0)
948cc86afeeSGordon Ross 		return (NT_STATUS_INTERNAL_ERROR);
94912b65585SGordon Ross 
95012b65585SGordon Ross 	/*
95112b65585SGordon Ross 	 * Convert the SPNEGO token into binary form,
95212b65585SGordon Ross 	 * writing it to the output buffer.
95312b65585SGordon Ross 	 */
95412b65585SGordon Ross 	toklen = smbd_authsvc_bufsize;
95512b65585SGordon Ross 	rc = spnegoTokenGetBinary(ctx->ctx_otoken,
95612b65585SGordon Ross 	    (uchar_t *)ctx->ctx_orawbuf, &toklen);
95712b65585SGordon Ross 	if (rc)
95812b65585SGordon Ross 		rc = NT_STATUS_INTERNAL_ERROR;
95912b65585SGordon Ross 	ctx->ctx_orawlen = (uint_t)toklen;
96012b65585SGordon Ross 
96112b65585SGordon Ross 	return (rc);
96212b65585SGordon Ross }
96312b65585SGordon Ross 
96412b65585SGordon Ross /*
96512b65585SGordon Ross  * Wrapper for "Raw NTLMSSP", which is exactly like the
96612b65585SGordon Ross  * normal (SPNEGO-wrapped) NTLMSSP but without SPNEGO.
96712b65585SGordon Ross  * Setup back-end handler for: special_mech_raw_NTLMSSP
96812b65585SGordon Ross  * Compare with smbd_authsvc_esfirst().
96912b65585SGordon Ross  */
97012b65585SGordon Ross static int
smbd_raw_ntlmssp_esfirst(authsvc_context_t * ctx)97112b65585SGordon Ross smbd_raw_ntlmssp_esfirst(authsvc_context_t *ctx)
97212b65585SGordon Ross {
97312b65585SGordon Ross 	const spnego_mech_handler_t *mh;
97412b65585SGordon Ross 	int rc;
97512b65585SGordon Ross 
97612b65585SGordon Ross 	mh = &smbd_auth_mech_raw_ntlmssp;
97712b65585SGordon Ross 	rc = mh->mh_init(ctx);
97812b65585SGordon Ross 	if (rc != 0)
97912b65585SGordon Ross 		return (rc);
98012b65585SGordon Ross 
98112b65585SGordon Ross 	ctx->ctx_mech_oid = mh->mh_oid;
98212b65585SGordon Ross 	ctx->ctx_mh_work = mh->mh_work;
98312b65585SGordon Ross 	ctx->ctx_mh_fini = mh->mh_fini;
98412b65585SGordon Ross 
98512b65585SGordon Ross 	rc = smbd_raw_ntlmssp_esnext(ctx);
98612b65585SGordon Ross 
98712b65585SGordon Ross 	return (rc);
98812b65585SGordon Ross }
98912b65585SGordon Ross 
99012b65585SGordon Ross 
99112b65585SGordon Ross /*
99212b65585SGordon Ross  * Wrapper for "Raw NTLMSSP", which is exactly like the
99312b65585SGordon Ross  * normal (SPNEGO-wrapped) NTLMSSP but without SPNEGO.
99412b65585SGordon Ross  * Just copy "raw" to "body", and vice versa.
99512b65585SGordon Ross  * Compare with smbd_authsvc_esnext, smbd_authsvc_escmn
99612b65585SGordon Ross  */
99712b65585SGordon Ross static int
smbd_raw_ntlmssp_esnext(authsvc_context_t * ctx)99812b65585SGordon Ross smbd_raw_ntlmssp_esnext(authsvc_context_t *ctx)
99912b65585SGordon Ross {
100012b65585SGordon Ross 	int rc;
100112b65585SGordon Ross 
100212b65585SGordon Ross 	ctx->ctx_ibodylen = ctx->ctx_irawlen;
100312b65585SGordon Ross 	(void) memcpy(ctx->ctx_ibodybuf,
100412b65585SGordon Ross 	    ctx->ctx_irawbuf, ctx->ctx_irawlen);
100512b65585SGordon Ross 
100612b65585SGordon Ross 	rc = ctx->ctx_mh_work(ctx);
100712b65585SGordon Ross 
100812b65585SGordon Ross 	ctx->ctx_orawlen = ctx->ctx_obodylen;
100912b65585SGordon Ross 	(void) memcpy(ctx->ctx_orawbuf,
101012b65585SGordon Ross 	    ctx->ctx_obodybuf, ctx->ctx_obodylen);
101112b65585SGordon Ross 
101212b65585SGordon Ross 	return (rc);
101312b65585SGordon Ross }
101412b65585SGordon Ross 
101512b65585SGordon Ross 
101612b65585SGordon Ross /*
101712b65585SGordon Ross  * After a successful authentication, request the access token.
101812b65585SGordon Ross  */
101912b65585SGordon Ross static int
smbd_authsvc_gettoken(authsvc_context_t * ctx)102012b65585SGordon Ross smbd_authsvc_gettoken(authsvc_context_t *ctx)
102112b65585SGordon Ross {
102212b65585SGordon Ross 	XDR		xdrs;
102312b65585SGordon Ross 	smb_token_t	*token = NULL;
102412b65585SGordon Ross 	int		rc = 0;
102512b65585SGordon Ross 	int		len;
102612b65585SGordon Ross 
102712b65585SGordon Ross 	if ((token = ctx->ctx_token) == NULL)
102812b65585SGordon Ross 		return (NT_STATUS_ACCESS_DENIED);
102912b65585SGordon Ross 
103012b65585SGordon Ross 	/*
103112b65585SGordon Ross 	 * Encode the token response
103212b65585SGordon Ross 	 */
103312b65585SGordon Ross 	len = xdr_sizeof(smb_token_xdr, token);
103412b65585SGordon Ross 	if (len > ctx->ctx_orawlen) {
103512b65585SGordon Ross 		if ((ctx->ctx_orawbuf = realloc(ctx->ctx_orawbuf, len)) ==
103612b65585SGordon Ross 		    NULL) {
10376926de2eSGordon Ross 			return (NT_STATUS_NO_MEMORY);
103812b65585SGordon Ross 		}
103912b65585SGordon Ross 	}
104012b65585SGordon Ross 
104112b65585SGordon Ross 	ctx->ctx_orawtype = LSA_MTYPE_TOKEN;
104212b65585SGordon Ross 	ctx->ctx_orawlen = len;
104312b65585SGordon Ross 	xdrmem_create(&xdrs, ctx->ctx_orawbuf, len, XDR_ENCODE);
104412b65585SGordon Ross 	if (!smb_token_xdr(&xdrs, token))
104512b65585SGordon Ross 		rc = NT_STATUS_INTERNAL_ERROR;
104612b65585SGordon Ross 	xdr_destroy(&xdrs);
104712b65585SGordon Ross 
104812b65585SGordon Ross 	return (rc);
104912b65585SGordon Ross }
105012b65585SGordon Ross 
105112b65585SGordon Ross /*
105212b65585SGordon Ross  * Initialization time code to figure out what mechanisms we support.
105312b65585SGordon Ross  * Careful with this table; the code below knows its format and may
1054a44e7c2cSGordon Ross  * skip the fist two entries to omit Kerberos.
105512b65585SGordon Ross  */
105612b65585SGordon Ross static SPNEGO_MECH_OID MechTypeList[] = {
105712b65585SGordon Ross 	spnego_mech_oid_Kerberos_V5,
105812b65585SGordon Ross 	spnego_mech_oid_Kerberos_V5_Legacy,
105912b65585SGordon Ross #define	MECH_OID_IDX_NTLMSSP	2
106012b65585SGordon Ross 	spnego_mech_oid_NTLMSSP,
106112b65585SGordon Ross };
106212b65585SGordon Ross static int MechTypeCnt = sizeof (MechTypeList) /
106312b65585SGordon Ross 	sizeof (MechTypeList[0]);
106412b65585SGordon Ross 
106512b65585SGordon Ross /* This string is just like Windows. */
106612b65585SGordon Ross static char IgnoreSPN[] = "not_defined_in_RFC4178@please_ignore";
106712b65585SGordon Ross 
106812b65585SGordon Ross /*
106912b65585SGordon Ross  * Build the SPNEGO "hint" token based on the
107012b65585SGordon Ross  * configured authentication mechanisms.
107112b65585SGordon Ross  * (NTLMSSP, and maybe Kerberos)
107212b65585SGordon Ross  */
107312b65585SGordon Ross void
smbd_get_authconf(smb_kmod_cfg_t * kcfg)107412b65585SGordon Ross smbd_get_authconf(smb_kmod_cfg_t *kcfg)
107512b65585SGordon Ross {
107612b65585SGordon Ross 	SPNEGO_MECH_OID *mechList = MechTypeList;
107712b65585SGordon Ross 	int mechCnt = MechTypeCnt;
107812b65585SGordon Ross 	SPNEGO_TOKEN_HANDLE hSpnegoToken = NULL;
107912b65585SGordon Ross 	uchar_t *pBuf = kcfg->skc_negtok;
108012b65585SGordon Ross 	uint32_t *pBufLen = &kcfg->skc_negtok_len;
108112b65585SGordon Ross 	ulong_t tLen = sizeof (kcfg->skc_negtok);
108212b65585SGordon Ross 	int rc;
108312b65585SGordon Ross 
108412b65585SGordon Ross 	/*
108512b65585SGordon Ross 	 * In workgroup mode, skip Kerberos.
108612b65585SGordon Ross 	 */
108712b65585SGordon Ross 	if (smb_config_get_secmode() != SMB_SECMODE_DOMAIN) {
108812b65585SGordon Ross 		mechList += MECH_OID_IDX_NTLMSSP;
108912b65585SGordon Ross 		mechCnt  -= MECH_OID_IDX_NTLMSSP;
109012b65585SGordon Ross 	}
109112b65585SGordon Ross 
109212b65585SGordon Ross 	rc = spnegoCreateNegTokenHint(mechList, mechCnt,
109312b65585SGordon Ross 	    (uchar_t *)IgnoreSPN, &hSpnegoToken);
109412b65585SGordon Ross 	if (rc != SPNEGO_E_SUCCESS) {
109512b65585SGordon Ross 		syslog(LOG_DEBUG, "smb_config_get_negtok: "
109612b65585SGordon Ross 		    "spnegoCreateNegTokenHint, rc=%d", rc);
109712b65585SGordon Ross 		*pBufLen = 0;
109812b65585SGordon Ross 		return;
109912b65585SGordon Ross 	}
110012b65585SGordon Ross 	rc = spnegoTokenGetBinary(hSpnegoToken, pBuf, &tLen);
110112b65585SGordon Ross 	if (rc != SPNEGO_E_SUCCESS) {
110212b65585SGordon Ross 		syslog(LOG_DEBUG, "smb_config_get_negtok: "
110312b65585SGordon Ross 		    "spnegoTokenGetBinary, rc=%d", rc);
110412b65585SGordon Ross 		*pBufLen = 0;
110512b65585SGordon Ross 	} else {
110612b65585SGordon Ross 		*pBufLen = (uint32_t)tLen;
110712b65585SGordon Ross 	}
110812b65585SGordon Ross 	spnegoFreeData(hSpnegoToken);
110912b65585SGordon Ross }
1110