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 /*
13*975041ddSGordon Ross * Copyright 2017 Nexenta Systems, Inc. All rights reserved.
1412b65585SGordon Ross */
1512b65585SGordon Ross
1612b65585SGordon Ross /*
1712b65585SGordon Ross * SPNEGO back-end for NTLMSSP. See [MS-NLMP]
1812b65585SGordon Ross */
1912b65585SGordon Ross
2012b65585SGordon Ross #include <sys/types.h>
2112b65585SGordon Ross #include <sys/byteorder.h>
2212b65585SGordon Ross #include <strings.h>
2312b65585SGordon Ross #include "smbd.h"
2412b65585SGordon Ross #include "smbd_authsvc.h"
2512b65585SGordon Ross #include "netsmb/ntlmssp.h"
2612b65585SGordon Ross #include <assert.h>
2712b65585SGordon Ross
2812b65585SGordon Ross /* A shorter alias for a crazy long name from [MS-NLMP] */
2912b65585SGordon Ross #define NTLMSSP_NEGOTIATE_NTLM2 \
3012b65585SGordon Ross NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY
3112b65585SGordon Ross
3212b65585SGordon Ross /* Need this in a header somewhere */
3312b65585SGordon Ross #ifdef _LITTLE_ENDIAN
3412b65585SGordon Ross /* little-endian values on little-endian */
3512b65585SGordon Ross #define htolel(x) ((uint32_t)(x))
3612b65585SGordon Ross #define letohl(x) ((uint32_t)(x))
3712b65585SGordon Ross #else /* (BYTE_ORDER == LITTLE_ENDIAN) */
3812b65585SGordon Ross /* little-endian values on big-endian (swap) */
39*975041ddSGordon Ross #define letohl(x) BSWAP_32(x)
40*975041ddSGordon Ross #define htolel(x) BSWAP_32(x)
4112b65585SGordon Ross #endif /* (BYTE_ORDER == LITTLE_ENDIAN) */
4212b65585SGordon Ross
4312b65585SGordon Ross typedef struct ntlmssp_backend {
4412b65585SGordon Ross uint32_t expect_type;
4512b65585SGordon Ross uint32_t clnt_flags;
4612b65585SGordon Ross uint32_t srv_flags;
4712b65585SGordon Ross char srv_challenge[8];
4812b65585SGordon Ross } ntlmssp_backend_t;
4912b65585SGordon Ross
5012b65585SGordon Ross struct genhdr {
5112b65585SGordon Ross char h_id[8]; /* "NTLMSSP" */
5212b65585SGordon Ross uint32_t h_type;
5312b65585SGordon Ross };
5412b65585SGordon Ross
5512b65585SGordon Ross struct sec_buf {
5612b65585SGordon Ross uint16_t sb_length;
5712b65585SGordon Ross uint16_t sb_maxlen;
5812b65585SGordon Ross uint32_t sb_offset;
5912b65585SGordon Ross };
6012b65585SGordon Ross
6112b65585SGordon Ross struct nego_hdr {
6212b65585SGordon Ross char h_id[8];
6312b65585SGordon Ross uint32_t h_type;
6412b65585SGordon Ross uint32_t h_flags;
6512b65585SGordon Ross /* workstation domain, name (place holders) */
6612b65585SGordon Ross uint16_t ws_dom[4];
6712b65585SGordon Ross uint16_t ws_name[4];
6812b65585SGordon Ross };
6912b65585SGordon Ross
7012b65585SGordon Ross struct auth_hdr {
7112b65585SGordon Ross char h_id[8];
7212b65585SGordon Ross uint32_t h_type;
7312b65585SGordon Ross struct sec_buf h_lm_resp;
7412b65585SGordon Ross struct sec_buf h_nt_resp;
7512b65585SGordon Ross struct sec_buf h_domain;
7612b65585SGordon Ross struct sec_buf h_user;
7712b65585SGordon Ross struct sec_buf h_wksta;
7812b65585SGordon Ross struct sec_buf h_essn_key; /* encrypted session key */
7912b65585SGordon Ross uint32_t h_flags;
8012b65585SGordon Ross /* Version struct (optional) */
8112b65585SGordon Ross /* MIC hash (optional) */
8212b65585SGordon Ross };
8312b65585SGordon Ross
8412b65585SGordon Ross /* Allow turning these off for debugging, etc. */
8512b65585SGordon Ross int smbd_signing_enabled = 1;
8612b65585SGordon Ross
8712b65585SGordon Ross int smbd_constant_challenge = 0;
8812b65585SGordon Ross static uint8_t constant_chal[8] = {
8912b65585SGordon Ross 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88 };
9012b65585SGordon Ross
9112b65585SGordon Ross static int smbd_ntlmssp_negotiate(authsvc_context_t *);
9212b65585SGordon Ross static int smbd_ntlmssp_authenticate(authsvc_context_t *);
9312b65585SGordon Ross static int encode_avpair_str(smb_msgbuf_t *, uint16_t, char *);
9412b65585SGordon Ross static int decode_secbuf_bin(smb_msgbuf_t *, struct sec_buf *, void **);
9512b65585SGordon Ross static int decode_secbuf_str(smb_msgbuf_t *, struct sec_buf *, char **);
9612b65585SGordon Ross
9712b65585SGordon Ross /*
9812b65585SGordon Ross * Initialize this context for NTLMSSP, if possible.
9912b65585SGordon Ross */
10012b65585SGordon Ross int
smbd_ntlmssp_init(authsvc_context_t * ctx)10112b65585SGordon Ross smbd_ntlmssp_init(authsvc_context_t *ctx)
10212b65585SGordon Ross {
10312b65585SGordon Ross ntlmssp_backend_t *be;
10412b65585SGordon Ross
10512b65585SGordon Ross be = malloc(sizeof (*be));
10612b65585SGordon Ross if (be == 0)
10712b65585SGordon Ross return (NT_STATUS_NO_MEMORY);
10812b65585SGordon Ross bzero(be, sizeof (*be));
10912b65585SGordon Ross be->expect_type = NTLMSSP_MSGTYPE_NEGOTIATE;
11012b65585SGordon Ross ctx->ctx_backend = be;
11112b65585SGordon Ross
11212b65585SGordon Ross return (0);
11312b65585SGordon Ross }
11412b65585SGordon Ross
11512b65585SGordon Ross void
smbd_ntlmssp_fini(authsvc_context_t * ctx)11612b65585SGordon Ross smbd_ntlmssp_fini(authsvc_context_t *ctx)
11712b65585SGordon Ross {
11812b65585SGordon Ross free(ctx->ctx_backend);
11912b65585SGordon Ross }
12012b65585SGordon Ross
12112b65585SGordon Ross /*
12212b65585SGordon Ross * Handle an auth message
12312b65585SGordon Ross */
12412b65585SGordon Ross int
smbd_ntlmssp_work(authsvc_context_t * ctx)12512b65585SGordon Ross smbd_ntlmssp_work(authsvc_context_t *ctx)
12612b65585SGordon Ross {
12712b65585SGordon Ross struct genhdr *ihdr = ctx->ctx_ibodybuf;
12812b65585SGordon Ross ntlmssp_backend_t *be = ctx->ctx_backend;
12912b65585SGordon Ross uint32_t mtype;
13012b65585SGordon Ross int rc;
13112b65585SGordon Ross
13212b65585SGordon Ross if (ctx->ctx_ibodylen < sizeof (*ihdr))
13312b65585SGordon Ross return (NT_STATUS_INVALID_PARAMETER);
13412b65585SGordon Ross
13512b65585SGordon Ross if (bcmp(ihdr->h_id, "NTLMSSP", 8))
13612b65585SGordon Ross return (NT_STATUS_INVALID_PARAMETER);
13712b65585SGordon Ross mtype = letohl(ihdr->h_type);
13812b65585SGordon Ross if (mtype != be->expect_type)
13912b65585SGordon Ross return (NT_STATUS_INVALID_PARAMETER);
14012b65585SGordon Ross
14112b65585SGordon Ross switch (mtype) {
14212b65585SGordon Ross case NTLMSSP_MSGTYPE_NEGOTIATE:
14312b65585SGordon Ross ctx->ctx_orawtype = LSA_MTYPE_ES_CONT;
14412b65585SGordon Ross rc = smbd_ntlmssp_negotiate(ctx);
14512b65585SGordon Ross break;
14612b65585SGordon Ross case NTLMSSP_MSGTYPE_AUTHENTICATE:
14712b65585SGordon Ross ctx->ctx_orawtype = LSA_MTYPE_ES_DONE;
14812b65585SGordon Ross rc = smbd_ntlmssp_authenticate(ctx);
14912b65585SGordon Ross break;
15012b65585SGordon Ross
15112b65585SGordon Ross default:
15212b65585SGordon Ross case NTLMSSP_MSGTYPE_CHALLENGE:
15312b65585SGordon Ross /* Sent by servers, not received. */
15412b65585SGordon Ross rc = NT_STATUS_INVALID_PARAMETER;
15512b65585SGordon Ross break;
15612b65585SGordon Ross }
15712b65585SGordon Ross
15812b65585SGordon Ross return (rc);
15912b65585SGordon Ross }
16012b65585SGordon Ross
16112b65585SGordon Ross #if (MAXHOSTNAMELEN < NETBIOS_NAME_SZ)
16212b65585SGordon Ross #error "MAXHOSTNAMELEN < NETBIOS_NAME_SZ"
16312b65585SGordon Ross #endif
16412b65585SGordon Ross
16512b65585SGordon Ross /*
16612b65585SGordon Ross * Handle an NTLMSSP_MSGTYPE_NEGOTIATE message, and reply
16712b65585SGordon Ross * with an NTLMSSP_MSGTYPE_CHALLENGE message.
16812b65585SGordon Ross * See: [MS-NLMP] 2.2.1.1, 3.2.5.1.1
16912b65585SGordon Ross */
17012b65585SGordon Ross static int
smbd_ntlmssp_negotiate(authsvc_context_t * ctx)17112b65585SGordon Ross smbd_ntlmssp_negotiate(authsvc_context_t *ctx)
17212b65585SGordon Ross {
17312b65585SGordon Ross char tmp_name[MAXHOSTNAMELEN];
17412b65585SGordon Ross ntlmssp_backend_t *be = ctx->ctx_backend;
17512b65585SGordon Ross struct nego_hdr *ihdr = ctx->ctx_ibodybuf;
17612b65585SGordon Ross smb_msgbuf_t mb;
17712b65585SGordon Ross uint8_t *save_scan;
17812b65585SGordon Ross int secmode;
17912b65585SGordon Ross int mbflags;
18012b65585SGordon Ross int rc;
18112b65585SGordon Ross size_t var_start, var_end;
18212b65585SGordon Ross uint16_t var_size;
18312b65585SGordon Ross
18412b65585SGordon Ross if (ctx->ctx_ibodylen < sizeof (*ihdr))
18512b65585SGordon Ross return (NT_STATUS_INVALID_PARAMETER);
18612b65585SGordon Ross be->clnt_flags = letohl(ihdr->h_flags);
18712b65585SGordon Ross
18812b65585SGordon Ross /*
18912b65585SGordon Ross * Looks like we can ignore ws_dom, ws_name.
19012b65585SGordon Ross * Otherwise would parse those here.
19112b65585SGordon Ross */
19212b65585SGordon Ross
19312b65585SGordon Ross secmode = smb_config_get_secmode();
19412b65585SGordon Ross if (smbd_constant_challenge) {
19512b65585SGordon Ross (void) memcpy(be->srv_challenge, constant_chal,
19612b65585SGordon Ross sizeof (be->srv_challenge));
19712b65585SGordon Ross } else {
19812b65585SGordon Ross randomize(be->srv_challenge, sizeof (be->srv_challenge));
19912b65585SGordon Ross }
20012b65585SGordon Ross
20112b65585SGordon Ross /*
20212b65585SGordon Ross * Compute srv_flags
20312b65585SGordon Ross */
20412b65585SGordon Ross be->srv_flags =
20512b65585SGordon Ross NTLMSSP_REQUEST_TARGET |
20612b65585SGordon Ross NTLMSSP_NEGOTIATE_NTLM |
20712b65585SGordon Ross NTLMSSP_NEGOTIATE_TARGET_INFO;
20812b65585SGordon Ross be->srv_flags |= be->clnt_flags & (
20912b65585SGordon Ross NTLMSSP_NEGOTIATE_NTLM2 |
21012b65585SGordon Ross NTLMSSP_NEGOTIATE_128 |
21112b65585SGordon Ross NTLMSSP_NEGOTIATE_KEY_EXCH |
21212b65585SGordon Ross NTLMSSP_NEGOTIATE_56);
21312b65585SGordon Ross
21412b65585SGordon Ross if (smbd_signing_enabled) {
21512b65585SGordon Ross be->srv_flags |= be->clnt_flags & (
21612b65585SGordon Ross NTLMSSP_NEGOTIATE_SIGN |
21712b65585SGordon Ross NTLMSSP_NEGOTIATE_SEAL |
21812b65585SGordon Ross NTLMSSP_NEGOTIATE_ALWAYS_SIGN);
21912b65585SGordon Ross }
22012b65585SGordon Ross
22112b65585SGordon Ross if (be->clnt_flags & NTLMSSP_NEGOTIATE_UNICODE)
22212b65585SGordon Ross be->srv_flags |= NTLMSSP_NEGOTIATE_UNICODE;
22312b65585SGordon Ross else if (be->clnt_flags & NTLMSSP_NEGOTIATE_OEM)
22412b65585SGordon Ross be->srv_flags |= NTLMSSP_NEGOTIATE_OEM;
22512b65585SGordon Ross
22612b65585SGordon Ross /* LM Key is mutually exclusive with NTLM2 */
22712b65585SGordon Ross if ((be->srv_flags & NTLMSSP_NEGOTIATE_NTLM2) == 0 &&
22812b65585SGordon Ross (be->clnt_flags & NTLMSSP_NEGOTIATE_LM_KEY) != 0)
22912b65585SGordon Ross be->srv_flags |= NTLMSSP_NEGOTIATE_LM_KEY;
23012b65585SGordon Ross
23112b65585SGordon Ross /* Get our "target name" */
23212b65585SGordon Ross if (secmode == SMB_SECMODE_DOMAIN) {
23312b65585SGordon Ross be->srv_flags |= NTLMSSP_TARGET_TYPE_DOMAIN;
23412b65585SGordon Ross rc = smb_getdomainname(tmp_name, NETBIOS_NAME_SZ);
23512b65585SGordon Ross } else {
23612b65585SGordon Ross be->srv_flags |= NTLMSSP_TARGET_TYPE_SERVER;
23712b65585SGordon Ross rc = smb_getnetbiosname(tmp_name, NETBIOS_NAME_SZ);
23812b65585SGordon Ross }
23912b65585SGordon Ross if (rc)
24012b65585SGordon Ross goto errout;
24112b65585SGordon Ross
24212b65585SGordon Ross /*
24312b65585SGordon Ross * Build the NTLMSSP_MSGTYPE_CHALLENGE message.
24412b65585SGordon Ross */
24512b65585SGordon Ross mbflags = SMB_MSGBUF_NOTERM;
24612b65585SGordon Ross if (be->srv_flags & NTLMSSP_NEGOTIATE_UNICODE)
24712b65585SGordon Ross mbflags |= SMB_MSGBUF_UNICODE;
24812b65585SGordon Ross smb_msgbuf_init(&mb, ctx->ctx_obodybuf, ctx->ctx_obodylen, mbflags);
24912b65585SGordon Ross
25012b65585SGordon Ross /*
25112b65585SGordon Ross * Fixed size parts
25212b65585SGordon Ross */
25312b65585SGordon Ross rc = smb_msgbuf_encode(
25412b65585SGordon Ross &mb, "8clwwll8cllwwl", /* offset, name (fmt) */
25512b65585SGordon Ross "NTLMSSP", /* 0: signature (8c) */
25612b65585SGordon Ross NTLMSSP_MSGTYPE_CHALLENGE, /* 8: type (l) */
25712b65585SGordon Ross 0, 0, 0, /* filled later: 12: target name (wwl) */
25812b65585SGordon Ross be->srv_flags, /* 20: flags (l) */
259*975041ddSGordon Ross be->srv_challenge, /* 24: (8c) */
26012b65585SGordon Ross 0, 0, /* 32: reserved (ll) */
26112b65585SGordon Ross 0, 0, 0); /* filled later: 40: target info (wwl) */
26212b65585SGordon Ross #define TARGET_NAME_OFFSET 12
26312b65585SGordon Ross #define TARGET_INFO_OFFSET 40
26412b65585SGordon Ross if (rc < 0)
26512b65585SGordon Ross goto errout;
26612b65585SGordon Ross
26712b65585SGordon Ross /*
26812b65585SGordon Ross * Variable length parts.
26912b65585SGordon Ross *
27012b65585SGordon Ross * Target name
27112b65585SGordon Ross */
27212b65585SGordon Ross var_start = smb_msgbuf_used(&mb);
27312b65585SGordon Ross rc = smb_msgbuf_encode(&mb, "u", tmp_name);
27412b65585SGordon Ross var_end = smb_msgbuf_used(&mb);
27512b65585SGordon Ross var_size = (uint16_t)(var_end - var_start);
27612b65585SGordon Ross if (rc < 0)
27712b65585SGordon Ross goto errout;
27812b65585SGordon Ross
27912b65585SGordon Ross /* overwrite target name offset+lengths */
28012b65585SGordon Ross save_scan = mb.scan;
28112b65585SGordon Ross mb.scan = mb.base + TARGET_NAME_OFFSET;
28212b65585SGordon Ross (void) smb_msgbuf_encode(&mb, "wwl", var_size, var_size, var_start);
28312b65585SGordon Ross mb.scan = save_scan;
28412b65585SGordon Ross
28512b65585SGordon Ross /*
28612b65585SGordon Ross * Target info (AvPairList)
28712b65585SGordon Ross *
28812b65585SGordon Ross * These AV pairs are like our name/value pairs, but have
28912b65585SGordon Ross * numeric identifiers instead of names. There are many
29012b65585SGordon Ross * of these, but we put only the four expected by Windows:
29112b65585SGordon Ross * NetBIOS computer name
29212b65585SGordon Ross * NetBIOS domain name
29312b65585SGordon Ross * DNS computer name
29412b65585SGordon Ross * DNS domain name
29512b65585SGordon Ross * Note that "domain" above (even "DNS domain") refers to
29612b65585SGordon Ross * the AD domain of which we're a member, which may be
29712b65585SGordon Ross * _different_ from the configured DNS domain.
29812b65585SGordon Ross *
29912b65585SGordon Ross * Also note that in "workgroup" mode (not a domain member)
30012b65585SGordon Ross * all "domain" fields should be set to the same values as
30112b65585SGordon Ross * the "computer" fields ("bare" host name, not FQDN).
30212b65585SGordon Ross */
30312b65585SGordon Ross var_start = smb_msgbuf_used(&mb);
30412b65585SGordon Ross
30512b65585SGordon Ross /* NetBIOS Computer Name */
30612b65585SGordon Ross if (smb_getnetbiosname(tmp_name, NETBIOS_NAME_SZ))
30712b65585SGordon Ross goto errout;
30812b65585SGordon Ross if (encode_avpair_str(&mb, MsvAvNbComputerName, tmp_name) < 0)
30912b65585SGordon Ross goto errout;
31012b65585SGordon Ross
31112b65585SGordon Ross if (secmode != SMB_SECMODE_DOMAIN) {
31212b65585SGordon Ross /*
31312b65585SGordon Ross * Workgroup mode. Set all to hostname.
31412b65585SGordon Ross * tmp_name = netbios hostname from above.
31512b65585SGordon Ross */
31612b65585SGordon Ross if (encode_avpair_str(&mb, MsvAvNbDomainName, tmp_name) < 0)
31712b65585SGordon Ross goto errout;
31812b65585SGordon Ross /*
31912b65585SGordon Ross * Want the bare computer name here (not FQDN).
32012b65585SGordon Ross */
32112b65585SGordon Ross if (smb_gethostname(tmp_name, MAXHOSTNAMELEN, SMB_CASE_LOWER))
32212b65585SGordon Ross goto errout;
32312b65585SGordon Ross if (encode_avpair_str(&mb, MsvAvDnsComputerName, tmp_name) < 0)
32412b65585SGordon Ross goto errout;
32512b65585SGordon Ross if (encode_avpair_str(&mb, MsvAvDnsDomainName, tmp_name) < 0)
32612b65585SGordon Ross goto errout;
32712b65585SGordon Ross } else {
32812b65585SGordon Ross /*
32912b65585SGordon Ross * Domain mode. Use real host and domain values.
33012b65585SGordon Ross */
33112b65585SGordon Ross
33212b65585SGordon Ross /* NetBIOS Domain Name */
33312b65585SGordon Ross if (smb_getdomainname(tmp_name, NETBIOS_NAME_SZ))
33412b65585SGordon Ross goto errout;
33512b65585SGordon Ross if (encode_avpair_str(&mb, MsvAvNbDomainName, tmp_name) < 0)
33612b65585SGordon Ross goto errout;
33712b65585SGordon Ross
33812b65585SGordon Ross /* DNS Computer Name */
33912b65585SGordon Ross if (smb_getfqhostname(tmp_name, MAXHOSTNAMELEN))
34012b65585SGordon Ross goto errout;
34112b65585SGordon Ross if (encode_avpair_str(&mb, MsvAvDnsComputerName, tmp_name) < 0)
34212b65585SGordon Ross goto errout;
34312b65585SGordon Ross
34412b65585SGordon Ross /* DNS Domain Name */
34512b65585SGordon Ross if (smb_getfqdomainname(tmp_name, MAXHOSTNAMELEN))
34612b65585SGordon Ross goto errout;
34712b65585SGordon Ross if (encode_avpair_str(&mb, MsvAvDnsDomainName, tmp_name) < 0)
34812b65585SGordon Ross goto errout;
34912b65585SGordon Ross }
35012b65585SGordon Ross
35112b65585SGordon Ross /* End marker */
35212b65585SGordon Ross if (smb_msgbuf_encode(&mb, "ww", MsvAvEOL, 0) < 0)
35312b65585SGordon Ross goto errout;
35412b65585SGordon Ross var_end = smb_msgbuf_used(&mb);
35512b65585SGordon Ross var_size = (uint16_t)(var_end - var_start);
35612b65585SGordon Ross
35712b65585SGordon Ross /* overwrite target offset+lengths */
35812b65585SGordon Ross save_scan = mb.scan;
35912b65585SGordon Ross mb.scan = mb.base + TARGET_INFO_OFFSET;
36012b65585SGordon Ross (void) smb_msgbuf_encode(&mb, "wwl", var_size, var_size, var_start);
36112b65585SGordon Ross mb.scan = save_scan;
36212b65585SGordon Ross
36312b65585SGordon Ross ctx->ctx_obodylen = smb_msgbuf_used(&mb);
36412b65585SGordon Ross smb_msgbuf_term(&mb);
36512b65585SGordon Ross
36612b65585SGordon Ross be->expect_type = NTLMSSP_MSGTYPE_AUTHENTICATE;
36712b65585SGordon Ross
36812b65585SGordon Ross return (0);
36912b65585SGordon Ross
37012b65585SGordon Ross errout:
37112b65585SGordon Ross smb_msgbuf_term(&mb);
37212b65585SGordon Ross return (NT_STATUS_INTERNAL_ERROR);
37312b65585SGordon Ross }
37412b65585SGordon Ross
37512b65585SGordon Ross static int
encode_avpair_str(smb_msgbuf_t * mb,uint16_t AvId,char * name)37612b65585SGordon Ross encode_avpair_str(smb_msgbuf_t *mb, uint16_t AvId, char *name)
37712b65585SGordon Ross {
37812b65585SGordon Ross int rc;
37912b65585SGordon Ross uint16_t len;
38012b65585SGordon Ross
38112b65585SGordon Ross len = smb_wcequiv_strlen(name);
38212b65585SGordon Ross rc = smb_msgbuf_encode(mb, "wwU", AvId, len, name);
38312b65585SGordon Ross return (rc);
38412b65585SGordon Ross }
38512b65585SGordon Ross
38612b65585SGordon Ross /*
38712b65585SGordon Ross * Handle an NTLMSSP_MSGTYPE_AUTHENTICATE message.
38812b65585SGordon Ross * See: [MS-NLMP] 2.2.1.3, 3.2.5.1.2
38912b65585SGordon Ross */
39012b65585SGordon Ross static int
smbd_ntlmssp_authenticate(authsvc_context_t * ctx)39112b65585SGordon Ross smbd_ntlmssp_authenticate(authsvc_context_t *ctx)
39212b65585SGordon Ross {
39312b65585SGordon Ross struct auth_hdr hdr;
39412b65585SGordon Ross smb_msgbuf_t mb;
39512b65585SGordon Ross smb_logon_t user_info;
39612b65585SGordon Ross smb_token_t *token = NULL;
39712b65585SGordon Ross ntlmssp_backend_t *be = ctx->ctx_backend;
39812b65585SGordon Ross void *lm_resp;
39912b65585SGordon Ross void *nt_resp;
40012b65585SGordon Ross char *domain;
40112b65585SGordon Ross char *user;
40212b65585SGordon Ross char *wksta;
40312b65585SGordon Ross void *essn_key; /* encrypted session key (optional) */
40412b65585SGordon Ross int mbflags;
40512b65585SGordon Ross uint_t status = NT_STATUS_INTERNAL_ERROR;
40612b65585SGordon Ross char combined_challenge[SMBAUTH_CHAL_SZ];
40712b65585SGordon Ross unsigned char kxkey[SMBAUTH_HASH_SZ];
40812b65585SGordon Ross boolean_t ntlm_v1x = B_FALSE;
40912b65585SGordon Ross
41012b65585SGordon Ross bzero(&user_info, sizeof (user_info));
41112b65585SGordon Ross
41212b65585SGordon Ross /*
41312b65585SGordon Ross * Parse the NTLMSSP_MSGTYPE_AUTHENTICATE message.
41412b65585SGordon Ross */
41512b65585SGordon Ross if (ctx->ctx_ibodylen < sizeof (hdr))
41612b65585SGordon Ross return (NT_STATUS_INVALID_PARAMETER);
41712b65585SGordon Ross mbflags = SMB_MSGBUF_NOTERM;
41812b65585SGordon Ross if (be->srv_flags & NTLMSSP_NEGOTIATE_UNICODE)
41912b65585SGordon Ross mbflags |= SMB_MSGBUF_UNICODE;
42012b65585SGordon Ross smb_msgbuf_init(&mb, ctx->ctx_ibodybuf, ctx->ctx_ibodylen, mbflags);
42112b65585SGordon Ross bzero(&hdr, sizeof (hdr));
42212b65585SGordon Ross
42312b65585SGordon Ross if (smb_msgbuf_decode(&mb, "12.") < 0)
42412b65585SGordon Ross goto errout;
42512b65585SGordon Ross if (decode_secbuf_bin(&mb, &hdr.h_lm_resp, &lm_resp) < 0)
42612b65585SGordon Ross goto errout;
42712b65585SGordon Ross if (decode_secbuf_bin(&mb, &hdr.h_nt_resp, &nt_resp) < 0)
42812b65585SGordon Ross goto errout;
42912b65585SGordon Ross if (decode_secbuf_str(&mb, &hdr.h_domain, &domain) < 0)
43012b65585SGordon Ross goto errout;
43112b65585SGordon Ross if (decode_secbuf_str(&mb, &hdr.h_user, &user) < 0)
43212b65585SGordon Ross goto errout;
43312b65585SGordon Ross if (decode_secbuf_str(&mb, &hdr.h_wksta, &wksta) < 0)
43412b65585SGordon Ross goto errout;
43512b65585SGordon Ross if (decode_secbuf_bin(&mb, &hdr.h_essn_key, &essn_key) < 0)
43612b65585SGordon Ross goto errout;
43712b65585SGordon Ross if (smb_msgbuf_decode(&mb, "l", &be->clnt_flags) < 0)
43812b65585SGordon Ross goto errout;
43912b65585SGordon Ross
44012b65585SGordon Ross if (be->clnt_flags & NTLMSSP_NEGOTIATE_KEY_EXCH) {
44112b65585SGordon Ross if (hdr.h_essn_key.sb_length < 16 || essn_key == NULL)
44212b65585SGordon Ross goto errout;
44312b65585SGordon Ross }
44412b65585SGordon Ross
44512b65585SGordon Ross user_info.lg_level = NETR_NETWORK_LOGON;
44612b65585SGordon Ross user_info.lg_flags = 0;
44712b65585SGordon Ross
44812b65585SGordon Ross user_info.lg_ntlm_flags = be->clnt_flags;
44912b65585SGordon Ross user_info.lg_username = (user) ? user : "";
45012b65585SGordon Ross user_info.lg_domain = (domain) ? domain : "";
45112b65585SGordon Ross user_info.lg_workstation = (wksta) ? wksta : "";
45212b65585SGordon Ross
45312b65585SGordon Ross user_info.lg_clnt_ipaddr =
45412b65585SGordon Ross ctx->ctx_clinfo.lci_clnt_ipaddr;
45512b65585SGordon Ross user_info.lg_local_port = 445;
45612b65585SGordon Ross
45712b65585SGordon Ross user_info.lg_challenge_key.len = SMBAUTH_CHAL_SZ;
45812b65585SGordon Ross user_info.lg_challenge_key.val = (uint8_t *)be->srv_challenge;
45912b65585SGordon Ross
46012b65585SGordon Ross user_info.lg_nt_password.len = hdr.h_nt_resp.sb_length;
46112b65585SGordon Ross user_info.lg_nt_password.val = nt_resp;
46212b65585SGordon Ross
46312b65585SGordon Ross user_info.lg_lm_password.len = hdr.h_lm_resp.sb_length;
46412b65585SGordon Ross user_info.lg_lm_password.val = lm_resp;
46512b65585SGordon Ross
46612b65585SGordon Ross user_info.lg_native_os = ctx->ctx_clinfo.lci_native_os;
46712b65585SGordon Ross user_info.lg_native_lm = ctx->ctx_clinfo.lci_native_lm;
46812b65585SGordon Ross
46912b65585SGordon Ross /*
47012b65585SGordon Ross * If we're doing extended session security, the challenge
47112b65585SGordon Ross * this OWF was computed with is different. [MS-NLMP 3.3.1]
47212b65585SGordon Ross * It's: MD5(concat(ServerChallenge,ClientChallenge))
47312b65585SGordon Ross * where the ClientChallenge is in the LM resp. field.
47412b65585SGordon Ross */
47512b65585SGordon Ross if (user_info.lg_nt_password.len == SMBAUTH_LM_RESP_SZ &&
47612b65585SGordon Ross user_info.lg_lm_password.len >= SMBAUTH_CHAL_SZ &&
47712b65585SGordon Ross (be->clnt_flags & NTLMSSP_NEGOTIATE_NTLM2) != 0) {
47812b65585SGordon Ross smb_auth_ntlm2_mkchallenge(combined_challenge,
47912b65585SGordon Ross be->srv_challenge, lm_resp);
48012b65585SGordon Ross user_info.lg_challenge_key.val =
48112b65585SGordon Ross (uint8_t *)combined_challenge;
48212b65585SGordon Ross user_info.lg_lm_password.len = 0;
48312b65585SGordon Ross ntlm_v1x = B_TRUE;
48412b65585SGordon Ross }
48512b65585SGordon Ross
48612b65585SGordon Ross /*
48712b65585SGordon Ross * This (indirectly) calls smb_auth_validate() to
48812b65585SGordon Ross * check that the client gave us a valid hash.
48912b65585SGordon Ross */
49012b65585SGordon Ross token = smbd_user_auth_logon(&user_info);
49112b65585SGordon Ross if (token == NULL) {
492*975041ddSGordon Ross status = user_info.lg_status;
493*975041ddSGordon Ross if (status == 0) /* should not happen */
494*975041ddSGordon Ross status = NT_STATUS_INTERNAL_ERROR;
49512b65585SGordon Ross goto errout;
49612b65585SGordon Ross }
49712b65585SGordon Ross
49812b65585SGordon Ross if (token->tkn_ssnkey.val != NULL &&
49912b65585SGordon Ross token->tkn_ssnkey.len == SMBAUTH_HASH_SZ) {
50012b65585SGordon Ross
50112b65585SGordon Ross /*
50212b65585SGordon Ross * At this point, token->tkn_session_key is the
50312b65585SGordon Ross * "Session Base Key" [MS-NLMP] 3.2.5.1.2
50412b65585SGordon Ross * Compute the final session key. First need the
50512b65585SGordon Ross * "Key Exchange Key" [MS-NLMP] 3.4.5.1
50612b65585SGordon Ross */
50712b65585SGordon Ross if (ntlm_v1x) {
50812b65585SGordon Ross smb_auth_ntlm2_kxkey(kxkey,
50912b65585SGordon Ross be->srv_challenge, lm_resp,
51012b65585SGordon Ross token->tkn_ssnkey.val);
51112b65585SGordon Ross } else {
51212b65585SGordon Ross /* KXKEY is the Session Base Key. */
51312b65585SGordon Ross (void) memcpy(kxkey, token->tkn_ssnkey.val,
51412b65585SGordon Ross SMBAUTH_HASH_SZ);
51512b65585SGordon Ross }
51612b65585SGordon Ross
51712b65585SGordon Ross /*
51812b65585SGordon Ross * If the client give us an encrypted session key,
51912b65585SGordon Ross * decrypt it (RC4) using the "key exchange key".
52012b65585SGordon Ross */
52112b65585SGordon Ross if (be->clnt_flags & NTLMSSP_NEGOTIATE_KEY_EXCH) {
52212b65585SGordon Ross /* RC4 args: result, key, data */
52312b65585SGordon Ross (void) smb_auth_RC4(token->tkn_ssnkey.val,
52412b65585SGordon Ross SMBAUTH_HASH_SZ, kxkey, SMBAUTH_HASH_SZ,
52512b65585SGordon Ross essn_key, hdr.h_essn_key.sb_length);
52612b65585SGordon Ross } else {
52712b65585SGordon Ross /* Final key is the KXKEY */
52812b65585SGordon Ross (void) memcpy(token->tkn_ssnkey.val, kxkey,
52912b65585SGordon Ross SMBAUTH_HASH_SZ);
53012b65585SGordon Ross }
53112b65585SGordon Ross }
53212b65585SGordon Ross
53312b65585SGordon Ross ctx->ctx_token = token;
53412b65585SGordon Ross ctx->ctx_obodylen = 0;
53512b65585SGordon Ross
53612b65585SGordon Ross smb_msgbuf_term(&mb);
53712b65585SGordon Ross return (0);
53812b65585SGordon Ross
53912b65585SGordon Ross errout:
54012b65585SGordon Ross smb_msgbuf_term(&mb);
54112b65585SGordon Ross return (status);
54212b65585SGordon Ross }
54312b65585SGordon Ross
54412b65585SGordon Ross static int
decode_secbuf_bin(smb_msgbuf_t * mb,struct sec_buf * sb,void ** binp)54512b65585SGordon Ross decode_secbuf_bin(smb_msgbuf_t *mb, struct sec_buf *sb, void **binp)
54612b65585SGordon Ross {
54712b65585SGordon Ross int rc;
54812b65585SGordon Ross
54912b65585SGordon Ross *binp = NULL;
55012b65585SGordon Ross rc = smb_msgbuf_decode(
55112b65585SGordon Ross mb, "wwl",
55212b65585SGordon Ross &sb->sb_length,
55312b65585SGordon Ross &sb->sb_maxlen,
55412b65585SGordon Ross &sb->sb_offset);
55512b65585SGordon Ross if (rc < 0)
55612b65585SGordon Ross return (rc);
55712b65585SGordon Ross
55812b65585SGordon Ross if (sb->sb_offset > mb->max)
55912b65585SGordon Ross return (SMB_MSGBUF_UNDERFLOW);
56012b65585SGordon Ross if (sb->sb_length > (mb->max - sb->sb_offset))
56112b65585SGordon Ross return (SMB_MSGBUF_UNDERFLOW);
56212b65585SGordon Ross if (sb->sb_length == 0)
56312b65585SGordon Ross return (rc);
56412b65585SGordon Ross
56512b65585SGordon Ross *binp = mb->base + sb->sb_offset;
56612b65585SGordon Ross return (0);
56712b65585SGordon Ross }
56812b65585SGordon Ross
56912b65585SGordon Ross static int
decode_secbuf_str(smb_msgbuf_t * mb,struct sec_buf * sb,char ** cpp)57012b65585SGordon Ross decode_secbuf_str(smb_msgbuf_t *mb, struct sec_buf *sb, char **cpp)
57112b65585SGordon Ross {
57212b65585SGordon Ross uint8_t *save_scan;
57312b65585SGordon Ross int rc;
57412b65585SGordon Ross
57512b65585SGordon Ross *cpp = NULL;
57612b65585SGordon Ross rc = smb_msgbuf_decode(
57712b65585SGordon Ross mb, "wwl",
57812b65585SGordon Ross &sb->sb_length,
57912b65585SGordon Ross &sb->sb_maxlen,
58012b65585SGordon Ross &sb->sb_offset);
58112b65585SGordon Ross if (rc < 0)
58212b65585SGordon Ross return (rc);
58312b65585SGordon Ross
58412b65585SGordon Ross if (sb->sb_offset > mb->max)
58512b65585SGordon Ross return (SMB_MSGBUF_UNDERFLOW);
58612b65585SGordon Ross if (sb->sb_length > (mb->max - sb->sb_offset))
58712b65585SGordon Ross return (SMB_MSGBUF_UNDERFLOW);
58812b65585SGordon Ross if (sb->sb_length == 0)
58912b65585SGordon Ross return (rc);
59012b65585SGordon Ross
59112b65585SGordon Ross save_scan = mb->scan;
59212b65585SGordon Ross mb->scan = mb->base + sb->sb_offset;
59312b65585SGordon Ross rc = smb_msgbuf_decode(mb, "#u", (int)sb->sb_length, cpp);
59412b65585SGordon Ross mb->scan = save_scan;
59512b65585SGordon Ross
59612b65585SGordon Ross return (rc);
59712b65585SGordon Ross }
598