xref: /onnv-gate/usr/src/lib/libsmbfs/smb/ntlmssp.c (revision 12140:3d7db9f327ef)
110023SGordon.Ross@Sun.COM /*
210023SGordon.Ross@Sun.COM  * CDDL HEADER START
310023SGordon.Ross@Sun.COM  *
410023SGordon.Ross@Sun.COM  * The contents of this file are subject to the terms of the
510023SGordon.Ross@Sun.COM  * Common Development and Distribution License (the "License").
610023SGordon.Ross@Sun.COM  * You may not use this file except in compliance with the License.
710023SGordon.Ross@Sun.COM  *
810023SGordon.Ross@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
910023SGordon.Ross@Sun.COM  * or http://www.opensolaris.org/os/licensing.
1010023SGordon.Ross@Sun.COM  * See the License for the specific language governing permissions
1110023SGordon.Ross@Sun.COM  * and limitations under the License.
1210023SGordon.Ross@Sun.COM  *
1310023SGordon.Ross@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
1410023SGordon.Ross@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1510023SGordon.Ross@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
1610023SGordon.Ross@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
1710023SGordon.Ross@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
1810023SGordon.Ross@Sun.COM  *
1910023SGordon.Ross@Sun.COM  * CDDL HEADER END
2010023SGordon.Ross@Sun.COM  */
2110023SGordon.Ross@Sun.COM 
2210023SGordon.Ross@Sun.COM /*
23*12140SGordon.Ross@Sun.COM  * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
2410023SGordon.Ross@Sun.COM  */
2510023SGordon.Ross@Sun.COM 
2610023SGordon.Ross@Sun.COM /*
2710023SGordon.Ross@Sun.COM  * NT Lan Manager Security Support Provider (NTLMSSP)
2810023SGordon.Ross@Sun.COM  *
2910023SGordon.Ross@Sun.COM  * Based on information from the "Davenport NTLM" page:
3010023SGordon.Ross@Sun.COM  * http://davenport.sourceforge.net/ntlm.html
3110023SGordon.Ross@Sun.COM  */
3210023SGordon.Ross@Sun.COM 
3310023SGordon.Ross@Sun.COM 
3410023SGordon.Ross@Sun.COM #include <errno.h>
3510023SGordon.Ross@Sun.COM #include <stdio.h>
3610023SGordon.Ross@Sun.COM #include <stddef.h>
3710023SGordon.Ross@Sun.COM #include <stdlib.h>
3810023SGordon.Ross@Sun.COM #include <unistd.h>
3910023SGordon.Ross@Sun.COM #include <strings.h>
4010023SGordon.Ross@Sun.COM #include <netdb.h>
4110023SGordon.Ross@Sun.COM #include <libintl.h>
4210023SGordon.Ross@Sun.COM #include <xti.h>
4310023SGordon.Ross@Sun.COM #include <assert.h>
4410023SGordon.Ross@Sun.COM 
4510023SGordon.Ross@Sun.COM #include <sys/types.h>
4610023SGordon.Ross@Sun.COM #include <sys/time.h>
4710023SGordon.Ross@Sun.COM #include <sys/byteorder.h>
4810023SGordon.Ross@Sun.COM #include <sys/socket.h>
4910023SGordon.Ross@Sun.COM #include <sys/fcntl.h>
5010023SGordon.Ross@Sun.COM 
5110023SGordon.Ross@Sun.COM #include <netinet/in.h>
5210023SGordon.Ross@Sun.COM #include <netinet/tcp.h>
5310023SGordon.Ross@Sun.COM #include <arpa/inet.h>
5410023SGordon.Ross@Sun.COM 
5510023SGordon.Ross@Sun.COM #include <netsmb/smb.h>
5610023SGordon.Ross@Sun.COM #include <netsmb/smb_lib.h>
5710023SGordon.Ross@Sun.COM #include <netsmb/mchain.h>
5810023SGordon.Ross@Sun.COM 
5910023SGordon.Ross@Sun.COM #include "private.h"
6010023SGordon.Ross@Sun.COM #include "charsets.h"
6110023SGordon.Ross@Sun.COM #include "spnego.h"
6210023SGordon.Ross@Sun.COM #include "derparse.h"
6310023SGordon.Ross@Sun.COM #include "ssp.h"
6410023SGordon.Ross@Sun.COM #include "ntlm.h"
6510023SGordon.Ross@Sun.COM #include "ntlmssp.h"
6610023SGordon.Ross@Sun.COM 
6710023SGordon.Ross@Sun.COM typedef struct ntlmssp_state {
6810023SGordon.Ross@Sun.COM 	uint32_t ss_flags;
6910023SGordon.Ross@Sun.COM 	char *ss_target_name;
7010023SGordon.Ross@Sun.COM 	struct mbuf *ss_target_info;
7110023SGordon.Ross@Sun.COM } ntlmssp_state_t;
7210023SGordon.Ross@Sun.COM 
7310023SGordon.Ross@Sun.COM /*
7410023SGordon.Ross@Sun.COM  * So called "security buffer".
7510023SGordon.Ross@Sun.COM  * A lot like an RPC string.
7610023SGordon.Ross@Sun.COM  */
7710023SGordon.Ross@Sun.COM struct sec_buf {
7810023SGordon.Ross@Sun.COM 	uint16_t sb_length;
7910023SGordon.Ross@Sun.COM 	uint16_t sb_maxlen;
8010023SGordon.Ross@Sun.COM 	uint32_t sb_offset;
8110023SGordon.Ross@Sun.COM };
8210023SGordon.Ross@Sun.COM #define	ID_SZ 8
8310023SGordon.Ross@Sun.COM static const char ntlmssp_id[ID_SZ] = "NTLMSSP";
8410023SGordon.Ross@Sun.COM 
8510023SGordon.Ross@Sun.COM /*
8610023SGordon.Ross@Sun.COM  * Get a "security buffer" (header part)
8710023SGordon.Ross@Sun.COM  */
8810023SGordon.Ross@Sun.COM static int
md_get_sb_hdr(struct mbdata * mbp,struct sec_buf * sb)8911332SGordon.Ross@Sun.COM md_get_sb_hdr(struct mbdata *mbp, struct sec_buf *sb)
9010023SGordon.Ross@Sun.COM {
9110023SGordon.Ross@Sun.COM 	int err;
9210023SGordon.Ross@Sun.COM 
9311332SGordon.Ross@Sun.COM 	(void) md_get_uint16le(mbp, &sb->sb_length);
9411332SGordon.Ross@Sun.COM 	(void) md_get_uint16le(mbp, &sb->sb_maxlen);
9511332SGordon.Ross@Sun.COM 	err = md_get_uint32le(mbp, &sb->sb_offset);
9610023SGordon.Ross@Sun.COM 
9710023SGordon.Ross@Sun.COM 	return (err);
9810023SGordon.Ross@Sun.COM }
9910023SGordon.Ross@Sun.COM 
10010023SGordon.Ross@Sun.COM /*
10110023SGordon.Ross@Sun.COM  * Get a "security buffer" (data part), where
10210023SGordon.Ross@Sun.COM  * the data is delivered as an mbuf.
10310023SGordon.Ross@Sun.COM  */
10410023SGordon.Ross@Sun.COM static int
md_get_sb_data(struct mbdata * mbp,struct sec_buf * sb,struct mbuf ** mp)10511332SGordon.Ross@Sun.COM md_get_sb_data(struct mbdata *mbp, struct sec_buf *sb, struct mbuf **mp)
10610023SGordon.Ross@Sun.COM {
10710023SGordon.Ross@Sun.COM 	struct mbdata tmp_mb;
10810023SGordon.Ross@Sun.COM 	int err;
10910023SGordon.Ross@Sun.COM 
11010023SGordon.Ross@Sun.COM 	/*
11110023SGordon.Ross@Sun.COM 	 * Setup tmp_mb to point to the start of the header.
11210023SGordon.Ross@Sun.COM 	 * This is a dup ref - do NOT free it.
11310023SGordon.Ross@Sun.COM 	 */
11410023SGordon.Ross@Sun.COM 	mb_initm(&tmp_mb, mbp->mb_top);
11510023SGordon.Ross@Sun.COM 
11610023SGordon.Ross@Sun.COM 	/* Skip data up to the offset. */
11711332SGordon.Ross@Sun.COM 	err = md_get_mem(&tmp_mb, NULL, sb->sb_offset, MB_MSYSTEM);
11810023SGordon.Ross@Sun.COM 	if (err)
11910023SGordon.Ross@Sun.COM 		return (err);
12010023SGordon.Ross@Sun.COM 
12110023SGordon.Ross@Sun.COM 	/* Get the data (as an mbuf). */
12211332SGordon.Ross@Sun.COM 	err = md_get_mbuf(&tmp_mb, sb->sb_maxlen, mp);
12310023SGordon.Ross@Sun.COM 
12410023SGordon.Ross@Sun.COM 	return (err);
12510023SGordon.Ross@Sun.COM }
12610023SGordon.Ross@Sun.COM 
12710023SGordon.Ross@Sun.COM /*
12810023SGordon.Ross@Sun.COM  * Put a "security buffer" (header part)
12910023SGordon.Ross@Sun.COM  */
13010023SGordon.Ross@Sun.COM static int
mb_put_sb_hdr(struct mbdata * mbp,struct sec_buf * sb)13110023SGordon.Ross@Sun.COM mb_put_sb_hdr(struct mbdata *mbp, struct sec_buf *sb)
13210023SGordon.Ross@Sun.COM {
13310023SGordon.Ross@Sun.COM 	int err;
13410023SGordon.Ross@Sun.COM 
13510023SGordon.Ross@Sun.COM 	(void) mb_put_uint16le(mbp, sb->sb_length);
13610023SGordon.Ross@Sun.COM 	(void) mb_put_uint16le(mbp, sb->sb_maxlen);
13710023SGordon.Ross@Sun.COM 	err = mb_put_uint32le(mbp, sb->sb_offset);
13810023SGordon.Ross@Sun.COM 
13910023SGordon.Ross@Sun.COM 	return (err);
14010023SGordon.Ross@Sun.COM }
14110023SGordon.Ross@Sun.COM 
14210023SGordon.Ross@Sun.COM /*
14310023SGordon.Ross@Sun.COM  * Put a "security buffer" (data part), where
14410023SGordon.Ross@Sun.COM  * the data is an mbuf.  Note: consumes m.
14510023SGordon.Ross@Sun.COM  */
14610023SGordon.Ross@Sun.COM static int
mb_put_sb_data(struct mbdata * mbp,struct sec_buf * sb,struct mbuf * m)14710023SGordon.Ross@Sun.COM mb_put_sb_data(struct mbdata *mbp, struct sec_buf *sb, struct mbuf *m)
14810023SGordon.Ross@Sun.COM {
14910023SGordon.Ross@Sun.COM 	int cnt0, err;
15010023SGordon.Ross@Sun.COM 
15110023SGordon.Ross@Sun.COM 	sb->sb_offset = cnt0 = mbp->mb_count;
15210023SGordon.Ross@Sun.COM 	err = mb_put_mbuf(mbp, m);
15310023SGordon.Ross@Sun.COM 	sb->sb_maxlen = sb->sb_length = mbp->mb_count - cnt0;
15410023SGordon.Ross@Sun.COM 
15510023SGordon.Ross@Sun.COM 	return (err);
15610023SGordon.Ross@Sun.COM }
15710023SGordon.Ross@Sun.COM 
15810023SGordon.Ross@Sun.COM /*
15910023SGordon.Ross@Sun.COM  * Put a "security buffer" (data part), where
16010023SGordon.Ross@Sun.COM  * the data is a string (OEM or unicode).
16110023SGordon.Ross@Sun.COM  *
16210023SGordon.Ross@Sun.COM  * The string is NOT null terminated.
16310023SGordon.Ross@Sun.COM  */
16410023SGordon.Ross@Sun.COM static int
mb_put_sb_string(struct mbdata * mbp,struct sec_buf * sb,const char * s,int unicode)16510023SGordon.Ross@Sun.COM mb_put_sb_string(struct mbdata *mbp, struct sec_buf *sb,
16610023SGordon.Ross@Sun.COM 	const char *s, int unicode)
16710023SGordon.Ross@Sun.COM {
16810023SGordon.Ross@Sun.COM 	int err, trim;
16910023SGordon.Ross@Sun.COM 	struct mbdata tmp_mb;
17010023SGordon.Ross@Sun.COM 
17110023SGordon.Ross@Sun.COM 	/*
17210023SGordon.Ross@Sun.COM 	 * Put the string into a temp. mbuf,
17310023SGordon.Ross@Sun.COM 	 * then chop off the null terminator
17410023SGordon.Ross@Sun.COM 	 * before appending to caller's mbp.
17510023SGordon.Ross@Sun.COM 	 */
17611332SGordon.Ross@Sun.COM 	err = mb_init(&tmp_mb);
17710023SGordon.Ross@Sun.COM 	if (err)
17810023SGordon.Ross@Sun.COM 		return (err);
17911332SGordon.Ross@Sun.COM 	err = mb_put_string(&tmp_mb, s, unicode);
18010023SGordon.Ross@Sun.COM 	if (err)
18110023SGordon.Ross@Sun.COM 		return (err);
18210023SGordon.Ross@Sun.COM 
18310023SGordon.Ross@Sun.COM 	trim = (unicode) ? 2 : 1;
18410023SGordon.Ross@Sun.COM 	if (tmp_mb.mb_cur->m_len < trim)
18510023SGordon.Ross@Sun.COM 		return (EFAULT);
18610023SGordon.Ross@Sun.COM 	tmp_mb.mb_cur->m_len -= trim;
18710023SGordon.Ross@Sun.COM 
18810023SGordon.Ross@Sun.COM 	err = mb_put_sb_data(mbp, sb, tmp_mb.mb_top);
18910023SGordon.Ross@Sun.COM 	/*
19010023SGordon.Ross@Sun.COM 	 * Note: tmp_mb.mb_top is consumed,
19110023SGordon.Ross@Sun.COM 	 * so do NOT free it (no mb_done)
19210023SGordon.Ross@Sun.COM 	 */
19310023SGordon.Ross@Sun.COM 	return (err);
19410023SGordon.Ross@Sun.COM }
19510023SGordon.Ross@Sun.COM 
19610023SGordon.Ross@Sun.COM /*
19710023SGordon.Ross@Sun.COM  * Build a Type 1 message
19810023SGordon.Ross@Sun.COM  *
19910023SGordon.Ross@Sun.COM  * This message has a header section containing offsets to
20010023SGordon.Ross@Sun.COM  * data later in the message.  We use the common trick of
20110023SGordon.Ross@Sun.COM  * building it in two parts and then concatenatening.
20210023SGordon.Ross@Sun.COM  */
20310023SGordon.Ross@Sun.COM int
ntlmssp_put_type1(struct ssp_ctx * sp,struct mbdata * out_mb)20410023SGordon.Ross@Sun.COM ntlmssp_put_type1(struct ssp_ctx *sp, struct mbdata *out_mb)
20510023SGordon.Ross@Sun.COM {
20610023SGordon.Ross@Sun.COM 	struct type1hdr {
20710023SGordon.Ross@Sun.COM 		char h_id[ID_SZ];
20810023SGordon.Ross@Sun.COM 		uint32_t h_type;
20910023SGordon.Ross@Sun.COM 		uint32_t h_flags;
21010023SGordon.Ross@Sun.COM 		struct sec_buf h_cldom;
21110023SGordon.Ross@Sun.COM 		struct sec_buf h_wksta;
21210023SGordon.Ross@Sun.COM 	} hdr;
21310023SGordon.Ross@Sun.COM 	struct mbdata mb2;	/* 2nd part */
21410023SGordon.Ross@Sun.COM 	int err;
21510023SGordon.Ross@Sun.COM 	struct smb_ctx *ctx = sp->smb_ctx;
21610023SGordon.Ross@Sun.COM 	ntlmssp_state_t *ssp_st = sp->sp_private;
21710023SGordon.Ross@Sun.COM 	char *ucdom = NULL;
21810023SGordon.Ross@Sun.COM 	char *ucwks = NULL;
21910023SGordon.Ross@Sun.COM 
22011332SGordon.Ross@Sun.COM 	if ((err = mb_init(&mb2)) != 0)
22110023SGordon.Ross@Sun.COM 		return (err);
22210023SGordon.Ross@Sun.COM 	mb2.mb_count = sizeof (hdr);
22310023SGordon.Ross@Sun.COM 
22410023SGordon.Ross@Sun.COM 	/*
22510023SGordon.Ross@Sun.COM 	 * Initialize the negotiation flags, and
22610023SGordon.Ross@Sun.COM 	 * save what we sent.  For reference:
22710023SGordon.Ross@Sun.COM 	 * [MS-NLMP] spec. (also ntlmssp.h)
22810023SGordon.Ross@Sun.COM 	 */
22910023SGordon.Ross@Sun.COM 	ssp_st->ss_flags =
23010023SGordon.Ross@Sun.COM 	    NTLMSSP_REQUEST_TARGET |
23110023SGordon.Ross@Sun.COM 	    NTLMSSP_NEGOTIATE_NTLM |
23210023SGordon.Ross@Sun.COM 	    NTLMSSP_NEGOTIATE_TARGET_INFO |
23310023SGordon.Ross@Sun.COM 	    NTLMSSP_NEGOTIATE_128 |
23410023SGordon.Ross@Sun.COM 	    NTLMSSP_NEGOTIATE_56;
23510023SGordon.Ross@Sun.COM 
23610023SGordon.Ross@Sun.COM 	if (ctx->ct_hflags2 & SMB_FLAGS2_UNICODE)
23710023SGordon.Ross@Sun.COM 		ssp_st->ss_flags |= NTLMSSP_NEGOTIATE_UNICODE;
23810023SGordon.Ross@Sun.COM 	else
23910023SGordon.Ross@Sun.COM 		ssp_st->ss_flags |= NTLMSSP_NEGOTIATE_OEM;
24010023SGordon.Ross@Sun.COM 
24110023SGordon.Ross@Sun.COM 	if (ctx->ct_vcflags & SMBV_WILL_SIGN) {
24210023SGordon.Ross@Sun.COM 		ssp_st->ss_flags |= NTLMSSP_NEGOTIATE_ALWAYS_SIGN;
24310023SGordon.Ross@Sun.COM 		ctx->ct_hflags2 |= SMB_FLAGS2_SECURITY_SIGNATURE;
24410023SGordon.Ross@Sun.COM 	}
24510023SGordon.Ross@Sun.COM 
24610023SGordon.Ross@Sun.COM 	bcopy(ntlmssp_id, &hdr.h_id, ID_SZ);
24710023SGordon.Ross@Sun.COM 	hdr.h_type = 1; /* Type 1 */
24810023SGordon.Ross@Sun.COM 	hdr.h_flags = ssp_st->ss_flags;
24910023SGordon.Ross@Sun.COM 
25010023SGordon.Ross@Sun.COM 	/*
25110023SGordon.Ross@Sun.COM 	 * Put the client domain, client name strings.
25210023SGordon.Ross@Sun.COM 	 * These are always in OEM format, upper-case.
25310023SGordon.Ross@Sun.COM 	 */
25410023SGordon.Ross@Sun.COM 	ucdom  = utf8_str_toupper(ctx->ct_domain);
25510023SGordon.Ross@Sun.COM 	ucwks  = utf8_str_toupper(ctx->ct_locname);
25610023SGordon.Ross@Sun.COM 	if (ucdom == NULL || ucwks == NULL) {
25710023SGordon.Ross@Sun.COM 		err = ENOMEM;
25810023SGordon.Ross@Sun.COM 		goto out;
25910023SGordon.Ross@Sun.COM 	}
26010023SGordon.Ross@Sun.COM 	err = mb_put_sb_string(&mb2, &hdr.h_cldom, ucdom, 0);
26110023SGordon.Ross@Sun.COM 	if (err)
26210023SGordon.Ross@Sun.COM 		goto out;
26310023SGordon.Ross@Sun.COM 	err = mb_put_sb_string(&mb2, &hdr.h_wksta, ucwks, 0);
26410023SGordon.Ross@Sun.COM 	if (err)
26510023SGordon.Ross@Sun.COM 		goto out;
26610023SGordon.Ross@Sun.COM 
26710023SGordon.Ross@Sun.COM 	/*
26810023SGordon.Ross@Sun.COM 	 * Marshal the header (in LE order)
26910023SGordon.Ross@Sun.COM 	 * then concatenate the 2nd part.
27010023SGordon.Ross@Sun.COM 	 */
27111332SGordon.Ross@Sun.COM 	(void) mb_put_mem(out_mb, &hdr.h_id, ID_SZ, MB_MSYSTEM);
27210023SGordon.Ross@Sun.COM 	(void) mb_put_uint32le(out_mb, hdr.h_type);
27310023SGordon.Ross@Sun.COM 	(void) mb_put_uint32le(out_mb, hdr.h_flags);
27410023SGordon.Ross@Sun.COM 	(void) mb_put_sb_hdr(out_mb, &hdr.h_cldom);
27510023SGordon.Ross@Sun.COM 	(void) mb_put_sb_hdr(out_mb, &hdr.h_wksta);
27610023SGordon.Ross@Sun.COM 
27710023SGordon.Ross@Sun.COM 	err = mb_put_mbuf(out_mb, mb2.mb_top);
27810023SGordon.Ross@Sun.COM 
27910023SGordon.Ross@Sun.COM out:
28010023SGordon.Ross@Sun.COM 	free(ucdom);
28110023SGordon.Ross@Sun.COM 	free(ucwks);
28210023SGordon.Ross@Sun.COM 
28310023SGordon.Ross@Sun.COM 	return (err);
28410023SGordon.Ross@Sun.COM }
28510023SGordon.Ross@Sun.COM 
28610023SGordon.Ross@Sun.COM /*
28710023SGordon.Ross@Sun.COM  * Parse a Type 2 message
28810023SGordon.Ross@Sun.COM  */
28910023SGordon.Ross@Sun.COM int
ntlmssp_get_type2(struct ssp_ctx * sp,struct mbdata * in_mb)29010023SGordon.Ross@Sun.COM ntlmssp_get_type2(struct ssp_ctx *sp, struct mbdata *in_mb)
29110023SGordon.Ross@Sun.COM {
29210023SGordon.Ross@Sun.COM 	struct type2hdr {
29310023SGordon.Ross@Sun.COM 		char h_id[ID_SZ];
29410023SGordon.Ross@Sun.COM 		uint32_t h_type;
29510023SGordon.Ross@Sun.COM 		struct sec_buf h_target_name;
29610023SGordon.Ross@Sun.COM 		uint32_t h_flags;
29710023SGordon.Ross@Sun.COM 		uint8_t h_challenge[8];
29810023SGordon.Ross@Sun.COM 		uint32_t h_context[2];		/* optional */
29910023SGordon.Ross@Sun.COM 		struct sec_buf h_target_info;	/* optional */
30010023SGordon.Ross@Sun.COM 	} hdr;
30110023SGordon.Ross@Sun.COM 	struct mbdata top_mb, tmp_mb;
30210023SGordon.Ross@Sun.COM 	struct mbuf *m;
30310023SGordon.Ross@Sun.COM 	int err, uc;
30410023SGordon.Ross@Sun.COM 	int min_hdr_sz = offsetof(struct type2hdr, h_context);
30510023SGordon.Ross@Sun.COM 	struct smb_ctx *ctx = sp->smb_ctx;
30610023SGordon.Ross@Sun.COM 	ntlmssp_state_t *ssp_st = sp->sp_private;
30710023SGordon.Ross@Sun.COM 	char *buf = NULL;
30810023SGordon.Ross@Sun.COM 
30910023SGordon.Ross@Sun.COM 	if (m_totlen(in_mb->mb_top) < min_hdr_sz) {
31010023SGordon.Ross@Sun.COM 		err = EBADRPC;
31110023SGordon.Ross@Sun.COM 		goto out;
31210023SGordon.Ross@Sun.COM 	}
31310023SGordon.Ross@Sun.COM 
31410023SGordon.Ross@Sun.COM 	/*
31510023SGordon.Ross@Sun.COM 	 * Save the mbdata pointers before we consume anything.
31610023SGordon.Ross@Sun.COM 	 * Careful to NOT free this (would be dup. free)
31710023SGordon.Ross@Sun.COM 	 * We use this below to find data based on offsets
31810023SGordon.Ross@Sun.COM 	 * from the start of the header.
31910023SGordon.Ross@Sun.COM 	 */
32010023SGordon.Ross@Sun.COM 	top_mb = *in_mb;
32110023SGordon.Ross@Sun.COM 
32210023SGordon.Ross@Sun.COM 	/* Parse the fixed size header stuff. */
32310023SGordon.Ross@Sun.COM 	bzero(&hdr, sizeof (hdr));
32411332SGordon.Ross@Sun.COM 	(void) md_get_mem(in_mb, &hdr.h_id, ID_SZ, MB_MSYSTEM);
32511332SGordon.Ross@Sun.COM 	(void) md_get_uint32le(in_mb, &hdr.h_type);
32610023SGordon.Ross@Sun.COM 	if (hdr.h_type != 2) {
32710023SGordon.Ross@Sun.COM 		err = EPROTO;
32810023SGordon.Ross@Sun.COM 		goto out;
32910023SGordon.Ross@Sun.COM 	}
33011332SGordon.Ross@Sun.COM 	(void) md_get_sb_hdr(in_mb, &hdr.h_target_name);
33111332SGordon.Ross@Sun.COM 	(void) md_get_uint32le(in_mb, &hdr.h_flags);
33211332SGordon.Ross@Sun.COM 	(void) md_get_mem(in_mb, &hdr.h_challenge, NTLM_CHAL_SZ, MB_MSYSTEM);
33310023SGordon.Ross@Sun.COM 
33410023SGordon.Ross@Sun.COM 	/*
33510023SGordon.Ross@Sun.COM 	 * Save flags, challenge for later.
33610023SGordon.Ross@Sun.COM 	 */
33710023SGordon.Ross@Sun.COM 	ssp_st->ss_flags = hdr.h_flags;
33810023SGordon.Ross@Sun.COM 	uc = hdr.h_flags & NTLMSSP_NEGOTIATE_UNICODE;
33910023SGordon.Ross@Sun.COM 	bcopy(&hdr.h_challenge, ctx->ct_ntlm_chal, NTLM_CHAL_SZ);
34010023SGordon.Ross@Sun.COM 
34110023SGordon.Ross@Sun.COM 	/*
34210023SGordon.Ross@Sun.COM 	 * Now find out if the optional parts are there.
34310023SGordon.Ross@Sun.COM 	 */
34410023SGordon.Ross@Sun.COM 	if ((m_totlen(top_mb.mb_top) > sizeof (hdr)) &&
34510023SGordon.Ross@Sun.COM 	    (hdr.h_target_name.sb_offset >= sizeof (hdr))) {
34611332SGordon.Ross@Sun.COM 		(void) md_get_uint32le(in_mb, &hdr.h_context[0]);
34711332SGordon.Ross@Sun.COM 		(void) md_get_uint32le(in_mb, &hdr.h_context[1]);
34811332SGordon.Ross@Sun.COM 		(void) md_get_sb_hdr(in_mb, &hdr.h_target_info);
34910023SGordon.Ross@Sun.COM 	}
35010023SGordon.Ross@Sun.COM 
35110023SGordon.Ross@Sun.COM 	/*
35210023SGordon.Ross@Sun.COM 	 * Get the target name string.  First get a copy of
35310023SGordon.Ross@Sun.COM 	 * the data from the offset/length indicated in the
35410023SGordon.Ross@Sun.COM 	 * security buffer header; then parse the string.
35510023SGordon.Ross@Sun.COM 	 */
35611332SGordon.Ross@Sun.COM 	err = md_get_sb_data(&top_mb, &hdr.h_target_name, &m);
35710023SGordon.Ross@Sun.COM 	if (err)
35810023SGordon.Ross@Sun.COM 		goto out;
35910023SGordon.Ross@Sun.COM 	mb_initm(&tmp_mb, m);
36011332SGordon.Ross@Sun.COM 	err = md_get_string(&tmp_mb, &ssp_st->ss_target_name, uc);
36110023SGordon.Ross@Sun.COM 	mb_done(&tmp_mb);
36210023SGordon.Ross@Sun.COM 
36310023SGordon.Ross@Sun.COM 	/*
36410023SGordon.Ross@Sun.COM 	 * Get the target info blob, if present.
36510023SGordon.Ross@Sun.COM 	 */
36610023SGordon.Ross@Sun.COM 	if (hdr.h_target_info.sb_offset >= sizeof (hdr)) {
36711332SGordon.Ross@Sun.COM 		err = md_get_sb_data(&top_mb, &hdr.h_target_info,
36810023SGordon.Ross@Sun.COM 		    &ssp_st->ss_target_info);
36910023SGordon.Ross@Sun.COM 	}
37010023SGordon.Ross@Sun.COM 
37110023SGordon.Ross@Sun.COM out:
37210023SGordon.Ross@Sun.COM 	if (buf != NULL)
37310023SGordon.Ross@Sun.COM 		free(buf);
37410023SGordon.Ross@Sun.COM 
37510023SGordon.Ross@Sun.COM 	return (err);
37610023SGordon.Ross@Sun.COM }
37710023SGordon.Ross@Sun.COM 
37810023SGordon.Ross@Sun.COM /*
37910023SGordon.Ross@Sun.COM  * Build a Type 3 message
38010023SGordon.Ross@Sun.COM  *
38110023SGordon.Ross@Sun.COM  * This message has a header section containing offsets to
38210023SGordon.Ross@Sun.COM  * data later in the message.  We use the common trick of
38310023SGordon.Ross@Sun.COM  * building it in two parts and then concatenatening.
38410023SGordon.Ross@Sun.COM  */
38510023SGordon.Ross@Sun.COM int
ntlmssp_put_type3(struct ssp_ctx * sp,struct mbdata * out_mb)38610023SGordon.Ross@Sun.COM ntlmssp_put_type3(struct ssp_ctx *sp, struct mbdata *out_mb)
38710023SGordon.Ross@Sun.COM {
38810023SGordon.Ross@Sun.COM 	struct type3hdr {
38910023SGordon.Ross@Sun.COM 		char h_id[ID_SZ];
39010023SGordon.Ross@Sun.COM 		uint32_t h_type;
39110023SGordon.Ross@Sun.COM 		struct sec_buf h_lm_resp;
39210023SGordon.Ross@Sun.COM 		struct sec_buf h_nt_resp;
39310023SGordon.Ross@Sun.COM 		struct sec_buf h_domain;
39410023SGordon.Ross@Sun.COM 		struct sec_buf h_user;
39510023SGordon.Ross@Sun.COM 		struct sec_buf h_wksta;
396*12140SGordon.Ross@Sun.COM 		struct sec_buf h_ssn_key;
397*12140SGordon.Ross@Sun.COM 		uint32_t h_flags;
39810023SGordon.Ross@Sun.COM 	} hdr;
399*12140SGordon.Ross@Sun.COM 	struct mbdata lm_mbc;	/* LM response */
400*12140SGordon.Ross@Sun.COM 	struct mbdata nt_mbc;	/* NT response */
401*12140SGordon.Ross@Sun.COM 	struct mbdata ti_mbc;	/* target info */
402*12140SGordon.Ross@Sun.COM 	struct mbdata mb2;	/* payload */
40310023SGordon.Ross@Sun.COM 	int err, uc;
40410023SGordon.Ross@Sun.COM 	struct smb_ctx *ctx = sp->smb_ctx;
40510023SGordon.Ross@Sun.COM 	ntlmssp_state_t *ssp_st = sp->sp_private;
40610023SGordon.Ross@Sun.COM 
407*12140SGordon.Ross@Sun.COM 	bzero(&hdr, sizeof (hdr));
40810023SGordon.Ross@Sun.COM 	bzero(&lm_mbc, sizeof (lm_mbc));
40910023SGordon.Ross@Sun.COM 	bzero(&nt_mbc, sizeof (nt_mbc));
41010023SGordon.Ross@Sun.COM 	bzero(&ti_mbc, sizeof (ti_mbc));
41110023SGordon.Ross@Sun.COM 	bzero(&mb2, sizeof (mb2));
41210023SGordon.Ross@Sun.COM 
41310023SGordon.Ross@Sun.COM 	/*
414*12140SGordon.Ross@Sun.COM 	 * Fill in the NTLMSSP header, etc.
41510023SGordon.Ross@Sun.COM 	 */
41611332SGordon.Ross@Sun.COM 	if ((err = mb_init(&mb2)) != 0)
41710023SGordon.Ross@Sun.COM 		goto out;
41810023SGordon.Ross@Sun.COM 	mb2.mb_count = sizeof (hdr);
41910023SGordon.Ross@Sun.COM 	uc = ssp_st->ss_flags & NTLMSSP_NEGOTIATE_UNICODE;
42010023SGordon.Ross@Sun.COM 
42110023SGordon.Ross@Sun.COM 	bcopy(ntlmssp_id, &hdr.h_id, ID_SZ);
42210023SGordon.Ross@Sun.COM 	hdr.h_type = 3; /* Type 3 */
423*12140SGordon.Ross@Sun.COM 	hdr.h_flags = ssp_st->ss_flags;
42410023SGordon.Ross@Sun.COM 
42510023SGordon.Ross@Sun.COM 	/*
42610023SGordon.Ross@Sun.COM 	 * Put the LMv2,NTLMv2 responses, or
42710023SGordon.Ross@Sun.COM 	 * possibly LM, NTLM (v1) responses.
42810023SGordon.Ross@Sun.COM 	 */
42910023SGordon.Ross@Sun.COM 	if (ctx->ct_authflags & SMB_AT_NTLM2) {
43010023SGordon.Ross@Sun.COM 		/* Build the NTLMv2 "target info" blob. */
43110023SGordon.Ross@Sun.COM 		err = ntlm_build_target_info(ctx,
43210023SGordon.Ross@Sun.COM 		    ssp_st->ss_target_info, &ti_mbc);
43310023SGordon.Ross@Sun.COM 		if (err)
43410023SGordon.Ross@Sun.COM 			goto out;
43510023SGordon.Ross@Sun.COM 		err = ntlm_put_v2_responses(ctx, &ti_mbc,
43610023SGordon.Ross@Sun.COM 		    &lm_mbc, &nt_mbc);
43710023SGordon.Ross@Sun.COM 	} else {
43810023SGordon.Ross@Sun.COM 		err = ntlm_put_v1_responses(ctx,
43910023SGordon.Ross@Sun.COM 		    &lm_mbc, &nt_mbc);
44010023SGordon.Ross@Sun.COM 	}
44110023SGordon.Ross@Sun.COM 	if (err)
44210023SGordon.Ross@Sun.COM 		goto out;
44310023SGordon.Ross@Sun.COM 
44410023SGordon.Ross@Sun.COM 	err = mb_put_sb_data(&mb2, &hdr.h_lm_resp, lm_mbc.mb_top);
44510023SGordon.Ross@Sun.COM 	lm_mbc.mb_top = NULL; /* consumed */
44610023SGordon.Ross@Sun.COM 	if (err)
44710023SGordon.Ross@Sun.COM 		goto out;
44810023SGordon.Ross@Sun.COM 	err = mb_put_sb_data(&mb2, &hdr.h_nt_resp, nt_mbc.mb_top);
44910023SGordon.Ross@Sun.COM 	nt_mbc.mb_top = NULL; /* consumed */
45010023SGordon.Ross@Sun.COM 	if (err)
45110023SGordon.Ross@Sun.COM 		goto out;
45210023SGordon.Ross@Sun.COM 
45310023SGordon.Ross@Sun.COM 	/*
45410023SGordon.Ross@Sun.COM 	 * Put the "target" (domain), user, workstation
45510023SGordon.Ross@Sun.COM 	 */
456*12140SGordon.Ross@Sun.COM 	err = mb_put_sb_string(&mb2, &hdr.h_domain, ctx->ct_domain, uc);
457*12140SGordon.Ross@Sun.COM 	if (err)
458*12140SGordon.Ross@Sun.COM 		goto out;
459*12140SGordon.Ross@Sun.COM 	err = mb_put_sb_string(&mb2, &hdr.h_user, ctx->ct_user, uc);
460*12140SGordon.Ross@Sun.COM 	if (err)
461*12140SGordon.Ross@Sun.COM 		goto out;
462*12140SGordon.Ross@Sun.COM 	err = mb_put_sb_string(&mb2, &hdr.h_wksta, ctx->ct_locname, uc);
46310023SGordon.Ross@Sun.COM 	if (err)
46410023SGordon.Ross@Sun.COM 		goto out;
465*12140SGordon.Ross@Sun.COM 
466*12140SGordon.Ross@Sun.COM 	/*
467*12140SGordon.Ross@Sun.COM 	 * Put the "Random Session Key".  We don't set
468*12140SGordon.Ross@Sun.COM 	 * NTLMSSP_NEGOTIATE_KEY_EXCH, so it's empty.
469*12140SGordon.Ross@Sun.COM 	 * (In-line mb_put_sb_data here.)
470*12140SGordon.Ross@Sun.COM 	 */
471*12140SGordon.Ross@Sun.COM 	hdr.h_ssn_key.sb_maxlen = hdr.h_ssn_key.sb_length = 0;
472*12140SGordon.Ross@Sun.COM 	hdr.h_ssn_key.sb_offset = mb2.mb_count;
47310023SGordon.Ross@Sun.COM 
47410023SGordon.Ross@Sun.COM 	/*
47510023SGordon.Ross@Sun.COM 	 * Marshal the header (in LE order)
47610023SGordon.Ross@Sun.COM 	 * then concatenate the 2nd part.
47710023SGordon.Ross@Sun.COM 	 */
47811332SGordon.Ross@Sun.COM 	(void) mb_put_mem(out_mb, &hdr.h_id, ID_SZ, MB_MSYSTEM);
47910023SGordon.Ross@Sun.COM 	(void) mb_put_uint32le(out_mb, hdr.h_type);
48010023SGordon.Ross@Sun.COM 
48110023SGordon.Ross@Sun.COM 	(void) mb_put_sb_hdr(out_mb, &hdr.h_lm_resp);
48210023SGordon.Ross@Sun.COM 	(void) mb_put_sb_hdr(out_mb, &hdr.h_nt_resp);
48310023SGordon.Ross@Sun.COM 
48410023SGordon.Ross@Sun.COM 	(void) mb_put_sb_hdr(out_mb, &hdr.h_domain);
48510023SGordon.Ross@Sun.COM 	(void) mb_put_sb_hdr(out_mb, &hdr.h_user);
48610023SGordon.Ross@Sun.COM 	(void) mb_put_sb_hdr(out_mb, &hdr.h_wksta);
48710023SGordon.Ross@Sun.COM 
488*12140SGordon.Ross@Sun.COM 	(void) mb_put_sb_hdr(out_mb, &hdr.h_ssn_key);
489*12140SGordon.Ross@Sun.COM 	(void) mb_put_uint32le(out_mb, hdr.h_flags);
490*12140SGordon.Ross@Sun.COM 
49110023SGordon.Ross@Sun.COM 	err = mb_put_mbuf(out_mb, mb2.mb_top);
49210023SGordon.Ross@Sun.COM 	mb2.mb_top = NULL; /* consumed */
49310023SGordon.Ross@Sun.COM 
49410023SGordon.Ross@Sun.COM out:
49510023SGordon.Ross@Sun.COM 	mb_done(&mb2);
49610023SGordon.Ross@Sun.COM 	mb_done(&lm_mbc);
49710023SGordon.Ross@Sun.COM 	mb_done(&nt_mbc);
498*12140SGordon.Ross@Sun.COM 	mb_done(&ti_mbc);
49910023SGordon.Ross@Sun.COM 
50010023SGordon.Ross@Sun.COM 	return (err);
50110023SGordon.Ross@Sun.COM }
50210023SGordon.Ross@Sun.COM 
50310023SGordon.Ross@Sun.COM /*
50410023SGordon.Ross@Sun.COM  * ntlmssp_final
50510023SGordon.Ross@Sun.COM  *
50610023SGordon.Ross@Sun.COM  * Called after successful authentication.
50710023SGordon.Ross@Sun.COM  * Setup the MAC key for signing.
50810023SGordon.Ross@Sun.COM  */
50910023SGordon.Ross@Sun.COM int
ntlmssp_final(struct ssp_ctx * sp)51010023SGordon.Ross@Sun.COM ntlmssp_final(struct ssp_ctx *sp)
51110023SGordon.Ross@Sun.COM {
51210023SGordon.Ross@Sun.COM 	struct smb_ctx *ctx = sp->smb_ctx;
51310023SGordon.Ross@Sun.COM 	int err = 0;
51410023SGordon.Ross@Sun.COM 
51510023SGordon.Ross@Sun.COM 	/*
51610023SGordon.Ross@Sun.COM 	 * MAC_key is just the session key, but
51710023SGordon.Ross@Sun.COM 	 * Only on the first successful auth.
51810023SGordon.Ross@Sun.COM 	 */
51910023SGordon.Ross@Sun.COM 	if ((ctx->ct_hflags2 & SMB_FLAGS2_SECURITY_SIGNATURE) &&
52010023SGordon.Ross@Sun.COM 	    (ctx->ct_mackey == NULL)) {
52110023SGordon.Ross@Sun.COM 		ctx->ct_mackeylen = NTLM_HASH_SZ;
52210023SGordon.Ross@Sun.COM 		ctx->ct_mackey = malloc(ctx->ct_mackeylen);
52310023SGordon.Ross@Sun.COM 		if (ctx->ct_mackey == NULL) {
52410023SGordon.Ross@Sun.COM 			ctx->ct_mackeylen = 0;
52510023SGordon.Ross@Sun.COM 			err = ENOMEM;
52610023SGordon.Ross@Sun.COM 			goto out;
52710023SGordon.Ross@Sun.COM 		}
52810023SGordon.Ross@Sun.COM 		memcpy(ctx->ct_mackey, ctx->ct_ssn_key, NTLM_HASH_SZ);
52910023SGordon.Ross@Sun.COM 		/*
53010023SGordon.Ross@Sun.COM 		 * Apparently, the server used seq. no. zero
53110023SGordon.Ross@Sun.COM 		 * for our previous message, so next is two.
53210023SGordon.Ross@Sun.COM 		 */
53310023SGordon.Ross@Sun.COM 		ctx->ct_mac_seqno = 2;
53410023SGordon.Ross@Sun.COM 	}
53510023SGordon.Ross@Sun.COM 
53610023SGordon.Ross@Sun.COM out:
53710023SGordon.Ross@Sun.COM 	return (err);
53810023SGordon.Ross@Sun.COM }
53910023SGordon.Ross@Sun.COM 
54010023SGordon.Ross@Sun.COM /*
54110023SGordon.Ross@Sun.COM  * ntlmssp_next_token
54210023SGordon.Ross@Sun.COM  *
54310023SGordon.Ross@Sun.COM  * See ssp.c: ssp_ctx_next_token
54410023SGordon.Ross@Sun.COM  */
54510023SGordon.Ross@Sun.COM int
ntlmssp_next_token(struct ssp_ctx * sp,struct mbdata * in_mb,struct mbdata * out_mb)54610023SGordon.Ross@Sun.COM ntlmssp_next_token(struct ssp_ctx *sp, struct mbdata *in_mb,
54710023SGordon.Ross@Sun.COM 	struct mbdata *out_mb)
54810023SGordon.Ross@Sun.COM {
54910023SGordon.Ross@Sun.COM 	int err;
55010023SGordon.Ross@Sun.COM 
55110023SGordon.Ross@Sun.COM 	if (out_mb == NULL) {
55210023SGordon.Ross@Sun.COM 		/* final call on successful auth. */
55310023SGordon.Ross@Sun.COM 		err = ntlmssp_final(sp);
55410023SGordon.Ross@Sun.COM 		goto out;
55510023SGordon.Ross@Sun.COM 	}
55610023SGordon.Ross@Sun.COM 
55710023SGordon.Ross@Sun.COM 	/* Will build an ouptut token. */
55811332SGordon.Ross@Sun.COM 	err = mb_init(out_mb);
55910023SGordon.Ross@Sun.COM 	if (err)
56010023SGordon.Ross@Sun.COM 		goto out;
56110023SGordon.Ross@Sun.COM 
56210023SGordon.Ross@Sun.COM 	/*
56310023SGordon.Ross@Sun.COM 	 * When called with in_mb == NULL, it means
56410023SGordon.Ross@Sun.COM 	 * this is the first call for this session,
56510023SGordon.Ross@Sun.COM 	 * so put a Type 1 (initialize) token.
56610023SGordon.Ross@Sun.COM 	 */
56710023SGordon.Ross@Sun.COM 	if (in_mb == NULL) {
56810023SGordon.Ross@Sun.COM 		err = ntlmssp_put_type1(sp, out_mb);
56910023SGordon.Ross@Sun.COM 		goto out;
57010023SGordon.Ross@Sun.COM 	}
57110023SGordon.Ross@Sun.COM 
57210023SGordon.Ross@Sun.COM 	/*
57310023SGordon.Ross@Sun.COM 	 * This is not the first call, so
57410023SGordon.Ross@Sun.COM 	 * parse the response token we received.
57510023SGordon.Ross@Sun.COM 	 * It should be a Type 2 (challenge).
57610023SGordon.Ross@Sun.COM 	 * Then put a Type 3 (authenticate)
57710023SGordon.Ross@Sun.COM 	 */
57810023SGordon.Ross@Sun.COM 	err = ntlmssp_get_type2(sp, in_mb);
57910023SGordon.Ross@Sun.COM 	if (err)
58010023SGordon.Ross@Sun.COM 		goto out;
58110023SGordon.Ross@Sun.COM 
58210023SGordon.Ross@Sun.COM 	err = ntlmssp_put_type3(sp, out_mb);
58310023SGordon.Ross@Sun.COM 
58410023SGordon.Ross@Sun.COM out:
58510023SGordon.Ross@Sun.COM 	if (err)
58610023SGordon.Ross@Sun.COM 		DPRINT("ret: %d", err);
58710023SGordon.Ross@Sun.COM 	return (err);
58810023SGordon.Ross@Sun.COM }
58910023SGordon.Ross@Sun.COM 
59010023SGordon.Ross@Sun.COM /*
59110023SGordon.Ross@Sun.COM  * ntlmssp_ctx_destroy
59210023SGordon.Ross@Sun.COM  *
59310023SGordon.Ross@Sun.COM  * Destroy mechanism-specific data.
59410023SGordon.Ross@Sun.COM  */
59510023SGordon.Ross@Sun.COM void
ntlmssp_destroy(struct ssp_ctx * sp)59610023SGordon.Ross@Sun.COM ntlmssp_destroy(struct ssp_ctx *sp)
59710023SGordon.Ross@Sun.COM {
59810023SGordon.Ross@Sun.COM 	ntlmssp_state_t *ssp_st;
59910023SGordon.Ross@Sun.COM 
60010023SGordon.Ross@Sun.COM 	ssp_st = sp->sp_private;
60110023SGordon.Ross@Sun.COM 	if (ssp_st != NULL) {
60210023SGordon.Ross@Sun.COM 		sp->sp_private = NULL;
60310023SGordon.Ross@Sun.COM 		free(ssp_st->ss_target_name);
60410023SGordon.Ross@Sun.COM 		m_freem(ssp_st->ss_target_info);
60510023SGordon.Ross@Sun.COM 		free(ssp_st);
60610023SGordon.Ross@Sun.COM 	}
60710023SGordon.Ross@Sun.COM }
60810023SGordon.Ross@Sun.COM 
60910023SGordon.Ross@Sun.COM /*
61010023SGordon.Ross@Sun.COM  * ntlmssp_init_clnt
61110023SGordon.Ross@Sun.COM  *
61210023SGordon.Ross@Sun.COM  * Initialize a new NTLMSSP client context.
61310023SGordon.Ross@Sun.COM  */
61410023SGordon.Ross@Sun.COM int
ntlmssp_init_client(struct ssp_ctx * sp)61510023SGordon.Ross@Sun.COM ntlmssp_init_client(struct ssp_ctx *sp)
61610023SGordon.Ross@Sun.COM {
61710023SGordon.Ross@Sun.COM 	ntlmssp_state_t *ssp_st;
61810023SGordon.Ross@Sun.COM 
61910023SGordon.Ross@Sun.COM 	if ((sp->smb_ctx->ct_authflags &
62010023SGordon.Ross@Sun.COM 	    (SMB_AT_NTLM2 | SMB_AT_NTLM1)) == 0) {
62110023SGordon.Ross@Sun.COM 		DPRINT("No NTLM authflags");
62210023SGordon.Ross@Sun.COM 		return (ENOTSUP);
62310023SGordon.Ross@Sun.COM 	}
62410023SGordon.Ross@Sun.COM 
62510023SGordon.Ross@Sun.COM 	ssp_st = calloc(1, sizeof (*ssp_st));
62610023SGordon.Ross@Sun.COM 	if (ssp_st == NULL)
62710023SGordon.Ross@Sun.COM 		return (ENOMEM);
62810023SGordon.Ross@Sun.COM 
62910023SGordon.Ross@Sun.COM 	sp->sp_nexttok = ntlmssp_next_token;
63010023SGordon.Ross@Sun.COM 	sp->sp_destroy = ntlmssp_destroy;
63110023SGordon.Ross@Sun.COM 	sp->sp_private = ssp_st;
63210023SGordon.Ross@Sun.COM 
63310023SGordon.Ross@Sun.COM 	return (0);
63410023SGordon.Ross@Sun.COM }
635