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 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 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 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 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 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 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 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 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 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 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 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 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