2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A
2N/A/*
2N/A * Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A/*
2N/A * NT Lan Manager Security Support Provider (NTLMSSP)
2N/A *
2N/A * Based on information from the "Davenport NTLM" page:
2N/A * http://davenport.sourceforge.net/ntlm.html
2N/A */
2N/A
2N/A
2N/A#include <errno.h>
2N/A#include <stdio.h>
2N/A#include <stddef.h>
2N/A#include <stdlib.h>
2N/A#include <unistd.h>
2N/A#include <strings.h>
2N/A#include <netdb.h>
2N/A#include <libintl.h>
2N/A#include <xti.h>
2N/A#include <assert.h>
2N/A
2N/A#include <sys/types.h>
2N/A#include <sys/time.h>
2N/A#include <sys/byteorder.h>
2N/A#include <sys/socket.h>
2N/A#include <sys/fcntl.h>
2N/A
2N/A#include <netinet/in.h>
2N/A#include <netinet/tcp.h>
2N/A#include <arpa/inet.h>
2N/A
2N/A#include <smb/smb.h>
2N/A#include "smbfs_lib.h"
2N/A#include <netsmb/mchain.h>
2N/A#include <smb/spnego.h>
2N/A#include <smb/ntlmssp.h>
2N/A#include "smbfs_private.h"
2N/A#include "smbfs_charsets.h"
2N/A#include "smbfs_derparse.h"
2N/A#include "smbfs_ssp.h"
2N/A#include "smbfs_ntlm.h"
2N/A
2N/Atypedef struct ntlmssp_state {
2N/A uint32_t ss_flags;
2N/A char *ss_target_name;
2N/A struct mbuf *ss_target_info;
2N/A} ntlmssp_state_t;
2N/A
2N/A/*
2N/A * So called "security buffer".
2N/A * A lot like an RPC string.
2N/A */
2N/Astruct sec_buf {
2N/A uint16_t sb_length;
2N/A uint16_t sb_maxlen;
2N/A uint32_t sb_offset;
2N/A};
2N/A#define ID_SZ 8
2N/Astatic const char ntlmssp_id[ID_SZ] = "NTLMSSP";
2N/A
2N/A/*
2N/A * Get a "security buffer" (header part)
2N/A */
2N/Astatic int
2N/Asmbfs_md_get_sb_hdr(struct mbdata *mbp, struct sec_buf *sb)
2N/A{
2N/A int err;
2N/A
2N/A (void) smbfs_md_get_uint16le(mbp, &sb->sb_length);
2N/A (void) smbfs_md_get_uint16le(mbp, &sb->sb_maxlen);
2N/A err = smbfs_md_get_uint32le(mbp, &sb->sb_offset);
2N/A
2N/A return (err);
2N/A}
2N/A
2N/A/*
2N/A * Get a "security buffer" (data part), where
2N/A * the data is delivered as an mbuf.
2N/A */
2N/Astatic int
2N/Asmbfs_md_get_sb_data(struct mbdata *mbp, struct sec_buf *sb, struct mbuf **mp)
2N/A{
2N/A struct mbdata tmp_mb;
2N/A int err;
2N/A
2N/A /*
2N/A * Setup tmp_mb to point to the start of the header.
2N/A * This is a dup ref - do NOT free it.
2N/A */
2N/A smbfs_mb_initm(&tmp_mb, mbp->mb_top);
2N/A
2N/A /* Skip data up to the offset. */
2N/A err = smbfs_md_get_mem(&tmp_mb, NULL, sb->sb_offset, MB_MSYSTEM);
2N/A if (err)
2N/A return (err);
2N/A
2N/A /* Get the data (as an mbuf). */
2N/A err = smbfs_md_get_mbuf(&tmp_mb, sb->sb_maxlen, mp);
2N/A
2N/A return (err);
2N/A}
2N/A
2N/A/*
2N/A * Put a "security buffer" (header part)
2N/A */
2N/Astatic int
2N/Asmbfs_mb_put_sb_hdr(struct mbdata *mbp, struct sec_buf *sb)
2N/A{
2N/A int err;
2N/A
2N/A (void) smbfs_mb_put_uint16le(mbp, sb->sb_length);
2N/A (void) smbfs_mb_put_uint16le(mbp, sb->sb_maxlen);
2N/A err = smbfs_mb_put_uint32le(mbp, sb->sb_offset);
2N/A
2N/A return (err);
2N/A}
2N/A
2N/A/*
2N/A * Put a "security buffer" (data part), where
2N/A * the data is an mbuf. Note: consumes m.
2N/A */
2N/Astatic int
2N/Asmbfs_mb_put_sb_data(struct mbdata *mbp, struct sec_buf *sb, struct mbuf *m)
2N/A{
2N/A int cnt0, err;
2N/A
2N/A sb->sb_offset = cnt0 = mbp->mb_count;
2N/A err = smbfs_mb_put_mbuf(mbp, m);
2N/A sb->sb_maxlen = sb->sb_length = mbp->mb_count - cnt0;
2N/A
2N/A return (err);
2N/A}
2N/A
2N/A/*
2N/A * Put a "security buffer" (data part), where
2N/A * the data is a string (OEM or unicode).
2N/A *
2N/A * The string is NOT null terminated.
2N/A */
2N/Astatic int
2N/Asmbfs_mb_put_sb_string(struct mbdata *mbp, struct sec_buf *sb,
2N/A const char *s, int unicode)
2N/A{
2N/A int err, trim;
2N/A struct mbdata tmp_mb;
2N/A
2N/A /*
2N/A * Put the string into a temp. mbuf,
2N/A * then chop off the null terminator
2N/A * before appending to caller's mbp.
2N/A */
2N/A err = smbfs_mb_init(&tmp_mb);
2N/A if (err)
2N/A return (err);
2N/A
2N/A err = smbfs_mb_put_string(&tmp_mb, s, unicode);
2N/A if (err) {
2N/A smbfs_mb_done(&tmp_mb);
2N/A return (err);
2N/A }
2N/A
2N/A trim = (unicode) ? 2 : 1;
2N/A if (tmp_mb.mb_cur->m_len < trim)
2N/A return (EFAULT);
2N/A tmp_mb.mb_cur->m_len -= trim;
2N/A
2N/A err = smbfs_mb_put_sb_data(mbp, sb, tmp_mb.mb_top);
2N/A /*
2N/A * Note: tmp_mb.mb_top has been consumed,
2N/A * so do NOT free it (no mb_done)
2N/A */
2N/A return (err);
2N/A}
2N/A
2N/A/*
2N/A * Build a Type 1 message
2N/A *
2N/A * This message has a header section containing offsets to
2N/A * data later in the message. We use the common trick of
2N/A * building it in two parts and then concatenatening.
2N/A */
2N/Astatic int
2N/Asmbfs_ntlmssp_put_type1(struct ssp_ctx *sp, struct mbdata *out_mb)
2N/A{
2N/A struct type1hdr {
2N/A char h_id[ID_SZ];
2N/A uint32_t h_type;
2N/A uint32_t h_flags;
2N/A struct sec_buf h_cldom;
2N/A struct sec_buf h_wksta;
2N/A } hdr;
2N/A struct mbdata mb2; /* 2nd part */
2N/A int err;
2N/A struct smb_ctx *ctx = sp->smb_ctx;
2N/A ntlmssp_state_t *ssp_st = sp->sp_private;
2N/A char *oem_domain = NULL;
2N/A char *oem_workstation = NULL;
2N/A
2N/A bzero(&hdr, sizeof (hdr));
2N/A
2N/A if ((err = smbfs_mb_init(&mb2)) != 0)
2N/A return (err);
2N/A mb2.mb_count = sizeof (hdr);
2N/A
2N/A /*
2N/A * Initialize the negotiation flags, and
2N/A * save what we sent. For reference:
2N/A * [MS-NLMP] spec. (also ntlmssp.h)
2N/A */
2N/A ssp_st->ss_flags =
2N/A NTLMSSP_REQUEST_TARGET |
2N/A NTLMSSP_NEGOTIATE_NTLM |
2N/A NTLMSSP_NEGOTIATE_TARGET_INFO |
2N/A NTLMSSP_NEGOTIATE_128 |
2N/A NTLMSSP_NEGOTIATE_56 |
2N/A NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY;
2N/A
2N/A if (ctx->ct_hflags2 & SMB_FLAGS2_UNICODE)
2N/A ssp_st->ss_flags |= NTLMSSP_NEGOTIATE_UNICODE;
2N/A else
2N/A ssp_st->ss_flags |= NTLMSSP_NEGOTIATE_OEM;
2N/A
2N/A if (ctx->ct_vcflags & SMBV_WILL_SIGN) {
2N/A ssp_st->ss_flags |= NTLMSSP_NEGOTIATE_ALWAYS_SIGN;
2N/A if (ctx->ct_vopt & SMBVOPT_SIGNING_REQUIRED) {
2N/A ctx->ct_hflags2 |=
2N/A SMB_FLAGS2_SMB_SECURITY_SIGNATURE_REQUIRED;
2N/A }
2N/A ctx->ct_hflags2 |= SMB_FLAGS2_SMB_SECURITY_SIGNATURE;
2N/A }
2N/A
2N/A /*
2N/A * The client domain and client name strings
2N/A * are always in OEM format, upper-case.
2N/A */
2N/A if (ctx->ct_domain[0] != '\0')
2N/A oem_domain = smbfs_utf8_str_toupper(ctx->ct_domain);
2N/A
2N/A if (oem_domain != NULL) {
2N/A ssp_st->ss_flags |= NTLMSSP_NEGOTIATE_OEM_DOMAIN_SUPPLIED;
2N/A err = smbfs_mb_put_sb_string(&mb2, &hdr.h_cldom, oem_domain, 0);
2N/A if (err)
2N/A goto out;
2N/A }
2N/A
2N/A if (ctx->ct_locname[0] != '\0')
2N/A oem_workstation = smbfs_utf8_str_toupper(ctx->ct_locname);
2N/A
2N/A if (oem_workstation != NULL) {
2N/A ssp_st->ss_flags |= NTLMSSP_NEGOTIATE_OEM_WORKSTATION_SUPPLIED;
2N/A err = smbfs_mb_put_sb_string(&mb2, &hdr.h_wksta,
2N/A oem_workstation, 0);
2N/A if (err)
2N/A goto out;
2N/A }
2N/A
2N/A bcopy(ntlmssp_id, &hdr.h_id, ID_SZ);
2N/A hdr.h_type = 1; /* Type 1 */
2N/A hdr.h_flags = ssp_st->ss_flags;
2N/A
2N/A /*
2N/A * Marshal the header (in LE order)
2N/A * then concatenate the 2nd part.
2N/A */
2N/A (void) smbfs_mb_put_mem(out_mb, &hdr.h_id, ID_SZ, MB_MSYSTEM);
2N/A (void) smbfs_mb_put_uint32le(out_mb, hdr.h_type);
2N/A (void) smbfs_mb_put_uint32le(out_mb, hdr.h_flags);
2N/A (void) smbfs_mb_put_sb_hdr(out_mb, &hdr.h_cldom);
2N/A (void) smbfs_mb_put_sb_hdr(out_mb, &hdr.h_wksta);
2N/A
2N/A err = smbfs_mb_put_mbuf(out_mb, mb2.mb_top);
2N/A
2N/Aout:
2N/A free(oem_domain);
2N/A free(oem_workstation);
2N/A return (err);
2N/A}
2N/A
2N/A/*
2N/A * Parse a Type 2 message
2N/A */
2N/Astatic int
2N/Asmbfs_ntlmssp_get_type2(struct ssp_ctx *sp, struct mbdata *in_mb)
2N/A{
2N/A struct type2hdr {
2N/A char h_id[ID_SZ];
2N/A uint32_t h_type;
2N/A struct sec_buf h_target_name;
2N/A uint32_t h_flags;
2N/A uint8_t h_challenge[8];
2N/A uint32_t h_context[2]; /* optional */
2N/A struct sec_buf h_target_info; /* optional */
2N/A } hdr;
2N/A struct mbdata top_mb, tmp_mb;
2N/A struct mbuf *m;
2N/A int err, uc;
2N/A int min_hdr_sz = offsetof(struct type2hdr, h_context);
2N/A struct smb_ctx *ctx = sp->smb_ctx;
2N/A ntlmssp_state_t *ssp_st = sp->sp_private;
2N/A char *buf = NULL;
2N/A
2N/A if (smbfs_m_totlen(in_mb->mb_top) < min_hdr_sz) {
2N/A err = EBADRPC;
2N/A goto out;
2N/A }
2N/A
2N/A /*
2N/A * Save the mbdata pointers before we consume anything.
2N/A * Careful to NOT free this (would be dup. free)
2N/A * We use this below to find data based on offsets
2N/A * from the start of the header.
2N/A */
2N/A top_mb = *in_mb;
2N/A
2N/A /* Parse the fixed size header stuff. */
2N/A bzero(&hdr, sizeof (hdr));
2N/A (void) smbfs_md_get_mem(in_mb, &hdr.h_id, ID_SZ, MB_MSYSTEM);
2N/A (void) smbfs_md_get_uint32le(in_mb, &hdr.h_type);
2N/A if (hdr.h_type != 2) {
2N/A err = EPROTO;
2N/A goto out;
2N/A }
2N/A (void) smbfs_md_get_sb_hdr(in_mb, &hdr.h_target_name);
2N/A (void) smbfs_md_get_uint32le(in_mb, &hdr.h_flags);
2N/A (void) smbfs_md_get_mem(in_mb, &hdr.h_challenge, NTLM_CHAL_SZ,
2N/A MB_MSYSTEM);
2N/A
2N/A /*
2N/A * Save flags, challenge for later.
2N/A */
2N/A ssp_st->ss_flags = hdr.h_flags;
2N/A uc = hdr.h_flags & NTLMSSP_NEGOTIATE_UNICODE;
2N/A bcopy(&hdr.h_challenge, ctx->ct_ntlm_chal, NTLM_CHAL_SZ);
2N/A
2N/A /*
2N/A * Now find out if the optional parts are there.
2N/A */
2N/A if ((smbfs_m_totlen(top_mb.mb_top) > sizeof (hdr)) &&
2N/A (hdr.h_target_name.sb_offset >= sizeof (hdr))) {
2N/A (void) smbfs_md_get_uint32le(in_mb, &hdr.h_context[0]);
2N/A (void) smbfs_md_get_uint32le(in_mb, &hdr.h_context[1]);
2N/A (void) smbfs_md_get_sb_hdr(in_mb, &hdr.h_target_info);
2N/A }
2N/A
2N/A /*
2N/A * Get the target name string. First get a copy of
2N/A * the data from the offset/length indicated in the
2N/A * security buffer header; then parse the string.
2N/A */
2N/A err = smbfs_md_get_sb_data(&top_mb, &hdr.h_target_name, &m);
2N/A if (err)
2N/A goto out;
2N/A smbfs_mb_initm(&tmp_mb, m);
2N/A err = smbfs_md_get_string(&tmp_mb, &ssp_st->ss_target_name, uc);
2N/A smbfs_mb_done(&tmp_mb);
2N/A
2N/A /*
2N/A * Get the target info blob, if present.
2N/A */
2N/A if (hdr.h_target_info.sb_offset >= sizeof (hdr)) {
2N/A err = smbfs_md_get_sb_data(&top_mb, &hdr.h_target_info,
2N/A &ssp_st->ss_target_info);
2N/A }
2N/A
2N/Aout:
2N/A if (buf != NULL)
2N/A free(buf);
2N/A
2N/A return (err);
2N/A}
2N/A
2N/A/*
2N/A * Build a Type 3 message
2N/A *
2N/A * This message has a header section containing offsets to
2N/A * data later in the message. We use the common trick of
2N/A * building it in two parts and then concatenatening.
2N/A */
2N/Astatic int
2N/Asmbfs_ntlmssp_put_type3(struct ssp_ctx *sp, struct mbdata *out_mb)
2N/A{
2N/A struct type3hdr {
2N/A char h_id[ID_SZ];
2N/A uint32_t h_type;
2N/A struct sec_buf h_lm_resp;
2N/A struct sec_buf h_nt_resp;
2N/A struct sec_buf h_domain;
2N/A struct sec_buf h_user;
2N/A struct sec_buf h_wksta;
2N/A struct sec_buf h_ssn_key;
2N/A uint32_t h_flags;
2N/A } hdr;
2N/A struct mbdata lm_mbc; /* LM response */
2N/A struct mbdata nt_mbc; /* NT response */
2N/A struct mbdata ti_mbc; /* target info */
2N/A struct mbdata mb2; /* payload */
2N/A int err, uc;
2N/A struct smb_ctx *ctx = sp->smb_ctx;
2N/A ntlmssp_state_t *ssp_st = sp->sp_private;
2N/A
2N/A bzero(&hdr, sizeof (hdr));
2N/A bzero(&lm_mbc, sizeof (lm_mbc));
2N/A bzero(&nt_mbc, sizeof (nt_mbc));
2N/A bzero(&ti_mbc, sizeof (ti_mbc));
2N/A bzero(&mb2, sizeof (mb2));
2N/A
2N/A /*
2N/A * Fill in the NTLMSSP header, etc.
2N/A */
2N/A if ((err = smbfs_mb_init(&mb2)) != 0)
2N/A goto out;
2N/A mb2.mb_count = sizeof (hdr);
2N/A uc = ssp_st->ss_flags & NTLMSSP_NEGOTIATE_UNICODE;
2N/A
2N/A bcopy(ntlmssp_id, &hdr.h_id, ID_SZ);
2N/A hdr.h_type = 3; /* Type 3 */
2N/A hdr.h_flags = ssp_st->ss_flags;
2N/A
2N/A /*
2N/A * Put the LMv2/NTLMv2 responses, the LM/NTLMv1 responses
2N/A * with extended session security or the LM/NTLMv1 responses
2N/A */
2N/A if (ctx->ct_authflags & SMB_AT_NTLM2) {
2N/A /* Build the NTLMv2 "target info" blob. */
2N/A err = smbfs_ntlm_build_target_info(ctx,
2N/A ssp_st->ss_target_info, &ti_mbc);
2N/A if (err)
2N/A goto out;
2N/A err = smbfs_ntlm_put_v2_responses(ctx, &ti_mbc,
2N/A &lm_mbc, &nt_mbc);
2N/A } else if (ssp_st->ss_flags &
2N/A NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY) {
2N/A err = smbfs_ntlm_put_v1ess_responses(ctx,
2N/A &lm_mbc, &nt_mbc);
2N/A } else {
2N/A err = smbfs_ntlm_put_v1_responses(ctx,
2N/A &lm_mbc, &nt_mbc);
2N/A }
2N/A if (err)
2N/A goto out;
2N/A
2N/A err = smbfs_mb_put_sb_data(&mb2, &hdr.h_lm_resp, lm_mbc.mb_top);
2N/A lm_mbc.mb_top = NULL; /* consumed */
2N/A if (err)
2N/A goto out;
2N/A err = smbfs_mb_put_sb_data(&mb2, &hdr.h_nt_resp, nt_mbc.mb_top);
2N/A nt_mbc.mb_top = NULL; /* consumed */
2N/A if (err)
2N/A goto out;
2N/A
2N/A /*
2N/A * Put the "target" (domain), user, workstation
2N/A */
2N/A err = smbfs_mb_put_sb_string(&mb2, &hdr.h_domain, ctx->ct_domain, uc);
2N/A if (err)
2N/A goto out;
2N/A err = smbfs_mb_put_sb_string(&mb2, &hdr.h_user, ctx->ct_user, uc);
2N/A if (err)
2N/A goto out;
2N/A err = smbfs_mb_put_sb_string(&mb2, &hdr.h_wksta, ctx->ct_locname, uc);
2N/A if (err)
2N/A goto out;
2N/A
2N/A /*
2N/A * Put the "Random Session Key". We don't set
2N/A * NTLMSSP_NEGOTIATE_KEY_EXCH, so it's empty.
2N/A * (In-line mb_put_sb_data here.)
2N/A */
2N/A hdr.h_ssn_key.sb_maxlen = hdr.h_ssn_key.sb_length = 0;
2N/A hdr.h_ssn_key.sb_offset = mb2.mb_count;
2N/A
2N/A /*
2N/A * Marshal the header (in LE order)
2N/A * then concatenate the 2nd part.
2N/A */
2N/A (void) smbfs_mb_put_mem(out_mb, &hdr.h_id, ID_SZ, MB_MSYSTEM);
2N/A (void) smbfs_mb_put_uint32le(out_mb, hdr.h_type);
2N/A
2N/A (void) smbfs_mb_put_sb_hdr(out_mb, &hdr.h_lm_resp);
2N/A (void) smbfs_mb_put_sb_hdr(out_mb, &hdr.h_nt_resp);
2N/A
2N/A (void) smbfs_mb_put_sb_hdr(out_mb, &hdr.h_domain);
2N/A (void) smbfs_mb_put_sb_hdr(out_mb, &hdr.h_user);
2N/A (void) smbfs_mb_put_sb_hdr(out_mb, &hdr.h_wksta);
2N/A
2N/A (void) smbfs_mb_put_sb_hdr(out_mb, &hdr.h_ssn_key);
2N/A (void) smbfs_mb_put_uint32le(out_mb, hdr.h_flags);
2N/A
2N/A err = smbfs_mb_put_mbuf(out_mb, mb2.mb_top);
2N/A mb2.mb_top = NULL; /* consumed */
2N/A
2N/Aout:
2N/A smbfs_mb_done(&mb2);
2N/A smbfs_mb_done(&lm_mbc);
2N/A smbfs_mb_done(&nt_mbc);
2N/A smbfs_mb_done(&ti_mbc);
2N/A
2N/A return (err);
2N/A}
2N/A
2N/A/*
2N/A * smbfs_ntlmssp_final
2N/A *
2N/A * Called after successful authentication.
2N/A * Setup the MAC key for signing.
2N/A */
2N/Astatic int
2N/Asmbfs_ntlmssp_final(struct ssp_ctx *sp)
2N/A{
2N/A struct smb_ctx *ctx = sp->smb_ctx;
2N/A int err = 0;
2N/A
2N/A /*
2N/A * MAC_key is just the session key, but
2N/A * Only on the first successful auth.
2N/A */
2N/A if ((ctx->ct_hflags2 & SMB_FLAGS2_SMB_SECURITY_SIGNATURE) &&
2N/A (ctx->ct_mackey == NULL)) {
2N/A ctx->ct_mackeylen = NTLM_HASH_SZ;
2N/A ctx->ct_mackey = malloc(ctx->ct_mackeylen);
2N/A if (ctx->ct_mackey == NULL) {
2N/A ctx->ct_mackeylen = 0;
2N/A err = ENOMEM;
2N/A goto out;
2N/A }
2N/A (void) memcpy(ctx->ct_mackey, ctx->ct_ssn_key, NTLM_HASH_SZ);
2N/A /*
2N/A * Apparently, the server used seq. no. zero
2N/A * for our previous message, so next is two.
2N/A */
2N/A ctx->ct_mac_seqno = 2;
2N/A }
2N/A
2N/Aout:
2N/A return (err);
2N/A}
2N/A
2N/A/*
2N/A * smbfs_ntlmssp_next_token
2N/A *
2N/A * See smbfs_ssp.c: smbfs_ssp_ctx_next_token
2N/A */
2N/Astatic int
2N/Asmbfs_ntlmssp_next_token(struct ssp_ctx *sp, struct mbdata *in_mb,
2N/A struct mbdata *out_mb)
2N/A{
2N/A int err;
2N/A
2N/A if (out_mb == NULL) {
2N/A /* final call on successful auth. */
2N/A err = smbfs_ntlmssp_final(sp);
2N/A goto out;
2N/A }
2N/A
2N/A /* Will build an ouptut token. */
2N/A err = smbfs_mb_init(out_mb);
2N/A if (err)
2N/A goto out;
2N/A
2N/A /*
2N/A * When called with in_mb == NULL, it means
2N/A * this is the first call for this session,
2N/A * so put a Type 1 (initialize) token.
2N/A */
2N/A if (in_mb == NULL) {
2N/A err = smbfs_ntlmssp_put_type1(sp, out_mb);
2N/A goto out;
2N/A }
2N/A
2N/A /*
2N/A * This is not the first call, so
2N/A * parse the response token we received.
2N/A * It should be a Type 2 (challenge).
2N/A * Then put a Type 3 (authenticate)
2N/A */
2N/A err = smbfs_ntlmssp_get_type2(sp, in_mb);
2N/A if (err)
2N/A goto out;
2N/A
2N/A err = smbfs_ntlmssp_put_type3(sp, out_mb);
2N/A
2N/Aout:
2N/A if (err)
2N/A DPRINT("ret: %d", err);
2N/A return (err);
2N/A}
2N/A
2N/A/*
2N/A * smbfs_ntlmssp_ctx_destroy
2N/A *
2N/A * Destroy mechanism-specific data.
2N/A */
2N/Astatic void
2N/Asmbfs_ntlmssp_destroy(struct ssp_ctx *sp)
2N/A{
2N/A ntlmssp_state_t *ssp_st;
2N/A
2N/A ssp_st = sp->sp_private;
2N/A if (ssp_st != NULL) {
2N/A sp->sp_private = NULL;
2N/A free(ssp_st->ss_target_name);
2N/A smbfs_m_freem(ssp_st->ss_target_info);
2N/A free(ssp_st);
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * smbfs_ntlmssp_init_clnt
2N/A *
2N/A * Initialize a new NTLMSSP client context.
2N/A */
2N/Aint
2N/Asmbfs_ntlmssp_init_client(struct ssp_ctx *sp)
2N/A{
2N/A ntlmssp_state_t *ssp_st;
2N/A
2N/A if ((sp->smb_ctx->ct_authflags &
2N/A (SMB_AT_NTLM2 | SMB_AT_NTLM1)) == 0) {
2N/A DPRINT("No NTLM authflags");
2N/A return (ENOTSUP);
2N/A }
2N/A
2N/A ssp_st = calloc(1, sizeof (*ssp_st));
2N/A if (ssp_st == NULL)
2N/A return (ENOMEM);
2N/A
2N/A sp->sp_nexttok = smbfs_ntlmssp_next_token;
2N/A sp->sp_destroy = smbfs_ntlmssp_destroy;
2N/A sp->sp_private = ssp_st;
2N/A
2N/A return (0);
2N/A}