1*10023SGordon.Ross@Sun.COM /* 2*10023SGordon.Ross@Sun.COM * CDDL HEADER START 3*10023SGordon.Ross@Sun.COM * 4*10023SGordon.Ross@Sun.COM * The contents of this file are subject to the terms of the 5*10023SGordon.Ross@Sun.COM * Common Development and Distribution License (the "License"). 6*10023SGordon.Ross@Sun.COM * You may not use this file except in compliance with the License. 7*10023SGordon.Ross@Sun.COM * 8*10023SGordon.Ross@Sun.COM * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9*10023SGordon.Ross@Sun.COM * or http://www.opensolaris.org/os/licensing. 10*10023SGordon.Ross@Sun.COM * See the License for the specific language governing permissions 11*10023SGordon.Ross@Sun.COM * and limitations under the License. 12*10023SGordon.Ross@Sun.COM * 13*10023SGordon.Ross@Sun.COM * When distributing Covered Code, include this CDDL HEADER in each 14*10023SGordon.Ross@Sun.COM * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15*10023SGordon.Ross@Sun.COM * If applicable, add the following below this CDDL HEADER, with the 16*10023SGordon.Ross@Sun.COM * fields enclosed by brackets "[]" replaced with your own identifying 17*10023SGordon.Ross@Sun.COM * information: Portions Copyright [yyyy] [name of copyright owner] 18*10023SGordon.Ross@Sun.COM * 19*10023SGordon.Ross@Sun.COM * CDDL HEADER END 20*10023SGordon.Ross@Sun.COM */ 21*10023SGordon.Ross@Sun.COM 22*10023SGordon.Ross@Sun.COM /* 23*10023SGordon.Ross@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24*10023SGordon.Ross@Sun.COM * Use is subject to license terms. 25*10023SGordon.Ross@Sun.COM */ 26*10023SGordon.Ross@Sun.COM 27*10023SGordon.Ross@Sun.COM /* 28*10023SGordon.Ross@Sun.COM * NT Lan Manager Security Support Provider (NTLMSSP) 29*10023SGordon.Ross@Sun.COM * 30*10023SGordon.Ross@Sun.COM * Based on information from the "Davenport NTLM" page: 31*10023SGordon.Ross@Sun.COM * http://davenport.sourceforge.net/ntlm.html 32*10023SGordon.Ross@Sun.COM */ 33*10023SGordon.Ross@Sun.COM 34*10023SGordon.Ross@Sun.COM 35*10023SGordon.Ross@Sun.COM #include <errno.h> 36*10023SGordon.Ross@Sun.COM #include <stdio.h> 37*10023SGordon.Ross@Sun.COM #include <stddef.h> 38*10023SGordon.Ross@Sun.COM #include <stdlib.h> 39*10023SGordon.Ross@Sun.COM #include <unistd.h> 40*10023SGordon.Ross@Sun.COM #include <strings.h> 41*10023SGordon.Ross@Sun.COM #include <netdb.h> 42*10023SGordon.Ross@Sun.COM #include <libintl.h> 43*10023SGordon.Ross@Sun.COM #include <xti.h> 44*10023SGordon.Ross@Sun.COM #include <assert.h> 45*10023SGordon.Ross@Sun.COM 46*10023SGordon.Ross@Sun.COM #include <sys/types.h> 47*10023SGordon.Ross@Sun.COM #include <sys/time.h> 48*10023SGordon.Ross@Sun.COM #include <sys/byteorder.h> 49*10023SGordon.Ross@Sun.COM #include <sys/socket.h> 50*10023SGordon.Ross@Sun.COM #include <sys/fcntl.h> 51*10023SGordon.Ross@Sun.COM 52*10023SGordon.Ross@Sun.COM #include <netinet/in.h> 53*10023SGordon.Ross@Sun.COM #include <netinet/tcp.h> 54*10023SGordon.Ross@Sun.COM #include <arpa/inet.h> 55*10023SGordon.Ross@Sun.COM 56*10023SGordon.Ross@Sun.COM #include <netsmb/smb.h> 57*10023SGordon.Ross@Sun.COM #include <netsmb/smb_lib.h> 58*10023SGordon.Ross@Sun.COM #include <netsmb/mchain.h> 59*10023SGordon.Ross@Sun.COM 60*10023SGordon.Ross@Sun.COM #include "private.h" 61*10023SGordon.Ross@Sun.COM #include "charsets.h" 62*10023SGordon.Ross@Sun.COM #include "spnego.h" 63*10023SGordon.Ross@Sun.COM #include "derparse.h" 64*10023SGordon.Ross@Sun.COM #include "ssp.h" 65*10023SGordon.Ross@Sun.COM #include "ntlm.h" 66*10023SGordon.Ross@Sun.COM #include "ntlmssp.h" 67*10023SGordon.Ross@Sun.COM 68*10023SGordon.Ross@Sun.COM typedef struct ntlmssp_state { 69*10023SGordon.Ross@Sun.COM uint32_t ss_flags; 70*10023SGordon.Ross@Sun.COM char *ss_target_name; 71*10023SGordon.Ross@Sun.COM struct mbuf *ss_target_info; 72*10023SGordon.Ross@Sun.COM } ntlmssp_state_t; 73*10023SGordon.Ross@Sun.COM 74*10023SGordon.Ross@Sun.COM /* 75*10023SGordon.Ross@Sun.COM * So called "security buffer". 76*10023SGordon.Ross@Sun.COM * A lot like an RPC string. 77*10023SGordon.Ross@Sun.COM */ 78*10023SGordon.Ross@Sun.COM struct sec_buf { 79*10023SGordon.Ross@Sun.COM uint16_t sb_length; 80*10023SGordon.Ross@Sun.COM uint16_t sb_maxlen; 81*10023SGordon.Ross@Sun.COM uint32_t sb_offset; 82*10023SGordon.Ross@Sun.COM }; 83*10023SGordon.Ross@Sun.COM #define ID_SZ 8 84*10023SGordon.Ross@Sun.COM static const char ntlmssp_id[ID_SZ] = "NTLMSSP"; 85*10023SGordon.Ross@Sun.COM 86*10023SGordon.Ross@Sun.COM /* 87*10023SGordon.Ross@Sun.COM * Get a "security buffer" (header part) 88*10023SGordon.Ross@Sun.COM */ 89*10023SGordon.Ross@Sun.COM static int 90*10023SGordon.Ross@Sun.COM mb_get_sb_hdr(struct mbdata *mbp, struct sec_buf *sb) 91*10023SGordon.Ross@Sun.COM { 92*10023SGordon.Ross@Sun.COM int err; 93*10023SGordon.Ross@Sun.COM 94*10023SGordon.Ross@Sun.COM (void) mb_get_uint16le(mbp, &sb->sb_length); 95*10023SGordon.Ross@Sun.COM (void) mb_get_uint16le(mbp, &sb->sb_maxlen); 96*10023SGordon.Ross@Sun.COM err = mb_get_uint32le(mbp, &sb->sb_offset); 97*10023SGordon.Ross@Sun.COM 98*10023SGordon.Ross@Sun.COM return (err); 99*10023SGordon.Ross@Sun.COM } 100*10023SGordon.Ross@Sun.COM 101*10023SGordon.Ross@Sun.COM /* 102*10023SGordon.Ross@Sun.COM * Get a "security buffer" (data part), where 103*10023SGordon.Ross@Sun.COM * the data is delivered as an mbuf. 104*10023SGordon.Ross@Sun.COM */ 105*10023SGordon.Ross@Sun.COM static int 106*10023SGordon.Ross@Sun.COM mb_get_sb_data(struct mbdata *mbp, struct sec_buf *sb, struct mbuf **mp) 107*10023SGordon.Ross@Sun.COM { 108*10023SGordon.Ross@Sun.COM struct mbdata tmp_mb; 109*10023SGordon.Ross@Sun.COM int err; 110*10023SGordon.Ross@Sun.COM 111*10023SGordon.Ross@Sun.COM /* 112*10023SGordon.Ross@Sun.COM * Setup tmp_mb to point to the start of the header. 113*10023SGordon.Ross@Sun.COM * This is a dup ref - do NOT free it. 114*10023SGordon.Ross@Sun.COM */ 115*10023SGordon.Ross@Sun.COM mb_initm(&tmp_mb, mbp->mb_top); 116*10023SGordon.Ross@Sun.COM 117*10023SGordon.Ross@Sun.COM /* Skip data up to the offset. */ 118*10023SGordon.Ross@Sun.COM err = mb_get_mem(&tmp_mb, NULL, sb->sb_offset); 119*10023SGordon.Ross@Sun.COM if (err) 120*10023SGordon.Ross@Sun.COM return (err); 121*10023SGordon.Ross@Sun.COM 122*10023SGordon.Ross@Sun.COM /* Get the data (as an mbuf). */ 123*10023SGordon.Ross@Sun.COM err = mb_get_mbuf(&tmp_mb, sb->sb_maxlen, mp); 124*10023SGordon.Ross@Sun.COM 125*10023SGordon.Ross@Sun.COM return (err); 126*10023SGordon.Ross@Sun.COM } 127*10023SGordon.Ross@Sun.COM 128*10023SGordon.Ross@Sun.COM /* 129*10023SGordon.Ross@Sun.COM * Put a "security buffer" (header part) 130*10023SGordon.Ross@Sun.COM */ 131*10023SGordon.Ross@Sun.COM static int 132*10023SGordon.Ross@Sun.COM mb_put_sb_hdr(struct mbdata *mbp, struct sec_buf *sb) 133*10023SGordon.Ross@Sun.COM { 134*10023SGordon.Ross@Sun.COM int err; 135*10023SGordon.Ross@Sun.COM 136*10023SGordon.Ross@Sun.COM (void) mb_put_uint16le(mbp, sb->sb_length); 137*10023SGordon.Ross@Sun.COM (void) mb_put_uint16le(mbp, sb->sb_maxlen); 138*10023SGordon.Ross@Sun.COM err = mb_put_uint32le(mbp, sb->sb_offset); 139*10023SGordon.Ross@Sun.COM 140*10023SGordon.Ross@Sun.COM return (err); 141*10023SGordon.Ross@Sun.COM } 142*10023SGordon.Ross@Sun.COM 143*10023SGordon.Ross@Sun.COM /* 144*10023SGordon.Ross@Sun.COM * Put a "security buffer" (data part), where 145*10023SGordon.Ross@Sun.COM * the data is an mbuf. Note: consumes m. 146*10023SGordon.Ross@Sun.COM */ 147*10023SGordon.Ross@Sun.COM static int 148*10023SGordon.Ross@Sun.COM mb_put_sb_data(struct mbdata *mbp, struct sec_buf *sb, struct mbuf *m) 149*10023SGordon.Ross@Sun.COM { 150*10023SGordon.Ross@Sun.COM int cnt0, err; 151*10023SGordon.Ross@Sun.COM 152*10023SGordon.Ross@Sun.COM sb->sb_offset = cnt0 = mbp->mb_count; 153*10023SGordon.Ross@Sun.COM err = mb_put_mbuf(mbp, m); 154*10023SGordon.Ross@Sun.COM sb->sb_maxlen = sb->sb_length = mbp->mb_count - cnt0; 155*10023SGordon.Ross@Sun.COM 156*10023SGordon.Ross@Sun.COM return (err); 157*10023SGordon.Ross@Sun.COM } 158*10023SGordon.Ross@Sun.COM 159*10023SGordon.Ross@Sun.COM /* 160*10023SGordon.Ross@Sun.COM * Put a "security buffer" (data part), where 161*10023SGordon.Ross@Sun.COM * the data is a string (OEM or unicode). 162*10023SGordon.Ross@Sun.COM * 163*10023SGordon.Ross@Sun.COM * The string is NOT null terminated. 164*10023SGordon.Ross@Sun.COM */ 165*10023SGordon.Ross@Sun.COM static int 166*10023SGordon.Ross@Sun.COM mb_put_sb_string(struct mbdata *mbp, struct sec_buf *sb, 167*10023SGordon.Ross@Sun.COM const char *s, int unicode) 168*10023SGordon.Ross@Sun.COM { 169*10023SGordon.Ross@Sun.COM int err, trim; 170*10023SGordon.Ross@Sun.COM struct mbdata tmp_mb; 171*10023SGordon.Ross@Sun.COM 172*10023SGordon.Ross@Sun.COM /* 173*10023SGordon.Ross@Sun.COM * Put the string into a temp. mbuf, 174*10023SGordon.Ross@Sun.COM * then chop off the null terminator 175*10023SGordon.Ross@Sun.COM * before appending to caller's mbp. 176*10023SGordon.Ross@Sun.COM */ 177*10023SGordon.Ross@Sun.COM err = mb_init(&tmp_mb, M_MINSIZE); 178*10023SGordon.Ross@Sun.COM if (err) 179*10023SGordon.Ross@Sun.COM return (err); 180*10023SGordon.Ross@Sun.COM err = mb_put_dstring(&tmp_mb, s, unicode); 181*10023SGordon.Ross@Sun.COM if (err) 182*10023SGordon.Ross@Sun.COM return (err); 183*10023SGordon.Ross@Sun.COM 184*10023SGordon.Ross@Sun.COM trim = (unicode) ? 2 : 1; 185*10023SGordon.Ross@Sun.COM if (tmp_mb.mb_cur->m_len < trim) 186*10023SGordon.Ross@Sun.COM return (EFAULT); 187*10023SGordon.Ross@Sun.COM tmp_mb.mb_cur->m_len -= trim; 188*10023SGordon.Ross@Sun.COM 189*10023SGordon.Ross@Sun.COM err = mb_put_sb_data(mbp, sb, tmp_mb.mb_top); 190*10023SGordon.Ross@Sun.COM /* 191*10023SGordon.Ross@Sun.COM * Note: tmp_mb.mb_top is consumed, 192*10023SGordon.Ross@Sun.COM * so do NOT free it (no mb_done) 193*10023SGordon.Ross@Sun.COM */ 194*10023SGordon.Ross@Sun.COM return (err); 195*10023SGordon.Ross@Sun.COM } 196*10023SGordon.Ross@Sun.COM 197*10023SGordon.Ross@Sun.COM /* 198*10023SGordon.Ross@Sun.COM * Build a Type 1 message 199*10023SGordon.Ross@Sun.COM * 200*10023SGordon.Ross@Sun.COM * This message has a header section containing offsets to 201*10023SGordon.Ross@Sun.COM * data later in the message. We use the common trick of 202*10023SGordon.Ross@Sun.COM * building it in two parts and then concatenatening. 203*10023SGordon.Ross@Sun.COM */ 204*10023SGordon.Ross@Sun.COM int 205*10023SGordon.Ross@Sun.COM ntlmssp_put_type1(struct ssp_ctx *sp, struct mbdata *out_mb) 206*10023SGordon.Ross@Sun.COM { 207*10023SGordon.Ross@Sun.COM struct type1hdr { 208*10023SGordon.Ross@Sun.COM char h_id[ID_SZ]; 209*10023SGordon.Ross@Sun.COM uint32_t h_type; 210*10023SGordon.Ross@Sun.COM uint32_t h_flags; 211*10023SGordon.Ross@Sun.COM struct sec_buf h_cldom; 212*10023SGordon.Ross@Sun.COM struct sec_buf h_wksta; 213*10023SGordon.Ross@Sun.COM } hdr; 214*10023SGordon.Ross@Sun.COM struct mbdata mb2; /* 2nd part */ 215*10023SGordon.Ross@Sun.COM int err; 216*10023SGordon.Ross@Sun.COM struct smb_ctx *ctx = sp->smb_ctx; 217*10023SGordon.Ross@Sun.COM ntlmssp_state_t *ssp_st = sp->sp_private; 218*10023SGordon.Ross@Sun.COM char *ucdom = NULL; 219*10023SGordon.Ross@Sun.COM char *ucwks = NULL; 220*10023SGordon.Ross@Sun.COM 221*10023SGordon.Ross@Sun.COM if ((err = mb_init(&mb2, M_MINSIZE)) != 0) 222*10023SGordon.Ross@Sun.COM return (err); 223*10023SGordon.Ross@Sun.COM mb2.mb_count = sizeof (hdr); 224*10023SGordon.Ross@Sun.COM 225*10023SGordon.Ross@Sun.COM /* 226*10023SGordon.Ross@Sun.COM * Initialize the negotiation flags, and 227*10023SGordon.Ross@Sun.COM * save what we sent. For reference: 228*10023SGordon.Ross@Sun.COM * [MS-NLMP] spec. (also ntlmssp.h) 229*10023SGordon.Ross@Sun.COM */ 230*10023SGordon.Ross@Sun.COM ssp_st->ss_flags = 231*10023SGordon.Ross@Sun.COM NTLMSSP_REQUEST_TARGET | 232*10023SGordon.Ross@Sun.COM NTLMSSP_NEGOTIATE_NTLM | 233*10023SGordon.Ross@Sun.COM NTLMSSP_NEGOTIATE_TARGET_INFO | 234*10023SGordon.Ross@Sun.COM NTLMSSP_NEGOTIATE_128 | 235*10023SGordon.Ross@Sun.COM NTLMSSP_NEGOTIATE_56; 236*10023SGordon.Ross@Sun.COM 237*10023SGordon.Ross@Sun.COM if (ctx->ct_hflags2 & SMB_FLAGS2_UNICODE) 238*10023SGordon.Ross@Sun.COM ssp_st->ss_flags |= NTLMSSP_NEGOTIATE_UNICODE; 239*10023SGordon.Ross@Sun.COM else 240*10023SGordon.Ross@Sun.COM ssp_st->ss_flags |= NTLMSSP_NEGOTIATE_OEM; 241*10023SGordon.Ross@Sun.COM 242*10023SGordon.Ross@Sun.COM if (ctx->ct_vcflags & SMBV_WILL_SIGN) { 243*10023SGordon.Ross@Sun.COM ssp_st->ss_flags |= NTLMSSP_NEGOTIATE_ALWAYS_SIGN; 244*10023SGordon.Ross@Sun.COM ctx->ct_hflags2 |= SMB_FLAGS2_SECURITY_SIGNATURE; 245*10023SGordon.Ross@Sun.COM } 246*10023SGordon.Ross@Sun.COM 247*10023SGordon.Ross@Sun.COM bcopy(ntlmssp_id, &hdr.h_id, ID_SZ); 248*10023SGordon.Ross@Sun.COM hdr.h_type = 1; /* Type 1 */ 249*10023SGordon.Ross@Sun.COM hdr.h_flags = ssp_st->ss_flags; 250*10023SGordon.Ross@Sun.COM 251*10023SGordon.Ross@Sun.COM /* 252*10023SGordon.Ross@Sun.COM * Put the client domain, client name strings. 253*10023SGordon.Ross@Sun.COM * These are always in OEM format, upper-case. 254*10023SGordon.Ross@Sun.COM */ 255*10023SGordon.Ross@Sun.COM ucdom = utf8_str_toupper(ctx->ct_domain); 256*10023SGordon.Ross@Sun.COM ucwks = utf8_str_toupper(ctx->ct_locname); 257*10023SGordon.Ross@Sun.COM if (ucdom == NULL || ucwks == NULL) { 258*10023SGordon.Ross@Sun.COM err = ENOMEM; 259*10023SGordon.Ross@Sun.COM goto out; 260*10023SGordon.Ross@Sun.COM } 261*10023SGordon.Ross@Sun.COM err = mb_put_sb_string(&mb2, &hdr.h_cldom, ucdom, 0); 262*10023SGordon.Ross@Sun.COM if (err) 263*10023SGordon.Ross@Sun.COM goto out; 264*10023SGordon.Ross@Sun.COM err = mb_put_sb_string(&mb2, &hdr.h_wksta, ucwks, 0); 265*10023SGordon.Ross@Sun.COM if (err) 266*10023SGordon.Ross@Sun.COM goto out; 267*10023SGordon.Ross@Sun.COM 268*10023SGordon.Ross@Sun.COM /* 269*10023SGordon.Ross@Sun.COM * Marshal the header (in LE order) 270*10023SGordon.Ross@Sun.COM * then concatenate the 2nd part. 271*10023SGordon.Ross@Sun.COM */ 272*10023SGordon.Ross@Sun.COM (void) mb_put_mem(out_mb, &hdr.h_id, ID_SZ); 273*10023SGordon.Ross@Sun.COM (void) mb_put_uint32le(out_mb, hdr.h_type); 274*10023SGordon.Ross@Sun.COM (void) mb_put_uint32le(out_mb, hdr.h_flags); 275*10023SGordon.Ross@Sun.COM (void) mb_put_sb_hdr(out_mb, &hdr.h_cldom); 276*10023SGordon.Ross@Sun.COM (void) mb_put_sb_hdr(out_mb, &hdr.h_wksta); 277*10023SGordon.Ross@Sun.COM 278*10023SGordon.Ross@Sun.COM err = mb_put_mbuf(out_mb, mb2.mb_top); 279*10023SGordon.Ross@Sun.COM 280*10023SGordon.Ross@Sun.COM out: 281*10023SGordon.Ross@Sun.COM free(ucdom); 282*10023SGordon.Ross@Sun.COM free(ucwks); 283*10023SGordon.Ross@Sun.COM 284*10023SGordon.Ross@Sun.COM return (err); 285*10023SGordon.Ross@Sun.COM } 286*10023SGordon.Ross@Sun.COM 287*10023SGordon.Ross@Sun.COM /* 288*10023SGordon.Ross@Sun.COM * Parse a Type 2 message 289*10023SGordon.Ross@Sun.COM */ 290*10023SGordon.Ross@Sun.COM int 291*10023SGordon.Ross@Sun.COM ntlmssp_get_type2(struct ssp_ctx *sp, struct mbdata *in_mb) 292*10023SGordon.Ross@Sun.COM { 293*10023SGordon.Ross@Sun.COM struct type2hdr { 294*10023SGordon.Ross@Sun.COM char h_id[ID_SZ]; 295*10023SGordon.Ross@Sun.COM uint32_t h_type; 296*10023SGordon.Ross@Sun.COM struct sec_buf h_target_name; 297*10023SGordon.Ross@Sun.COM uint32_t h_flags; 298*10023SGordon.Ross@Sun.COM uint8_t h_challenge[8]; 299*10023SGordon.Ross@Sun.COM uint32_t h_context[2]; /* optional */ 300*10023SGordon.Ross@Sun.COM struct sec_buf h_target_info; /* optional */ 301*10023SGordon.Ross@Sun.COM } hdr; 302*10023SGordon.Ross@Sun.COM struct mbdata top_mb, tmp_mb; 303*10023SGordon.Ross@Sun.COM struct mbuf *m; 304*10023SGordon.Ross@Sun.COM int err, uc; 305*10023SGordon.Ross@Sun.COM int min_hdr_sz = offsetof(struct type2hdr, h_context); 306*10023SGordon.Ross@Sun.COM struct smb_ctx *ctx = sp->smb_ctx; 307*10023SGordon.Ross@Sun.COM ntlmssp_state_t *ssp_st = sp->sp_private; 308*10023SGordon.Ross@Sun.COM char *buf = NULL; 309*10023SGordon.Ross@Sun.COM 310*10023SGordon.Ross@Sun.COM if (m_totlen(in_mb->mb_top) < min_hdr_sz) { 311*10023SGordon.Ross@Sun.COM err = EBADRPC; 312*10023SGordon.Ross@Sun.COM goto out; 313*10023SGordon.Ross@Sun.COM } 314*10023SGordon.Ross@Sun.COM 315*10023SGordon.Ross@Sun.COM /* 316*10023SGordon.Ross@Sun.COM * Save the mbdata pointers before we consume anything. 317*10023SGordon.Ross@Sun.COM * Careful to NOT free this (would be dup. free) 318*10023SGordon.Ross@Sun.COM * We use this below to find data based on offsets 319*10023SGordon.Ross@Sun.COM * from the start of the header. 320*10023SGordon.Ross@Sun.COM */ 321*10023SGordon.Ross@Sun.COM top_mb = *in_mb; 322*10023SGordon.Ross@Sun.COM 323*10023SGordon.Ross@Sun.COM /* Parse the fixed size header stuff. */ 324*10023SGordon.Ross@Sun.COM bzero(&hdr, sizeof (hdr)); 325*10023SGordon.Ross@Sun.COM (void) mb_get_mem(in_mb, &hdr.h_id, ID_SZ); 326*10023SGordon.Ross@Sun.COM (void) mb_get_uint32le(in_mb, &hdr.h_type); 327*10023SGordon.Ross@Sun.COM if (hdr.h_type != 2) { 328*10023SGordon.Ross@Sun.COM err = EPROTO; 329*10023SGordon.Ross@Sun.COM goto out; 330*10023SGordon.Ross@Sun.COM } 331*10023SGordon.Ross@Sun.COM (void) mb_get_sb_hdr(in_mb, &hdr.h_target_name); 332*10023SGordon.Ross@Sun.COM (void) mb_get_uint32le(in_mb, &hdr.h_flags); 333*10023SGordon.Ross@Sun.COM (void) mb_get_mem(in_mb, &hdr.h_challenge, NTLM_CHAL_SZ); 334*10023SGordon.Ross@Sun.COM 335*10023SGordon.Ross@Sun.COM /* 336*10023SGordon.Ross@Sun.COM * Save flags, challenge for later. 337*10023SGordon.Ross@Sun.COM */ 338*10023SGordon.Ross@Sun.COM ssp_st->ss_flags = hdr.h_flags; 339*10023SGordon.Ross@Sun.COM uc = hdr.h_flags & NTLMSSP_NEGOTIATE_UNICODE; 340*10023SGordon.Ross@Sun.COM bcopy(&hdr.h_challenge, ctx->ct_ntlm_chal, NTLM_CHAL_SZ); 341*10023SGordon.Ross@Sun.COM 342*10023SGordon.Ross@Sun.COM /* 343*10023SGordon.Ross@Sun.COM * Now find out if the optional parts are there. 344*10023SGordon.Ross@Sun.COM */ 345*10023SGordon.Ross@Sun.COM if ((m_totlen(top_mb.mb_top) > sizeof (hdr)) && 346*10023SGordon.Ross@Sun.COM (hdr.h_target_name.sb_offset >= sizeof (hdr))) { 347*10023SGordon.Ross@Sun.COM (void) mb_get_uint32le(in_mb, &hdr.h_context[0]); 348*10023SGordon.Ross@Sun.COM (void) mb_get_uint32le(in_mb, &hdr.h_context[1]); 349*10023SGordon.Ross@Sun.COM (void) mb_get_sb_hdr(in_mb, &hdr.h_target_info); 350*10023SGordon.Ross@Sun.COM } 351*10023SGordon.Ross@Sun.COM 352*10023SGordon.Ross@Sun.COM /* 353*10023SGordon.Ross@Sun.COM * Get the target name string. First get a copy of 354*10023SGordon.Ross@Sun.COM * the data from the offset/length indicated in the 355*10023SGordon.Ross@Sun.COM * security buffer header; then parse the string. 356*10023SGordon.Ross@Sun.COM */ 357*10023SGordon.Ross@Sun.COM err = mb_get_sb_data(&top_mb, &hdr.h_target_name, &m); 358*10023SGordon.Ross@Sun.COM if (err) 359*10023SGordon.Ross@Sun.COM goto out; 360*10023SGordon.Ross@Sun.COM mb_initm(&tmp_mb, m); 361*10023SGordon.Ross@Sun.COM err = mb_get_string(&tmp_mb, &ssp_st->ss_target_name, uc); 362*10023SGordon.Ross@Sun.COM mb_done(&tmp_mb); 363*10023SGordon.Ross@Sun.COM 364*10023SGordon.Ross@Sun.COM /* 365*10023SGordon.Ross@Sun.COM * Get the target info blob, if present. 366*10023SGordon.Ross@Sun.COM */ 367*10023SGordon.Ross@Sun.COM if (hdr.h_target_info.sb_offset >= sizeof (hdr)) { 368*10023SGordon.Ross@Sun.COM err = mb_get_sb_data(&top_mb, &hdr.h_target_info, 369*10023SGordon.Ross@Sun.COM &ssp_st->ss_target_info); 370*10023SGordon.Ross@Sun.COM } 371*10023SGordon.Ross@Sun.COM 372*10023SGordon.Ross@Sun.COM out: 373*10023SGordon.Ross@Sun.COM if (buf != NULL) 374*10023SGordon.Ross@Sun.COM free(buf); 375*10023SGordon.Ross@Sun.COM 376*10023SGordon.Ross@Sun.COM return (err); 377*10023SGordon.Ross@Sun.COM } 378*10023SGordon.Ross@Sun.COM 379*10023SGordon.Ross@Sun.COM /* 380*10023SGordon.Ross@Sun.COM * Build a Type 3 message 381*10023SGordon.Ross@Sun.COM * 382*10023SGordon.Ross@Sun.COM * This message has a header section containing offsets to 383*10023SGordon.Ross@Sun.COM * data later in the message. We use the common trick of 384*10023SGordon.Ross@Sun.COM * building it in two parts and then concatenatening. 385*10023SGordon.Ross@Sun.COM */ 386*10023SGordon.Ross@Sun.COM int 387*10023SGordon.Ross@Sun.COM ntlmssp_put_type3(struct ssp_ctx *sp, struct mbdata *out_mb) 388*10023SGordon.Ross@Sun.COM { 389*10023SGordon.Ross@Sun.COM struct type3hdr { 390*10023SGordon.Ross@Sun.COM char h_id[ID_SZ]; 391*10023SGordon.Ross@Sun.COM uint32_t h_type; 392*10023SGordon.Ross@Sun.COM struct sec_buf h_lm_resp; 393*10023SGordon.Ross@Sun.COM struct sec_buf h_nt_resp; 394*10023SGordon.Ross@Sun.COM struct sec_buf h_domain; 395*10023SGordon.Ross@Sun.COM struct sec_buf h_user; 396*10023SGordon.Ross@Sun.COM struct sec_buf h_wksta; 397*10023SGordon.Ross@Sun.COM } hdr; 398*10023SGordon.Ross@Sun.COM struct mbdata lm_mbc, nt_mbc, ti_mbc; 399*10023SGordon.Ross@Sun.COM struct mbdata mb2; /* 2nd part */ 400*10023SGordon.Ross@Sun.COM int err, uc; 401*10023SGordon.Ross@Sun.COM char *ucdom = NULL; /* user's domain */ 402*10023SGordon.Ross@Sun.COM char *ucuser = NULL; /* user name */ 403*10023SGordon.Ross@Sun.COM char *ucwksta = NULL; /* workstation */ 404*10023SGordon.Ross@Sun.COM struct smb_ctx *ctx = sp->smb_ctx; 405*10023SGordon.Ross@Sun.COM ntlmssp_state_t *ssp_st = sp->sp_private; 406*10023SGordon.Ross@Sun.COM 407*10023SGordon.Ross@Sun.COM bzero(&lm_mbc, sizeof (lm_mbc)); 408*10023SGordon.Ross@Sun.COM bzero(&nt_mbc, sizeof (nt_mbc)); 409*10023SGordon.Ross@Sun.COM bzero(&ti_mbc, sizeof (ti_mbc)); 410*10023SGordon.Ross@Sun.COM bzero(&mb2, sizeof (mb2)); 411*10023SGordon.Ross@Sun.COM 412*10023SGordon.Ross@Sun.COM /* 413*10023SGordon.Ross@Sun.COM * Convert the user name to upper-case, as that's what's 414*10023SGordon.Ross@Sun.COM * used when computing LMv2 and NTLMv2 responses. Also 415*10023SGordon.Ross@Sun.COM * domain, workstation 416*10023SGordon.Ross@Sun.COM */ 417*10023SGordon.Ross@Sun.COM ucdom = utf8_str_toupper(ctx->ct_domain); 418*10023SGordon.Ross@Sun.COM ucuser = utf8_str_toupper(ctx->ct_user); 419*10023SGordon.Ross@Sun.COM ucwksta = utf8_str_toupper(ctx->ct_locname); 420*10023SGordon.Ross@Sun.COM if (ucdom == NULL || ucuser == NULL || ucwksta == NULL) { 421*10023SGordon.Ross@Sun.COM err = ENOMEM; 422*10023SGordon.Ross@Sun.COM goto out; 423*10023SGordon.Ross@Sun.COM } 424*10023SGordon.Ross@Sun.COM 425*10023SGordon.Ross@Sun.COM if ((err = mb_init(&mb2, M_MINSIZE)) != 0) 426*10023SGordon.Ross@Sun.COM goto out; 427*10023SGordon.Ross@Sun.COM mb2.mb_count = sizeof (hdr); 428*10023SGordon.Ross@Sun.COM uc = ssp_st->ss_flags & NTLMSSP_NEGOTIATE_UNICODE; 429*10023SGordon.Ross@Sun.COM 430*10023SGordon.Ross@Sun.COM bcopy(ntlmssp_id, &hdr.h_id, ID_SZ); 431*10023SGordon.Ross@Sun.COM hdr.h_type = 3; /* Type 3 */ 432*10023SGordon.Ross@Sun.COM 433*10023SGordon.Ross@Sun.COM /* 434*10023SGordon.Ross@Sun.COM * Put the LMv2,NTLMv2 responses, or 435*10023SGordon.Ross@Sun.COM * possibly LM, NTLM (v1) responses. 436*10023SGordon.Ross@Sun.COM */ 437*10023SGordon.Ross@Sun.COM if (ctx->ct_authflags & SMB_AT_NTLM2) { 438*10023SGordon.Ross@Sun.COM /* Build the NTLMv2 "target info" blob. */ 439*10023SGordon.Ross@Sun.COM err = ntlm_build_target_info(ctx, 440*10023SGordon.Ross@Sun.COM ssp_st->ss_target_info, &ti_mbc); 441*10023SGordon.Ross@Sun.COM if (err) 442*10023SGordon.Ross@Sun.COM goto out; 443*10023SGordon.Ross@Sun.COM err = ntlm_put_v2_responses(ctx, &ti_mbc, 444*10023SGordon.Ross@Sun.COM &lm_mbc, &nt_mbc); 445*10023SGordon.Ross@Sun.COM } else { 446*10023SGordon.Ross@Sun.COM err = ntlm_put_v1_responses(ctx, 447*10023SGordon.Ross@Sun.COM &lm_mbc, &nt_mbc); 448*10023SGordon.Ross@Sun.COM } 449*10023SGordon.Ross@Sun.COM if (err) 450*10023SGordon.Ross@Sun.COM goto out; 451*10023SGordon.Ross@Sun.COM 452*10023SGordon.Ross@Sun.COM err = mb_put_sb_data(&mb2, &hdr.h_lm_resp, lm_mbc.mb_top); 453*10023SGordon.Ross@Sun.COM lm_mbc.mb_top = NULL; /* consumed */ 454*10023SGordon.Ross@Sun.COM if (err) 455*10023SGordon.Ross@Sun.COM goto out; 456*10023SGordon.Ross@Sun.COM err = mb_put_sb_data(&mb2, &hdr.h_nt_resp, nt_mbc.mb_top); 457*10023SGordon.Ross@Sun.COM nt_mbc.mb_top = NULL; /* consumed */ 458*10023SGordon.Ross@Sun.COM if (err) 459*10023SGordon.Ross@Sun.COM goto out; 460*10023SGordon.Ross@Sun.COM 461*10023SGordon.Ross@Sun.COM /* 462*10023SGordon.Ross@Sun.COM * Put the "target" (domain), user, workstation 463*10023SGordon.Ross@Sun.COM */ 464*10023SGordon.Ross@Sun.COM err = mb_put_sb_string(&mb2, &hdr.h_domain, ucdom, uc); 465*10023SGordon.Ross@Sun.COM if (err) 466*10023SGordon.Ross@Sun.COM goto out; 467*10023SGordon.Ross@Sun.COM err = mb_put_sb_string(&mb2, &hdr.h_user, ucuser, uc); 468*10023SGordon.Ross@Sun.COM if (err) 469*10023SGordon.Ross@Sun.COM goto out; 470*10023SGordon.Ross@Sun.COM err = mb_put_sb_string(&mb2, &hdr.h_wksta, ucwksta, uc); 471*10023SGordon.Ross@Sun.COM if (err) 472*10023SGordon.Ross@Sun.COM goto out; 473*10023SGordon.Ross@Sun.COM 474*10023SGordon.Ross@Sun.COM /* 475*10023SGordon.Ross@Sun.COM * Marshal the header (in LE order) 476*10023SGordon.Ross@Sun.COM * then concatenate the 2nd part. 477*10023SGordon.Ross@Sun.COM */ 478*10023SGordon.Ross@Sun.COM (void) mb_put_mem(out_mb, &hdr.h_id, ID_SZ); 479*10023SGordon.Ross@Sun.COM (void) mb_put_uint32le(out_mb, hdr.h_type); 480*10023SGordon.Ross@Sun.COM 481*10023SGordon.Ross@Sun.COM (void) mb_put_sb_hdr(out_mb, &hdr.h_lm_resp); 482*10023SGordon.Ross@Sun.COM (void) mb_put_sb_hdr(out_mb, &hdr.h_nt_resp); 483*10023SGordon.Ross@Sun.COM 484*10023SGordon.Ross@Sun.COM (void) mb_put_sb_hdr(out_mb, &hdr.h_domain); 485*10023SGordon.Ross@Sun.COM (void) mb_put_sb_hdr(out_mb, &hdr.h_user); 486*10023SGordon.Ross@Sun.COM (void) mb_put_sb_hdr(out_mb, &hdr.h_wksta); 487*10023SGordon.Ross@Sun.COM 488*10023SGordon.Ross@Sun.COM err = mb_put_mbuf(out_mb, mb2.mb_top); 489*10023SGordon.Ross@Sun.COM mb2.mb_top = NULL; /* consumed */ 490*10023SGordon.Ross@Sun.COM 491*10023SGordon.Ross@Sun.COM out: 492*10023SGordon.Ross@Sun.COM free(ucdom); 493*10023SGordon.Ross@Sun.COM free(ucuser); 494*10023SGordon.Ross@Sun.COM free(ucwksta); 495*10023SGordon.Ross@Sun.COM 496*10023SGordon.Ross@Sun.COM mb_done(&mb2); 497*10023SGordon.Ross@Sun.COM mb_done(&lm_mbc); 498*10023SGordon.Ross@Sun.COM mb_done(&nt_mbc); 499*10023SGordon.Ross@Sun.COM 500*10023SGordon.Ross@Sun.COM return (err); 501*10023SGordon.Ross@Sun.COM } 502*10023SGordon.Ross@Sun.COM 503*10023SGordon.Ross@Sun.COM /* 504*10023SGordon.Ross@Sun.COM * ntlmssp_final 505*10023SGordon.Ross@Sun.COM * 506*10023SGordon.Ross@Sun.COM * Called after successful authentication. 507*10023SGordon.Ross@Sun.COM * Setup the MAC key for signing. 508*10023SGordon.Ross@Sun.COM */ 509*10023SGordon.Ross@Sun.COM int 510*10023SGordon.Ross@Sun.COM ntlmssp_final(struct ssp_ctx *sp) 511*10023SGordon.Ross@Sun.COM { 512*10023SGordon.Ross@Sun.COM struct smb_ctx *ctx = sp->smb_ctx; 513*10023SGordon.Ross@Sun.COM int err = 0; 514*10023SGordon.Ross@Sun.COM 515*10023SGordon.Ross@Sun.COM /* 516*10023SGordon.Ross@Sun.COM * MAC_key is just the session key, but 517*10023SGordon.Ross@Sun.COM * Only on the first successful auth. 518*10023SGordon.Ross@Sun.COM */ 519*10023SGordon.Ross@Sun.COM if ((ctx->ct_hflags2 & SMB_FLAGS2_SECURITY_SIGNATURE) && 520*10023SGordon.Ross@Sun.COM (ctx->ct_mackey == NULL)) { 521*10023SGordon.Ross@Sun.COM ctx->ct_mackeylen = NTLM_HASH_SZ; 522*10023SGordon.Ross@Sun.COM ctx->ct_mackey = malloc(ctx->ct_mackeylen); 523*10023SGordon.Ross@Sun.COM if (ctx->ct_mackey == NULL) { 524*10023SGordon.Ross@Sun.COM ctx->ct_mackeylen = 0; 525*10023SGordon.Ross@Sun.COM err = ENOMEM; 526*10023SGordon.Ross@Sun.COM goto out; 527*10023SGordon.Ross@Sun.COM } 528*10023SGordon.Ross@Sun.COM memcpy(ctx->ct_mackey, ctx->ct_ssn_key, NTLM_HASH_SZ); 529*10023SGordon.Ross@Sun.COM /* 530*10023SGordon.Ross@Sun.COM * Apparently, the server used seq. no. zero 531*10023SGordon.Ross@Sun.COM * for our previous message, so next is two. 532*10023SGordon.Ross@Sun.COM */ 533*10023SGordon.Ross@Sun.COM ctx->ct_mac_seqno = 2; 534*10023SGordon.Ross@Sun.COM } 535*10023SGordon.Ross@Sun.COM 536*10023SGordon.Ross@Sun.COM out: 537*10023SGordon.Ross@Sun.COM return (err); 538*10023SGordon.Ross@Sun.COM } 539*10023SGordon.Ross@Sun.COM 540*10023SGordon.Ross@Sun.COM /* 541*10023SGordon.Ross@Sun.COM * ntlmssp_next_token 542*10023SGordon.Ross@Sun.COM * 543*10023SGordon.Ross@Sun.COM * See ssp.c: ssp_ctx_next_token 544*10023SGordon.Ross@Sun.COM */ 545*10023SGordon.Ross@Sun.COM int 546*10023SGordon.Ross@Sun.COM ntlmssp_next_token(struct ssp_ctx *sp, struct mbdata *in_mb, 547*10023SGordon.Ross@Sun.COM struct mbdata *out_mb) 548*10023SGordon.Ross@Sun.COM { 549*10023SGordon.Ross@Sun.COM int err; 550*10023SGordon.Ross@Sun.COM 551*10023SGordon.Ross@Sun.COM if (out_mb == NULL) { 552*10023SGordon.Ross@Sun.COM /* final call on successful auth. */ 553*10023SGordon.Ross@Sun.COM err = ntlmssp_final(sp); 554*10023SGordon.Ross@Sun.COM goto out; 555*10023SGordon.Ross@Sun.COM } 556*10023SGordon.Ross@Sun.COM 557*10023SGordon.Ross@Sun.COM /* Will build an ouptut token. */ 558*10023SGordon.Ross@Sun.COM err = mb_init(out_mb, M_MINSIZE); 559*10023SGordon.Ross@Sun.COM if (err) 560*10023SGordon.Ross@Sun.COM goto out; 561*10023SGordon.Ross@Sun.COM 562*10023SGordon.Ross@Sun.COM /* 563*10023SGordon.Ross@Sun.COM * When called with in_mb == NULL, it means 564*10023SGordon.Ross@Sun.COM * this is the first call for this session, 565*10023SGordon.Ross@Sun.COM * so put a Type 1 (initialize) token. 566*10023SGordon.Ross@Sun.COM */ 567*10023SGordon.Ross@Sun.COM if (in_mb == NULL) { 568*10023SGordon.Ross@Sun.COM err = ntlmssp_put_type1(sp, out_mb); 569*10023SGordon.Ross@Sun.COM goto out; 570*10023SGordon.Ross@Sun.COM } 571*10023SGordon.Ross@Sun.COM 572*10023SGordon.Ross@Sun.COM /* 573*10023SGordon.Ross@Sun.COM * This is not the first call, so 574*10023SGordon.Ross@Sun.COM * parse the response token we received. 575*10023SGordon.Ross@Sun.COM * It should be a Type 2 (challenge). 576*10023SGordon.Ross@Sun.COM * Then put a Type 3 (authenticate) 577*10023SGordon.Ross@Sun.COM */ 578*10023SGordon.Ross@Sun.COM err = ntlmssp_get_type2(sp, in_mb); 579*10023SGordon.Ross@Sun.COM if (err) 580*10023SGordon.Ross@Sun.COM goto out; 581*10023SGordon.Ross@Sun.COM 582*10023SGordon.Ross@Sun.COM err = ntlmssp_put_type3(sp, out_mb); 583*10023SGordon.Ross@Sun.COM 584*10023SGordon.Ross@Sun.COM out: 585*10023SGordon.Ross@Sun.COM if (err) 586*10023SGordon.Ross@Sun.COM DPRINT("ret: %d", err); 587*10023SGordon.Ross@Sun.COM return (err); 588*10023SGordon.Ross@Sun.COM } 589*10023SGordon.Ross@Sun.COM 590*10023SGordon.Ross@Sun.COM /* 591*10023SGordon.Ross@Sun.COM * ntlmssp_ctx_destroy 592*10023SGordon.Ross@Sun.COM * 593*10023SGordon.Ross@Sun.COM * Destroy mechanism-specific data. 594*10023SGordon.Ross@Sun.COM */ 595*10023SGordon.Ross@Sun.COM void 596*10023SGordon.Ross@Sun.COM ntlmssp_destroy(struct ssp_ctx *sp) 597*10023SGordon.Ross@Sun.COM { 598*10023SGordon.Ross@Sun.COM ntlmssp_state_t *ssp_st; 599*10023SGordon.Ross@Sun.COM 600*10023SGordon.Ross@Sun.COM ssp_st = sp->sp_private; 601*10023SGordon.Ross@Sun.COM if (ssp_st != NULL) { 602*10023SGordon.Ross@Sun.COM sp->sp_private = NULL; 603*10023SGordon.Ross@Sun.COM free(ssp_st->ss_target_name); 604*10023SGordon.Ross@Sun.COM m_freem(ssp_st->ss_target_info); 605*10023SGordon.Ross@Sun.COM free(ssp_st); 606*10023SGordon.Ross@Sun.COM } 607*10023SGordon.Ross@Sun.COM } 608*10023SGordon.Ross@Sun.COM 609*10023SGordon.Ross@Sun.COM /* 610*10023SGordon.Ross@Sun.COM * ntlmssp_init_clnt 611*10023SGordon.Ross@Sun.COM * 612*10023SGordon.Ross@Sun.COM * Initialize a new NTLMSSP client context. 613*10023SGordon.Ross@Sun.COM */ 614*10023SGordon.Ross@Sun.COM int 615*10023SGordon.Ross@Sun.COM ntlmssp_init_client(struct ssp_ctx *sp) 616*10023SGordon.Ross@Sun.COM { 617*10023SGordon.Ross@Sun.COM ntlmssp_state_t *ssp_st; 618*10023SGordon.Ross@Sun.COM 619*10023SGordon.Ross@Sun.COM if ((sp->smb_ctx->ct_authflags & 620*10023SGordon.Ross@Sun.COM (SMB_AT_NTLM2 | SMB_AT_NTLM1)) == 0) { 621*10023SGordon.Ross@Sun.COM DPRINT("No NTLM authflags"); 622*10023SGordon.Ross@Sun.COM return (ENOTSUP); 623*10023SGordon.Ross@Sun.COM } 624*10023SGordon.Ross@Sun.COM 625*10023SGordon.Ross@Sun.COM ssp_st = calloc(1, sizeof (*ssp_st)); 626*10023SGordon.Ross@Sun.COM if (ssp_st == NULL) 627*10023SGordon.Ross@Sun.COM return (ENOMEM); 628*10023SGordon.Ross@Sun.COM 629*10023SGordon.Ross@Sun.COM sp->sp_nexttok = ntlmssp_next_token; 630*10023SGordon.Ross@Sun.COM sp->sp_destroy = ntlmssp_destroy; 631*10023SGordon.Ross@Sun.COM sp->sp_private = ssp_st; 632*10023SGordon.Ross@Sun.COM 633*10023SGordon.Ross@Sun.COM return (0); 634*10023SGordon.Ross@Sun.COM } 635