summaryrefslogtreecommitdiff
path: root/usr/src/lib/libsmbfs/smb
diff options
context:
space:
mode:
authorGordon Ross <Gordon.Ross@Sun.COM>2009-07-02 12:58:38 -0400
committerGordon Ross <Gordon.Ross@Sun.COM>2009-07-02 12:58:38 -0400
commit613a2f6ba31e891e3d947a356daf5e563d43c1ce (patch)
tree0f7f3438a5792c05ed156a43e8cd84f25695d7f2 /usr/src/lib/libsmbfs/smb
parentbf73eaa5a8ea69ac16a1e6e7b736f09286d073f9 (diff)
downloadillumos-joyent-613a2f6ba31e891e3d947a356daf5e563d43c1ce.tar.gz
6584198 SMB Client needs authentication improvements
6587713 Need to reconnect after server disconnect --HG-- rename : usr/src/lib/libsmbfs/netsmb/smbfs_isec.h => usr/src/lib/libsmbfs/smb/acl_nt.h
Diffstat (limited to 'usr/src/lib/libsmbfs/smb')
-rw-r--r--usr/src/lib/libsmbfs/smb/acl_api.c6
-rw-r--r--usr/src/lib/libsmbfs/smb/acl_conv.c15
-rw-r--r--usr/src/lib/libsmbfs/smb/acl_nt.h113
-rw-r--r--usr/src/lib/libsmbfs/smb/acl_print.c7
-rw-r--r--usr/src/lib/libsmbfs/smb/cfopt.c25
-rw-r--r--usr/src/lib/libsmbfs/smb/charsets.c2
-rw-r--r--usr/src/lib/libsmbfs/smb/charsets.h4
-rw-r--r--usr/src/lib/libsmbfs/smb/connect.c535
-rw-r--r--usr/src/lib/libsmbfs/smb/crypt.c175
-rw-r--r--usr/src/lib/libsmbfs/smb/ctx.c1880
-rw-r--r--usr/src/lib/libsmbfs/smb/derparse.c16
-rw-r--r--usr/src/lib/libsmbfs/smb/file.c120
-rw-r--r--usr/src/lib/libsmbfs/smb/findvc.c139
-rw-r--r--usr/src/lib/libsmbfs/smb/getaddr.c215
-rw-r--r--usr/src/lib/libsmbfs/smb/iod_cl.c195
-rw-r--r--usr/src/lib/libsmbfs/smb/iod_wk.c176
-rw-r--r--usr/src/lib/libsmbfs/smb/keychain.c204
-rw-r--r--usr/src/lib/libsmbfs/smb/krb5ssp.c535
-rw-r--r--usr/src/lib/libsmbfs/smb/llib-lsmbfs11
-rw-r--r--usr/src/lib/libsmbfs/smb/mapfile-vers32
-rw-r--r--usr/src/lib/libsmbfs/smb/mbuf.c351
-rw-r--r--usr/src/lib/libsmbfs/smb/nb.c108
-rw-r--r--usr/src/lib/libsmbfs/smb/nb_name.c133
-rw-r--r--usr/src/lib/libsmbfs/smb/nb_net.c49
-rw-r--r--usr/src/lib/libsmbfs/smb/nb_ssn.c330
-rw-r--r--usr/src/lib/libsmbfs/smb/nbns_rq.c142
-rw-r--r--usr/src/lib/libsmbfs/smb/negprot.c428
-rw-r--r--usr/src/lib/libsmbfs/smb/netshareenum.c8
-rw-r--r--usr/src/lib/libsmbfs/smb/newvc.c118
-rw-r--r--usr/src/lib/libsmbfs/smb/nls.c41
-rw-r--r--usr/src/lib/libsmbfs/smb/ntlm.c584
-rw-r--r--usr/src/lib/libsmbfs/smb/ntlm.h65
-rw-r--r--usr/src/lib/libsmbfs/smb/ntlmssp.c634
-rw-r--r--usr/src/lib/libsmbfs/smb/ntlmssp.h76
-rw-r--r--usr/src/lib/libsmbfs/smb/print.c51
-rw-r--r--usr/src/lib/libsmbfs/smb/private.h147
-rw-r--r--usr/src/lib/libsmbfs/smb/rap.c5
-rw-r--r--usr/src/lib/libsmbfs/smb/rcfile.c426
-rw-r--r--usr/src/lib/libsmbfs/smb/rcfile_priv.h39
-rw-r--r--usr/src/lib/libsmbfs/smb/rq.c381
-rw-r--r--usr/src/lib/libsmbfs/smb/signing.c265
-rw-r--r--usr/src/lib/libsmbfs/smb/smb_crypt.h49
-rw-r--r--usr/src/lib/libsmbfs/smb/spnegoparse.c4
-rw-r--r--usr/src/lib/libsmbfs/smb/ssnsetup.c519
-rw-r--r--usr/src/lib/libsmbfs/smb/ssp.c395
-rw-r--r--usr/src/lib/libsmbfs/smb/ssp.h56
-rw-r--r--usr/src/lib/libsmbfs/smb/subr.c183
-rw-r--r--usr/src/lib/libsmbfs/smb/ui-sun.c111
-rw-r--r--usr/src/lib/libsmbfs/smb/utf_str.c66
49 files changed, 8037 insertions, 2132 deletions
diff --git a/usr/src/lib/libsmbfs/smb/acl_api.c b/usr/src/lib/libsmbfs/smb/acl_api.c
index 6c8552e75a..3e9d703c99 100644
--- a/usr/src/lib/libsmbfs/smb/acl_api.c
+++ b/usr/src/lib/libsmbfs/smb/acl_api.c
@@ -20,7 +20,7 @@
*/
/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -49,9 +49,11 @@
#include <sys/fs/smbfs_ioctl.h>
+#include <netsmb/smb.h>
#include <netsmb/smb_lib.h>
#include <netsmb/smbfs_acl.h>
-#include <netsmb/smbfs_isec.h>
+
+#include "acl_nt.h"
#include "private.h"
/* Sanity check SD sizes */
diff --git a/usr/src/lib/libsmbfs/smb/acl_conv.c b/usr/src/lib/libsmbfs/smb/acl_conv.c
index ec77c8f3c5..d19b323dfe 100644
--- a/usr/src/lib/libsmbfs/smb/acl_conv.c
+++ b/usr/src/lib/libsmbfs/smb/acl_conv.c
@@ -20,7 +20,7 @@
*/
/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -54,10 +54,13 @@
#include <sys/fs/smbfs_ioctl.h>
+#include <netsmb/mchain.h>
+#include <netsmb/smb.h>
+
#include <netsmb/smb_lib.h>
#include <netsmb/smbfs_acl.h>
-#include <netsmb/smbfs_isec.h>
-#include <netsmb/mchain.h>
+
+#include "acl_nt.h"
#include "private.h"
#ifdef _KERNEL
@@ -167,7 +170,6 @@ errout:
static void
ifree_ace(i_ntace_t *ace)
{
- size_t sz;
if (ace == NULL)
return;
@@ -282,7 +284,6 @@ mb_get_acl(mbdata_t *mbp, i_ntacl_t **aclp)
i_ntace_t **acep;
uint8_t revision;
uint16_t acl_len, acecount;
- uint32_t *subauthp;
size_t aclsz;
int i, error;
@@ -327,10 +328,7 @@ static int
mb_put_acl(mbdata_t *mbp, i_ntacl_t *acl)
{
i_ntace_t **acep;
- uint8_t revision;
uint16_t acl_len, *acl_len_p;
- uint32_t *subauthp;
- size_t aclsz;
int i, cnt0, error;
cnt0 = mbp->mb_count;
@@ -929,6 +927,7 @@ errout:
* Include owner/group too if uid/gid != -1.
* Note optional arg: vsa/acl
*/
+/*ARGSUSED*/
int smbfs_acl_zfs2sd(
#ifdef _KERNEL
vsecattr_t *vsa,
diff --git a/usr/src/lib/libsmbfs/smb/acl_nt.h b/usr/src/lib/libsmbfs/smb/acl_nt.h
new file mode 100644
index 0000000000..844a7e6543
--- /dev/null
+++ b/usr/src/lib/libsmbfs/smb/acl_nt.h
@@ -0,0 +1,113 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _ACL_NT_H
+#define _ACL_NT_H
+
+/*
+ * Internal functions for dealing with
+ * NT Security data structures.
+ */
+
+#include <netsmb/smbfs_acl.h>
+
+/*
+ * Internal form of an NT SID
+ * Same as on the wire, but possibly byte-swapped.
+ */
+typedef struct i_ntsid {
+ uint8_t sid_revision;
+ uint8_t sid_subauthcount;
+ uint8_t sid_authority[6];
+ uint32_t sid_subauthvec[1]; /* actually len=subauthcount */
+} i_ntsid_t;
+#define I_SID_SIZE(sacnt) (8 + 4 * (sacnt))
+
+/*
+ * Internal form of an NT ACE
+ */
+typedef struct i_ntace {
+ uint8_t ace_type;
+ uint8_t ace_flags;
+ uint32_t ace_rights; /* generic, standard, specific, etc */
+ i_ntsid_t *ace_sid;
+} i_ntace_t;
+
+/*
+ * Internal form of an NT ACL (see sacl/dacl below)
+ */
+typedef struct i_ntacl {
+ uint8_t acl_revision; /* 0x02 observed with W2K */
+ uint16_t acl_acecount;
+ i_ntace_t *acl_acevec[1]; /* actually, len=acecount */
+} i_ntacl_t;
+
+/*
+ * Internal form of an NT Security Descriptor (SD)
+ */
+struct i_ntsd {
+ uint8_t sd_revision; /* 0x01 observed between W2K */
+ uint16_t sd_flags;
+ i_ntsid_t *sd_owner;
+ i_ntsid_t *sd_group;
+ i_ntacl_t *sd_sacl;
+ i_ntacl_t *sd_dacl;
+};
+
+struct mbdata;
+
+/*
+ * Import a raw SD (mb chain) into "internal" form.
+ * (like "absolute" form per. NT docs)
+ * Returns allocated data in sdp
+ */
+int mb_get_ntsd(struct mbdata *mbp, i_ntsd_t **sdp);
+
+/*
+ * Export an "internal" SD into an raw SD (mb chain).
+ * (a.k.a "self-relative" form per. NT docs)
+ * Returns allocated mbchain in mbp.
+ */
+int mb_put_ntsd(struct mbdata *mbp, i_ntsd_t *sd);
+
+
+/*
+ * Get an SD via ioctl on FD (with "selector" bits),
+ * stroing the raw Windows SD in the mb chain mbp.
+ */
+int smbfs_acl_iocget(int fd, uint32_t selector, struct mbdata *mbp);
+
+/*
+ * Set an SD via ioctl on FD (with "selector" bits),
+ * with a raw Windows SD from the chain mbp.
+ */
+int smbfs_acl_iocset(int fd, uint32_t selector, struct mbdata *mbp);
+
+
+int smbfs_sid2str(i_ntsid_t *sid,
+ char *obuf, size_t olen, uint32_t *ridp);
+
+#endif /* _ACL_NT_H */
diff --git a/usr/src/lib/libsmbfs/smb/acl_print.c b/usr/src/lib/libsmbfs/smb/acl_print.c
index 9a1d51e320..259258a9f1 100644
--- a/usr/src/lib/libsmbfs/smb/acl_print.c
+++ b/usr/src/lib/libsmbfs/smb/acl_print.c
@@ -20,12 +20,10 @@
*/
/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
/*
* Print an NT Security Descriptor (SD) and its sub-components.
*/
@@ -54,7 +52,8 @@
#include <netsmb/smb_lib.h>
#include <netsmb/smbfs_acl.h>
-#include <netsmb/smbfs_isec.h>
+
+#include "acl_nt.h"
static void
fprint_sid(FILE *fp, i_ntsid_t *sid)
diff --git a/usr/src/lib/libsmbfs/smb/cfopt.c b/usr/src/lib/libsmbfs/smb/cfopt.c
index 094682f6d8..3b2672b2cd 100644
--- a/usr/src/lib/libsmbfs/smb/cfopt.c
+++ b/usr/src/lib/libsmbfs/smb/cfopt.c
@@ -32,16 +32,19 @@
* $Id: cfopt.c,v 1.1.1.1 2001/06/09 00:28:12 zarzycki Exp $
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
-#include <sys/param.h>
+#include <sys/types.h>
#include <stdio.h>
#include <string.h>
+#include <synch.h>
#include <libintl.h>
#include <cflib.h>
#include <netsmb/smb_lib.h>
+#include <assert.h>
+
+/* lock for the variables below */
+mutex_t cf_opt_mutex = DEFAULTMUTEX;
int cf_opterr = 1, /* if error message should be printed */
cf_optind = 1, /* index into parent argv vector */
@@ -53,6 +56,18 @@ const char *cf_optarg; /* argument associated with option */
#define BADARG (int)':'
#define EMSG ""
+void
+cf_opt_lock(void)
+{
+ mutex_lock(&cf_opt_mutex);
+}
+
+void
+cf_opt_unlock(void)
+{
+ mutex_unlock(&cf_opt_mutex);
+}
+
int
cf_getopt(nargc, nargv, ostr)
int nargc;
@@ -63,10 +78,12 @@ cf_getopt(nargc, nargv, ostr)
char *oli; /* option letter list index */
int tmpind;
+ assert(MUTEX_HELD(&cf_opt_mutex));
+
if (cf_optreset || !*place) { /* update scanning pointer */
cf_optreset = 0;
tmpind = cf_optind;
- while (1) {
+ for (;;) {
if (tmpind >= nargc) {
place = EMSG;
return (-1);
diff --git a/usr/src/lib/libsmbfs/smb/charsets.c b/usr/src/lib/libsmbfs/smb/charsets.c
index 81075a3a2f..1ca0eb751c 100644
--- a/usr/src/lib/libsmbfs/smb/charsets.c
+++ b/usr/src/lib/libsmbfs/smb/charsets.c
@@ -43,7 +43,9 @@
#include <iconv.h>
#include <langinfo.h>
#include <strings.h>
+#include <libintl.h>
+#include <sys/isa_defs.h>
#include <netsmb/smb_lib.h>
#include <netsmb/mchain.h>
diff --git a/usr/src/lib/libsmbfs/smb/charsets.h b/usr/src/lib/libsmbfs/smb/charsets.h
index c754425f37..4c917c99d2 100644
--- a/usr/src/lib/libsmbfs/smb/charsets.h
+++ b/usr/src/lib/libsmbfs/smb/charsets.h
@@ -43,5 +43,9 @@ extern char *convert_leunicode_to_utf8(unsigned short *windows_string);
extern char *convert_unicode_to_utf8(unsigned short *windows_string);
extern unsigned short *convert_utf8_to_leunicode(const char *utf8_string);
extern size_t unicode_strlen(const uint16_t *unicode_string);
+extern char *utf8_str_tolower(const char *s);
+extern char *utf8_str_toupper(const char *s);
+
+extern char *unpercent(char *component);
#endif /* __CHARSETS_H__ */
diff --git a/usr/src/lib/libsmbfs/smb/connect.c b/usr/src/lib/libsmbfs/smb/connect.c
new file mode 100644
index 0000000000..d0f4e2b228
--- /dev/null
+++ b/usr/src/lib/libsmbfs/smb/connect.c
@@ -0,0 +1,535 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Functions to setup connections (TCP and/or NetBIOS)
+ * This has the fall-back logic for IP6, IP4, NBT
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <libintl.h>
+#include <xti.h>
+#include <assert.h>
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/byteorder.h>
+#include <sys/socket.h>
+#include <sys/fcntl.h>
+
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+
+#include <netsmb/smb.h>
+#include <netsmb/smb_lib.h>
+#include <netsmb/netbios.h>
+#include <netsmb/nb_lib.h>
+#include <netsmb/smb_dev.h>
+
+#include "charsets.h"
+#include "private.h"
+
+/*
+ * SMB messages are up to 64K.
+ * Let's leave room for two.
+ */
+static int smb_tcpsndbuf = 0x20000;
+static int smb_tcprcvbuf = 0x20000;
+static int smb_connect_timeout = 30; /* seconds */
+int smb_recv_timeout = 30; /* seconds */
+
+int conn_tcp6(struct smb_ctx *, const struct sockaddr *, int);
+int conn_tcp4(struct smb_ctx *, const struct sockaddr *, int);
+int conn_nbt(struct smb_ctx *, const struct sockaddr *, char *);
+
+/*
+ * Internal set sockopt for int-sized options.
+ * Borrowed from: libnsl/rpc/ti_opts.c
+ */
+static int
+smb_setopt_int(int fd, int level, int name, int val)
+{
+ struct t_optmgmt oreq, ores;
+ struct {
+ struct t_opthdr oh;
+ int ival;
+ } opts;
+
+ /* opt header */
+ opts.oh.len = sizeof (opts);
+ opts.oh.level = level;
+ opts.oh.name = name;
+ opts.oh.status = 0;
+ opts.ival = val;
+
+ oreq.flags = T_NEGOTIATE;
+ oreq.opt.buf = (void *)&opts;
+ oreq.opt.len = sizeof (opts);
+
+ ores.flags = 0;
+ ores.opt.buf = NULL;
+ ores.opt.maxlen = 0;
+
+ if (t_optmgmt(fd, &oreq, &ores) < 0) {
+ DPRINT("t_opgmgnt, t_errno = %d", t_errno);
+ if (t_errno == TSYSERR)
+ return (errno);
+ return (EPROTO);
+ }
+ if (ores.flags != T_SUCCESS) {
+ DPRINT("flags 0x%x, status 0x%x",
+ (int)ores.flags, (int)opts.oh.status);
+ return (EPROTO);
+ }
+
+ return (0);
+}
+
+static int
+smb_setopts(int fd)
+{
+ int err;
+
+ /*
+ * Set various socket/TCP options.
+ * Failures here are not fatal -
+ * just log a complaint.
+ *
+ * We don't need these two:
+ * SO_RCVTIMEO, SO_SNDTIMEO
+ */
+
+ err = smb_setopt_int(fd, SOL_SOCKET, SO_SNDBUF, smb_tcpsndbuf);
+ if (err) {
+ DPRINT("set SO_SNDBUF, err %d", err);
+ }
+
+ err = smb_setopt_int(fd, SOL_SOCKET, SO_RCVBUF, smb_tcprcvbuf);
+ if (err) {
+ DPRINT("set SO_RCVBUF, err %d", err);
+ }
+
+ err = smb_setopt_int(fd, SOL_SOCKET, SO_KEEPALIVE, 1);
+ if (err) {
+ DPRINT("set SO_KEEPALIVE, err %d", err);
+ }
+
+ err = smb_setopt_int(fd, IPPROTO_TCP, TCP_NODELAY, 1);
+ if (err) {
+ DPRINT("set TCP_NODELAY, err %d", err);
+ }
+
+ /* Set the connect timeout (in milliseconds). */
+ err = smb_setopt_int(fd, IPPROTO_TCP,
+ TCP_CONN_ABORT_THRESHOLD,
+ smb_connect_timeout * 1000);
+ if (err) {
+ DPRINT("set connect timeout, err %d", err);
+ }
+ return (0);
+}
+
+
+int
+conn_tcp6(struct smb_ctx *ctx, const struct sockaddr *sa, int port)
+{
+ struct sockaddr_in6 sin6;
+ char *dev = "/dev/tcp6";
+ char paddrbuf[INET6_ADDRSTRLEN];
+ struct t_call sndcall;
+ int fd, err;
+
+ if (sa->sa_family != AF_INET6) {
+ DPRINT("bad af %d", sa->sa_family);
+ return (EINVAL);
+ }
+ bcopy(sa, &sin6, sizeof (sin6));
+ sin6.sin6_port = htons(port);
+
+ DPRINT("tcp6: %s (%d)",
+ inet_ntop(AF_INET6, &sin6.sin6_addr,
+ paddrbuf, sizeof (paddrbuf)), port);
+
+ fd = t_open(dev, O_RDWR, NULL);
+ if (fd < 0) {
+ /* Assume t_errno = TSYSERR */
+ err = errno;
+ perror(dev);
+ return (err);
+ }
+ if ((err = smb_setopts(fd)) != 0)
+ goto errout;
+ if (t_bind(fd, NULL, NULL) < 0) {
+ DPRINT("t_bind t_errno %d", t_errno);
+ if (t_errno == TSYSERR)
+ err = errno;
+ else
+ err = EPROTO;
+ goto errout;
+ }
+ sndcall.addr.maxlen = sizeof (sin6);
+ sndcall.addr.len = sizeof (sin6);
+ sndcall.addr.buf = (void *) &sin6;
+ sndcall.opt.len = 0;
+ sndcall.udata.len = 0;
+ if (t_connect(fd, &sndcall, NULL) < 0) {
+ err = get_xti_err(fd);
+ DPRINT("connect, err %d", err);
+ goto errout;
+ }
+
+ DPRINT("tcp6: connected, fd=%d", fd);
+ ctx->ct_tran_fd = fd;
+ return (0);
+
+errout:
+ close(fd);
+ return (err);
+}
+
+/*
+ * This is used for both SMB over TCP (port 445)
+ * and NetBIOS - see conn_nbt().
+ */
+int
+conn_tcp4(struct smb_ctx *ctx, const struct sockaddr *sa, int port)
+{
+ struct sockaddr_in sin;
+ char *dev = "/dev/tcp";
+ char paddrbuf[INET_ADDRSTRLEN];
+ struct t_call sndcall;
+ int fd, err;
+
+ if (sa->sa_family != AF_INET) {
+ DPRINT("bad af %d", sa->sa_family);
+ return (EINVAL);
+ }
+ bcopy(sa, &sin, sizeof (sin));
+ sin.sin_port = htons(port);
+
+ DPRINT("tcp4: %s (%d)",
+ inet_ntop(AF_INET, &sin.sin_addr,
+ paddrbuf, sizeof (paddrbuf)), port);
+
+ fd = t_open(dev, O_RDWR, NULL);
+ if (fd < 0) {
+ /* Assume t_errno = TSYSERR */
+ err = errno;
+ perror(dev);
+ return (err);
+ }
+ if ((err = smb_setopts(fd)) != 0)
+ goto errout;
+ if (t_bind(fd, NULL, NULL) < 0) {
+ DPRINT("t_bind t_errno %d", t_errno);
+ if (t_errno == TSYSERR)
+ err = errno;
+ else
+ err = EPROTO;
+ goto errout;
+ }
+ sndcall.addr.maxlen = sizeof (sin);
+ sndcall.addr.len = sizeof (sin);
+ sndcall.addr.buf = (void *) &sin;
+ sndcall.opt.len = 0;
+ sndcall.udata.len = 0;
+ if (t_connect(fd, &sndcall, NULL) < 0) {
+ err = get_xti_err(fd);
+ DPRINT("connect, err %d", err);
+ goto errout;
+ }
+
+ DPRINT("tcp4: connected, fd=%d", fd);
+ ctx->ct_tran_fd = fd;
+ return (0);
+
+errout:
+ close(fd);
+ return (err);
+}
+
+/*
+ * Open a NetBIOS connection (session, port 139)
+ *
+ * The optional name parameter, if passed, means
+ * we found the sockaddr via NetBIOS name lookup,
+ * and can just use that for our session request.
+ * Otherwise (if name is NULL), we're connecting
+ * by IP address, and need to come up with the
+ * NetBIOS name by other means.
+ */
+int
+conn_nbt(struct smb_ctx *ctx, const struct sockaddr *saarg, char *name)
+{
+ struct sockaddr_in sin;
+ struct sockaddr *sa;
+ char server[NB_NAMELEN];
+ char workgroup[NB_NAMELEN];
+ int err, nberr, port;
+
+ bcopy(saarg, &sin, sizeof (sin));
+ sa = (struct sockaddr *)&sin;
+
+ switch (sin.sin_family) {
+ case AF_NETBIOS: /* our fake AF */
+ sin.sin_family = AF_INET;
+ break;
+ case AF_INET:
+ break;
+ default:
+ DPRINT("bad af %d", sin.sin_family);
+ return (EINVAL);
+ }
+ port = IPPORT_NETBIOS_SSN;
+
+ /*
+ * If we have a NetBIOS name, just use it.
+ * This is the path taken when we've done a
+ * NetBIOS name lookup on this name to get
+ * the IP address in the passed sa. Otherwise,
+ * we're connecting by IP address, and need to
+ * figure out what NetBIOS name to use.
+ */
+ if (name) {
+ strlcpy(server, name, sizeof (server));
+ DPRINT("given name: %s", server);
+ } else {
+ /*
+ *
+ * Try a NetBIOS node status query,
+ * which searches for a type=[20] name.
+ * If that doesn't work, just use the
+ * (fake) "*SMBSERVER" name.
+ */
+ DPRINT("try node status");
+ server[0] = '\0';
+ nberr = nbns_getnodestatus(ctx->ct_nb,
+ &sin.sin_addr, server, workgroup);
+ if (nberr == 0 && server[0] != '\0') {
+ /* Found the name. Save for reconnect. */
+ DPRINT("found name: %s", server);
+ strlcpy(ctx->ct_srvname, server,
+ sizeof (ctx->ct_srvname));
+ } else {
+ DPRINT("getnodestatus, nberr %d", nberr);
+ strlcpy(server, "*SMBSERVER", sizeof (server));
+ }
+ }
+
+ /*
+ * Establish the TCP connection.
+ * Careful to close it on errors.
+ */
+ if ((err = conn_tcp4(ctx, sa, port)) != 0) {
+ DPRINT("TCP connect: err=%d", err);
+ goto out;
+ }
+
+ /* Connected. Do NetBIOS session request. */
+ err = nb_ssn_request(ctx, server);
+ if (err)
+ DPRINT("ssn_rq, err %d", err);
+
+out:
+ if (err) {
+ if (ctx->ct_tran_fd != -1) {
+ close(ctx->ct_tran_fd);
+ ctx->ct_tran_fd = -1;
+ }
+ }
+ return (err);
+}
+
+/*
+ * Make a new connection, or reconnect.
+ */
+int
+smb_iod_connect(smb_ctx_t *ctx)
+{
+ struct sockaddr *sa;
+ int err, err2;
+ struct mbdata blob;
+
+ memset(&blob, 0, sizeof (blob));
+
+ if (ctx->ct_srvname[0] == '\0') {
+ DPRINT("sername not set!");
+ return (EINVAL);
+ }
+ DPRINT("server: %s", ctx->ct_srvname);
+
+ if (smb_debug)
+ dump_ctx("smb_iod_connect", ctx);
+
+ /*
+ * This may be a reconnect, so
+ * cleanup if necessary.
+ */
+ if (ctx->ct_tran_fd != -1) {
+ close(ctx->ct_tran_fd);
+ ctx->ct_tran_fd = -1;
+ }
+
+ /*
+ * Get local machine name.
+ * Full name - not a NetBIOS name.
+ */
+ if (ctx->ct_locname == NULL) {
+ err = smb_getlocalname(&ctx->ct_locname);
+ if (err) {
+ smb_error(dgettext(TEXT_DOMAIN,
+ "can't get local name"), err);
+ return (err);
+ }
+ }
+
+ /*
+ * We're called with each IP address
+ * already copied into ct_srvaddr.
+ */
+ ctx->ct_flags |= SMBCF_RESOLVED;
+
+ sa = &ctx->ct_srvaddr.sa;
+ switch (sa->sa_family) {
+
+ case AF_INET6:
+ err = conn_tcp6(ctx, sa, IPPORT_SMB);
+ break;
+
+ case AF_INET:
+ err = conn_tcp4(ctx, sa, IPPORT_SMB);
+ /*
+ * If port 445 was not listening, try port 139.
+ * Note: Not doing NetBIOS name lookup here.
+ * We already have the IP address.
+ */
+ switch (err) {
+ case ECONNRESET:
+ case ECONNREFUSED:
+ err2 = conn_nbt(ctx, sa, NULL);
+ if (err2 == 0)
+ err = 0;
+ }
+ break;
+
+ case AF_NETBIOS:
+ /* Like AF_INET, but use NetBIOS ssn. */
+ err = conn_nbt(ctx, sa, ctx->ct_srvname);
+ break;
+
+ default:
+ DPRINT("skipped family %d", sa->sa_family);
+ err = EPROTONOSUPPORT;
+ break;
+ }
+
+
+ if (err) {
+ DPRINT("connect, err=%d", err);
+ return (err);
+ }
+
+ /*
+ * SMB Negotiate Protocol and
+ * SMB Session Setup, one of 3 ways:
+ * NULL session
+ * Extended security,
+ * NTLM (v2, v1)
+ *
+ * Empty user name means an explicit request for
+ * NULL session setup. No fall-back logic here.
+ *
+ * For NULL session, don't offer extended security.
+ * That's a lot simpler than dealing with NTLMSSP.
+ */
+ if (ctx->ct_user[0] == '\0') {
+ ctx->ct_vopt &= ~SMBVOPT_EXT_SEC;
+ err = smb_negprot(ctx, &blob);
+ if (err)
+ goto out;
+ err = smb_ssnsetup_null(ctx);
+ } else {
+ /*
+ * Do SMB Negotiate Protocol.
+ */
+ err = smb_negprot(ctx, &blob);
+ if (err)
+ goto out;
+
+ /*
+ * Do SMB Session Setup (authenticate)
+ *
+ * If the server negotiated extended security,
+ * run the SPNEGO state machine.
+ */
+ if (ctx->ct_sopt.sv_caps & SMB_CAP_EXT_SECURITY) {
+ err = smb_ssnsetup_spnego(ctx, &blob);
+ } else {
+ /*
+ * Server did NOT negotiate extended security.
+ * Try NTLMv2, NTLMv1 (if enabled).
+ */
+ if ((ctx->ct_authflags &
+ (SMB_AT_NTLM2 | SMB_AT_NTLM1)) == 0) {
+ /*
+ * Don't return EAUTH, because a
+ * new password will not help.
+ */
+ DPRINT("No NTLM authflags");
+ err = ENOTSUP;
+ goto out;
+ }
+ if (ctx->ct_authflags & SMB_AT_NTLM2)
+ err = smb_ssnsetup_ntlm2(ctx);
+ else
+ err = EAUTH;
+ if (err == EAUTH && 0 !=
+ (ctx->ct_authflags & SMB_AT_NTLM1))
+ err = smb_ssnsetup_ntlm1(ctx);
+ }
+ }
+
+ /* Tell library code we have a session. */
+ ctx->ct_flags |= SMBCF_RESOLVED | SMBCF_SSNACTIVE;
+
+out:
+ mb_done(&blob);
+
+ if (err) {
+ close(ctx->ct_tran_fd);
+ ctx->ct_tran_fd = -1;
+ } else
+ DPRINT("tran_fd = %d", ctx->ct_tran_fd);
+
+ return (err);
+}
diff --git a/usr/src/lib/libsmbfs/smb/crypt.c b/usr/src/lib/libsmbfs/smb/crypt.c
new file mode 100644
index 0000000000..ea1d7e6dd1
--- /dev/null
+++ b/usr/src/lib/libsmbfs/smb/crypt.c
@@ -0,0 +1,175 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Crypto support, using libpkcs11
+ *
+ * Some code copied from the server: libsmb smb_crypt.c
+ * with minor changes, i.e. errno.h return values.
+ * XXX: Move this to a common library (later).
+ */
+
+#include <sys/types.h>
+#include <sys/md4.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+
+#include <security/cryptoki.h>
+#include <security/pkcs11.h>
+#include <cryptoutil.h>
+
+#include "smb_crypt.h"
+
+static void
+smb_initlmkey(uchar_t *keyout, const uchar_t *keyin);
+
+/*
+ * Like libsmb smb_auth_DES,
+ * but use uchar_t, return errno.
+ */
+int
+smb_encrypt_DES(uchar_t *Result, int ResultLen,
+ const uchar_t *Key, int KeyLen,
+ const uchar_t *Data, int DataLen)
+{
+ CK_RV rv;
+ CK_MECHANISM mechanism;
+ CK_OBJECT_HANDLE hKey;
+ CK_SESSION_HANDLE hSession;
+ CK_ULONG ciphertext_len;
+ uchar_t des_key[8];
+ int error = 0;
+ int K, D;
+ int k, d;
+
+ /* Calculate proper number of iterations */
+ K = KeyLen / 7;
+ D = DataLen / 8;
+
+ if (ResultLen < (K * 8 * D)) {
+ return (EINVAL);
+ }
+
+ /*
+ * Use SUNW convenience function to initialize the cryptoki
+ * library, and open a session with a slot that supports
+ * the mechanism we plan on using.
+ */
+ mechanism.mechanism = CKM_DES_ECB;
+ mechanism.pParameter = NULL;
+ mechanism.ulParameterLen = 0;
+ rv = SUNW_C_GetMechSession(mechanism.mechanism, &hSession);
+ if (rv != CKR_OK) {
+ return (ENOTSUP);
+ }
+
+ for (k = 0; k < K; k++) {
+ smb_initlmkey(des_key, &Key[k * 7]);
+ rv = SUNW_C_KeyToObject(hSession, mechanism.mechanism,
+ des_key, 8, &hKey);
+ if (rv != CKR_OK) {
+ error = EIO;
+ goto exit_session;
+ }
+ /* Initialize the encryption operation in the session */
+ rv = C_EncryptInit(hSession, &mechanism, hKey);
+ if (rv != CKR_OK) {
+ error = EIO;
+ goto exit_encrypt;
+ }
+ ciphertext_len = DataLen;
+ for (d = 0; d < D; d++) {
+ /* Read in the data and encrypt this portion */
+ rv = C_EncryptUpdate(hSession,
+ (CK_BYTE_PTR)Data + (d * 8), 8,
+ &Result[(k * (8 * D)) + (d * 8)],
+ &ciphertext_len);
+ if (rv != CKR_OK) {
+ error = EIO;
+ goto exit_encrypt;
+ }
+ }
+ (void) C_DestroyObject(hSession, hKey);
+ }
+ goto exit_session;
+
+exit_encrypt:
+ (void) C_DestroyObject(hSession, hKey);
+exit_session:
+ (void) C_CloseSession(hSession);
+
+ return (error);
+}
+
+/*
+ * See "Netlogon Credential Computation" section of MS-NRPC document.
+ * Same as in libsmb, but output arg first.
+ */
+static void
+smb_initlmkey(uchar_t *keyout, const uchar_t *keyin)
+{
+ int i;
+
+ keyout[0] = keyin[0] >> 0x01;
+ keyout[1] = ((keyin[0] & 0x01) << 6) | (keyin[1] >> 2);
+ keyout[2] = ((keyin[1] & 0x03) << 5) | (keyin[2] >> 3);
+ keyout[3] = ((keyin[2] & 0x07) << 4) | (keyin[3] >> 4);
+ keyout[4] = ((keyin[3] & 0x0f) << 3) | (keyin[4] >> 5);
+ keyout[5] = ((keyin[4] & 0x1f) << 2) | (keyin[5] >> 6);
+ keyout[6] = ((keyin[5] & 0x3f) << 1) | (keyin[6] >> 7);
+ keyout[7] = keyin[6] & 0x7f;
+
+ for (i = 0; i < 8; i++)
+ keyout[i] = (keyout[i] << 1) & 0xfe;
+}
+
+/*
+ * Get some random bytes from /dev/urandom
+ *
+ * There may be a preferred way to call this via libpkcs11
+ * XXX: (see: C_GenerateRandom, etc. -- later...)
+ * Just read from /dev/urandom for now.
+ */
+int
+smb_get_urandom(void *data, size_t dlen)
+{
+ int fd, rlen;
+
+ fd = open("/dev/urandom", O_RDONLY);
+ if (fd < 0)
+ return (errno);
+
+ rlen = read(fd, data, dlen);
+ close(fd);
+
+ if (rlen < 0)
+ return (errno);
+ if (rlen < dlen)
+ return (EIO);
+ return (0);
+}
diff --git a/usr/src/lib/libsmbfs/smb/ctx.c b/usr/src/lib/libsmbfs/smb/ctx.c
index dfc86bd191..87f069402c 100644
--- a/usr/src/lib/libsmbfs/smb/ctx.c
+++ b/usr/src/lib/libsmbfs/smb/ctx.c
@@ -33,7 +33,7 @@
*/
/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -58,28 +58,35 @@
#include <assert.h>
#include <nss_dbdefs.h>
-#include <kerberosv5/krb5.h>
-#include <kerberosv5/com_err.h>
-
+#include <cflib.h>
#include <netsmb/smb_lib.h>
#include <netsmb/netbios.h>
#include <netsmb/nb_lib.h>
#include <netsmb/smb_dev.h>
-#include <cflib.h>
-#include <charsets.h>
-#include <spnego.h>
+#include "charsets.h"
+#include "spnego.h"
#include "derparse.h"
#include "private.h"
+#include "ntlm.h"
-extern MECH_OID g_stcMechOIDList [];
+#ifndef FALSE
+#define FALSE 0
+#endif
+#ifndef TRUE
+#define TRUE 1
+#endif
-#define POWEROF2(x) (((x) & ((x)-1)) == 0)
/* These two may be set by commands. */
int smb_debug, smb_verbose;
/*
+ * Was: STDPARAM_OPT - see smb_ctx_scan_argv, smb_ctx_opt
+ */
+const char smbutil_std_opts[] = "ABCD:E:I:L:M:NO:P:U:R:S:T:W:";
+
+/*
* Give the RPC library a callback hook that will be
* called whenever we destroy or reinit an smb_ctx_t.
* The name rpc_cleanup_smbctx() is legacy, and was
@@ -138,21 +145,30 @@ dump_ctx_flags(int flags)
}
void
-dump_ctx_ssn(struct smbioc_ossn *ssn)
+dump_iod_ssn(smb_iod_ssn_t *is)
{
- printf(" srvname=\"%s\", dom=\"%s\", user=\"%s\", password=%s\n",
- ssn->ioc_srvname, ssn->ioc_workgroup, ssn->ioc_user,
- ssn->ioc_password[0] ? "(non-null)" : "NULL");
- printf(" timeout=%d, retry=%d, owner=%d, group=%d\n",
- ssn->ioc_timeout, ssn->ioc_retrycount,
- ssn->ioc_owner, ssn->ioc_group);
-}
+ static const char zeros[NTLM_HASH_SZ] = {0};
+ struct smbioc_ossn *ssn = &is->iod_ossn;
+
+ printf(" ct_srvname=\"%s\", ", ssn->ssn_srvname);
+ dump_sockaddr(&ssn->ssn_srvaddr.sa);
+ printf(" dom=\"%s\", user=\"%s\"\n",
+ ssn->ssn_domain, ssn->ssn_user);
+ printf(" ct_vopt=0x%x, ct_owner=%d\n",
+ ssn->ssn_vopt, ssn->ssn_owner);
+ printf(" ct_authflags=0x%x\n", is->iod_authflags);
+
+ printf(" ct_nthash:");
+ if (bcmp(zeros, &is->iod_nthash, NTLM_HASH_SZ))
+ smb_hexdump(&is->iod_nthash, NTLM_HASH_SZ);
+ else
+ printf(" {0}\n");
-void
-dump_ctx_sh(struct smbioc_oshare *sh)
-{
- printf(" share_name=\"%s\", share_pw=\"%s\"\n",
- sh->ioc_share, sh->ioc_password);
+ printf(" ct_lmhash:");
+ if (bcmp(zeros, &is->iod_lmhash, NTLM_HASH_SZ))
+ smb_hexdump(&is->iod_lmhash, NTLM_HASH_SZ);
+ else
+ printf(" {0}\n");
}
void
@@ -161,24 +177,105 @@ dump_ctx(char *where, struct smb_ctx *ctx)
printf("context %s:\n", where);
dump_ctx_flags(ctx->ct_flags);
- printf(" localname=\"%s\"", ctx->ct_locname);
+ if (ctx->ct_locname)
+ printf(" localname=\"%s\"", ctx->ct_locname);
+ else
+ printf(" localname=NULL");
if (ctx->ct_fullserver)
printf(" fullserver=\"%s\"", ctx->ct_fullserver);
else
printf(" fullserver=NULL");
- if (ctx->ct_srvaddr)
- printf(" srvaddr=\"%s\"\n", ctx->ct_srvaddr);
+ if (ctx->ct_srvaddr_s)
+ printf(" srvaddr_s=\"%s\"\n", ctx->ct_srvaddr_s);
+ else
+ printf(" srvaddr_s=NULL\n");
+
+ if (ctx->ct_addrinfo)
+ dump_addrinfo(ctx->ct_addrinfo);
else
- printf(" srvaddr=NULL\n");
+ printf(" ct_addrinfo = NULL\n");
+
+ dump_iod_ssn(&ctx->ct_iod_ssn);
+
+ printf(" share_name=\"%s\", share_type=%d\n",
+ ctx->ct_origshare ? ctx->ct_origshare : "",
+ ctx->ct_shtype_req);
- dump_ctx_ssn(&ctx->ct_ssn);
- dump_ctx_sh(&ctx->ct_sh);
+ /* dump_iod_work()? */
+}
+
+int
+smb_ctx_alloc(struct smb_ctx **ctx_pp)
+{
+ smb_ctx_t *ctx;
+ int err;
+
+ ctx = malloc(sizeof (*ctx));
+ if (ctx == NULL)
+ return (ENOMEM);
+ err = smb_ctx_init(ctx);
+ if (err != 0) {
+ free(ctx);
+ return (err);
+ }
+ *ctx_pp = ctx;
+ return (0);
}
/*
- * Initialize an smb_ctx struct.
+ * Initialize an smb_ctx struct (defaults)
+ */
+int
+smb_ctx_init(struct smb_ctx *ctx)
+{
+ char pwbuf[NSS_BUFLEN_PASSWD];
+ struct passwd pw;
+ int error = 0;
+
+ bzero(ctx, sizeof (*ctx));
+
+ error = nb_ctx_create(&ctx->ct_nb);
+ if (error)
+ return (error);
+
+ ctx->ct_dev_fd = -1;
+ ctx->ct_tran_fd = -1;
+ ctx->ct_parsedlevel = SMBL_NONE;
+ ctx->ct_minlevel = SMBL_NONE;
+ ctx->ct_maxlevel = SMBL_PATH;
+
+ /* Fill in defaults */
+ ctx->ct_vopt = SMBVOPT_EXT_SEC;
+ ctx->ct_owner = SMBM_ANY_OWNER;
+ ctx->ct_authflags = SMB_AT_DEFAULT;
+ ctx->ct_minauth = SMB_AT_DEFAULT;
+
+ nb_ctx_setscope(ctx->ct_nb, "");
+
+ /*
+ * if the user name is not specified some other way,
+ * use the current user name (built-in default)
+ */
+ if (getpwuid_r(getuid(), &pw, pwbuf, sizeof (pwbuf)) != NULL) {
+ smb_ctx_setuser(ctx, pw.pw_name, 0);
+ ctx->ct_home = strdup(pw.pw_name);
+ }
+
+ /*
+ * Set a built-in default domain (workgroup).
+ * Using the Windows/NT default for now.
+ */
+ smb_ctx_setdomain(ctx, "WORKGROUP", 0);
+
+ return (error);
+}
+
+/*
+ * "Scan" the command line args to find the server name,
+ * user name, and share name, as needed. We need these
+ * before reading the RC files and/or sharectl values.
*
* The sequence for getting all the members filled in
* has some tricky aspects. Here's how it works:
@@ -211,154 +308,137 @@ dump_ctx(char *where, struct smb_ctx *ctx)
* ignore options not in the options string.
*/
int
-smb_ctx_init(struct smb_ctx *ctx, int argc, char *argv[],
+smb_ctx_scan_argv(struct smb_ctx *ctx, int argc, char **argv,
int minlevel, int maxlevel, int sharetype)
{
- int opt, error = 0;
- const char *arg, *cp;
- struct passwd pw;
- char pwbuf[NSS_BUFLEN_PASSWD];
+ int ind, opt, error = 0;
int aflg = 0, uflg = 0;
-
- bzero(ctx, sizeof (*ctx));
- if (sharetype == SMB_ST_DISK)
- ctx->ct_flags |= SMBCF_BROWSEOK;
- error = nb_ctx_create(&ctx->ct_nb);
- if (error)
- return (error);
-
- ctx->ct_fd = -1;
- ctx->ct_parsedlevel = SMBL_NONE;
- ctx->ct_minlevel = minlevel;
- ctx->ct_maxlevel = maxlevel;
-
- /* Fill in defaults */
- ctx->ct_ssn.ioc_opt = SMBVOPT_CREATE | SMBVOPT_MINAUTH_NTLM;
-
- ctx->ct_ssn.ioc_timeout = 15;
- ctx->ct_ssn.ioc_retrycount = 4;
- ctx->ct_ssn.ioc_owner = SMBM_ANY_OWNER;
- ctx->ct_ssn.ioc_group = SMBM_ANY_GROUP;
- ctx->ct_ssn.ioc_mode = SMBM_EXEC;
- ctx->ct_ssn.ioc_rights = SMBM_DEFAULT;
-
- ctx->ct_sh.ioc_opt = SMBVOPT_CREATE;
- ctx->ct_sh.ioc_owner = SMBM_ANY_OWNER;
- ctx->ct_sh.ioc_group = SMBM_ANY_GROUP;
- ctx->ct_sh.ioc_mode = SMBM_EXEC;
- ctx->ct_sh.ioc_rights = SMBM_DEFAULT;
- ctx->ct_sh.ioc_owner = SMBM_ANY_OWNER;
- ctx->ct_sh.ioc_group = SMBM_ANY_GROUP;
-
- nb_ctx_setscope(ctx->ct_nb, "");
-
- /*
- * if the user name is not specified some other way,
- * use the current user name (built-in default)
- */
- if (getpwuid_r(geteuid(), &pw, pwbuf, sizeof (pwbuf)) != NULL)
- smb_ctx_setuser(ctx, pw.pw_name, 0);
-
- /*
- * Set a built-in default domain (workgroup).
- * XXX: What's the best default? Use "?" instead?
- * Using the Windows/NT default for now.
- */
- smb_ctx_setworkgroup(ctx, "WORKGROUP", 0);
-
- /*
- * Parse the UNC path. Values from here are
- * marked as "from CMD".
- */
- if (argv == NULL)
- goto done;
- for (opt = 1; opt < argc; opt++) {
- cp = argv[opt];
- if (strncmp(cp, "//", 2) != 0)
- continue;
- error = smb_ctx_parseunc(ctx, cp, sharetype, &cp);
- if (error)
- return (error);
- break;
- }
+ const char *arg;
/*
* Parse options, if any. Values from here too
* are marked as "from CMD".
*/
- while (error == 0 && (opt = cf_getopt(argc, argv, ":AU:E:L:")) != -1) {
+ if (argv == NULL)
+ return (0);
+
+ ctx->ct_minlevel = minlevel;
+ ctx->ct_maxlevel = maxlevel;
+ ctx->ct_shtype_req = sharetype;
+
+ cf_opt_lock();
+ /* Careful: no return/goto before cf_opt_unlock! */
+ while (error == 0) {
+ opt = cf_getopt(argc, argv, STDPARAM_OPT);
+ if (opt == -1)
+ break;
arg = cf_optarg;
+ /* NB: handle most in smb_ctx_opt */
switch (opt) {
case 'A':
aflg = 1;
error = smb_ctx_setuser(ctx, "", TRUE);
- error = smb_ctx_setpassword(ctx, "", TRUE);
ctx->ct_flags |= SMBCF_NOPWD;
break;
- case 'E':
-#if 0 /* We don't support any "charset" stuff. (ignore -E) */
- error = smb_ctx_setcharset(ctx, arg);
- if (error)
- return (error);
-#endif
- break;
- case 'L':
-#if 0 /* Use the standard environment variables (ignore -L) */
- error = nls_setlocale(optarg);
- if (error)
- break;
-#endif
- break;
case 'U':
uflg = 1;
error = smb_ctx_setuser(ctx, arg, TRUE);
break;
+ default:
+ DPRINT("skip opt=%c", opt);
+ break;
}
}
+ ind = cf_optind;
+ arg = argv[ind];
+ cf_optind = cf_optreset = 1;
+ cf_opt_unlock();
+
+ if (error)
+ return (error);
+
if (aflg && uflg) {
printf(gettext("-A and -U flags are exclusive.\n"));
- return (1);
+ return (EINVAL);
}
- cf_optind = cf_optreset = 1;
-done:
- if (smb_debug)
- dump_ctx("after smb_ctx_init", ctx);
+ /*
+ * Parse the UNC path. Values from here are
+ * marked as "from CMD".
+ */
+ for (; ind < argc; ind++) {
+ arg = argv[ind];
+ if (strncmp(arg, "//", 2) != 0)
+ continue;
+ error = smb_ctx_parseunc(ctx, arg,
+ minlevel, maxlevel, sharetype, &arg);
+ if (error)
+ return (error);
+ break;
+ }
return (error);
}
void
+smb_ctx_free(smb_ctx_t *ctx)
+{
+ smb_ctx_done(ctx);
+ free(ctx);
+}
+
+void
smb_ctx_done(struct smb_ctx *ctx)
{
rpc_cleanup_smbctx(ctx);
- /* Kerberos stuff. See smb_ctx_krb5init() */
- if (ctx->ct_krb5ctx) {
- if (ctx->ct_krb5cp)
- krb5_free_principal(ctx->ct_krb5ctx, ctx->ct_krb5cp);
- krb5_free_context(ctx->ct_krb5ctx);
+ if (ctx->ct_dev_fd != -1) {
+ close(ctx->ct_dev_fd);
+ ctx->ct_dev_fd = -1;
}
-
- if (ctx->ct_fd != -1)
- close(ctx->ct_fd);
-#if 0 /* XXX: not pointers anymore */
- if (&ctx->ct_ssn.ioc_server)
- nb_snbfree(&ctx->ct_ssn.ioc_server);
- if (&ctx->ct_ssn.ioc_local)
- nb_snbfree(&ctx->ct_ssn.ioc_local);
-#endif
- if (ctx->ct_srvaddr)
- free(ctx->ct_srvaddr);
- if (ctx->ct_nb)
+ if (ctx->ct_tran_fd != -1) {
+ close(ctx->ct_tran_fd);
+ ctx->ct_tran_fd = -1;
+ }
+ if (ctx->ct_srvaddr_s) {
+ free(ctx->ct_srvaddr_s);
+ ctx->ct_srvaddr_s = NULL;
+ }
+ if (ctx->ct_nb) {
nb_ctx_done(ctx->ct_nb);
- if (ctx->ct_secblob)
- free(ctx->ct_secblob);
- if (ctx->ct_origshare)
+ ctx->ct_nb = NULL;
+ }
+ if (ctx->ct_locname) {
+ free(ctx->ct_locname);
+ ctx->ct_locname = NULL;
+ }
+ if (ctx->ct_origshare) {
free(ctx->ct_origshare);
- if (ctx->ct_fullserver)
+ ctx->ct_origshare = NULL;
+ }
+ if (ctx->ct_fullserver) {
free(ctx->ct_fullserver);
+ ctx->ct_fullserver = NULL;
+ }
+ if (ctx->ct_addrinfo) {
+ freeaddrinfo(ctx->ct_addrinfo);
+ ctx->ct_addrinfo = NULL;
+ }
+ if (ctx->ct_home)
+ free(ctx->ct_home);
+ if (ctx->ct_srv_OS) {
+ free(ctx->ct_srv_OS);
+ ctx->ct_srv_OS = NULL;
+ }
+ if (ctx->ct_srv_LM) {
+ free(ctx->ct_srv_LM);
+ ctx->ct_srv_LM = NULL;
+ }
+ if (ctx->ct_mackey) {
+ free(ctx->ct_mackey);
+ ctx->ct_mackey = NULL;
+ }
}
static int
@@ -385,20 +465,30 @@ getsubstring(const char *p, uchar_t sep, char *dest, int maxlen,
* Values found here are marked as "from CMD".
*/
int
-smb_ctx_parseunc(struct smb_ctx *ctx, const char *unc, int sharetype,
+smb_ctx_parseunc(struct smb_ctx *ctx, const char *unc,
+ int minlevel, int maxlevel, int sharetype,
const char **next)
{
const char *p = unc;
- char *p1, *colon, *servername;
+ char *p1, *colon;
char tmp[1024];
char tmp2[1024];
int error;
+ /*
+ * This may be called outside of _scan_argv,
+ * so make sure these get initialized.
+ */
+ ctx->ct_minlevel = minlevel;
+ ctx->ct_maxlevel = maxlevel;
+ ctx->ct_shtype_req = sharetype;
+
ctx->ct_parsedlevel = SMBL_NONE;
if (*p++ != '/' || *p++ != '/') {
smb_error(dgettext(TEXT_DOMAIN,
"UNC should start with '//'"), 0);
- return (EINVAL);
+ error = EINVAL;
+ goto out;
}
p1 = tmp;
error = getsubstring(p, ';', p1, sizeof (tmp), &p);
@@ -406,12 +496,13 @@ smb_ctx_parseunc(struct smb_ctx *ctx, const char *unc, int sharetype,
if (*p1 == 0) {
smb_error(dgettext(TEXT_DOMAIN,
"empty workgroup name"), 0);
- return (EINVAL);
+ error = EINVAL;
+ goto out;
}
nls_str_upper(tmp, tmp);
- error = smb_ctx_setworkgroup(ctx, unpercent(tmp), TRUE);
+ error = smb_ctx_setdomain(ctx, unpercent(tmp), TRUE);
if (error)
- return (error);
+ goto out;
}
colon = (char *)p;
error = getsubstring(p, '@', p1, sizeof (tmp), &p);
@@ -419,7 +510,8 @@ smb_ctx_parseunc(struct smb_ctx *ctx, const char *unc, int sharetype,
if (ctx->ct_maxlevel < SMBL_VC) {
smb_error(dgettext(TEXT_DOMAIN,
"no user name required"), 0);
- return (EINVAL);
+ error = EINVAL;
+ goto out;
}
p1 = strchr(tmp, ':');
if (p1) {
@@ -427,7 +519,7 @@ smb_ctx_parseunc(struct smb_ctx *ctx, const char *unc, int sharetype,
*p1++ = (char)0;
error = smb_ctx_setpassword(ctx, unpercent(p1), TRUE);
if (error)
- return (error);
+ goto out;
if (p - colon > 2)
memset(colon+1, '*', p - colon - 2);
}
@@ -435,11 +527,12 @@ smb_ctx_parseunc(struct smb_ctx *ctx, const char *unc, int sharetype,
if (*p1 == 0) {
smb_error(dgettext(TEXT_DOMAIN,
"empty user name"), 0);
- return (EINVAL);
+ error = EINVAL;
+ goto out;
}
error = smb_ctx_setuser(ctx, unpercent(tmp), TRUE);
if (error)
- return (error);
+ goto out;
ctx->ct_parsedlevel = SMBL_VC;
}
error = getsubstring(p, '/', p1, sizeof (tmp), &p);
@@ -448,15 +541,15 @@ smb_ctx_parseunc(struct smb_ctx *ctx, const char *unc, int sharetype,
if (error) {
smb_error(dgettext(TEXT_DOMAIN,
"no server name found"), 0);
- return (error);
+ goto out;
}
}
if (*p1 == 0) {
smb_error(dgettext(TEXT_DOMAIN, "empty server name"), 0);
- return (EINVAL);
+ error = EINVAL;
+ goto out;
}
-
/*
* It's safe to uppercase this string, which
* consists of ascii characters that should
@@ -464,49 +557,32 @@ smb_ctx_parseunc(struct smb_ctx *ctx, const char *unc, int sharetype,
* hex digits 0-9 and A-F (already uppercased, and
* if not uppercased they need to be). However,
* it is NOT safe to uppercase after it has been
- * converted, below!
+ * "unpercent" converted, below!
*/
-
nls_str_upper(tmp2, tmp);
/*
- * scan for % in the string.
- * If we find one, convert
- * to the assumed codepage.
+ * Save ct_fullserver without case conversion.
*/
-
- if (strchr(tmp2, '%')) {
- /* use the 1st buffer, we don't need the old string */
- servername = tmp;
- if (!(servername = convert_utf8_to_wincs(unpercent(tmp2)))) {
- smb_error(dgettext(TEXT_DOMAIN, "bad server name"), 0);
- return (EINVAL);
- }
- /*
- * Converts utf8 to win equivalent of
- * what is configured on this machine.
- * Note that we are assuming this is the
- * encoding used on the server, and that
- * assumption might be incorrect. This is
- * the best we can do now, and we should
- * move to use port 445 to avoid having
- * to worry about server codepages.
- */
- } else /* no conversion needed */
- servername = tmp2;
-
- smb_ctx_setserver(ctx, servername);
- error = smb_ctx_setfullserver(ctx, servername);
-
+ if (strchr(tmp, '%'))
+ (void) unpercent(tmp);
+ smb_ctx_setfullserver(ctx, tmp);
if (error)
- return (error);
+ goto out;
+
+#ifdef SMB_ST_NONE
if (sharetype == SMB_ST_NONE) {
- *next = p;
- return (0);
+ if (next)
+ *next = p;
+ error = 0;
+ goto out;
}
+#endif
+
if (*p != 0 && ctx->ct_maxlevel < SMBL_SHARE) {
smb_error(dgettext(TEXT_DOMAIN, "no share name required"), 0);
- return (EINVAL);
+ error = EINVAL;
+ goto out;
}
error = getsubstring(p, '/', p1, sizeof (tmp), &p);
if (error) {
@@ -514,21 +590,31 @@ smb_ctx_parseunc(struct smb_ctx *ctx, const char *unc, int sharetype,
if (error) {
smb_error(dgettext(TEXT_DOMAIN,
"unexpected end of line"), 0);
- return (error);
+ goto out;
}
}
if (*p1 == 0 && ctx->ct_minlevel >= SMBL_SHARE &&
!(ctx->ct_flags & SMBCF_BROWSEOK)) {
smb_error(dgettext(TEXT_DOMAIN, "empty share name"), 0);
- return (EINVAL);
+ error = EINVAL;
+ goto out;
+ }
+ if (next)
+ *next = p;
+ if (*p1 == 0) {
+ error = 0;
+ goto out;
}
- *next = p;
- if (*p1 == 0)
- return (0);
error = smb_ctx_setshare(ctx, unpercent(p1), sharetype);
+
+out:
+ if (error == 0 && smb_debug > 0)
+ dump_ctx("after smb_ctx_parseunc", ctx);
+
return (error);
}
+#ifdef KICONV_SUPPORT
int
smb_ctx_setcharset(struct smb_ctx *ctx, const char *arg)
{
@@ -562,98 +648,43 @@ smb_ctx_setcharset(struct smb_ctx *ctx, const char *arg)
servercs[0] = 0;
return (error);
}
+#endif /* KICONV_SUPPORT */
int
-smb_ctx_setfullserver(struct smb_ctx *ctx, const char *name)
+smb_ctx_setauthflags(struct smb_ctx *ctx, int flags)
{
- ctx->ct_fullserver = strdup(name);
- if (ctx->ct_fullserver == NULL)
- return (ENOMEM);
+ ctx->ct_authflags = flags;
return (0);
}
-/*
- * XXX TODO FIXME etc etc
- * If the call to nbns_getnodestatus(...) fails we can try one of two other
- * methods; use a name of "*SMBSERVER", which is supported by Samba (at least)
- * or, as a last resort, try the "truncate-at-dot" heuristic.
- * And the heuristic really should attempt truncation at
- * each dot in turn, left to right.
- *
- * These fallback heuristics should be triggered when the attempt to open the
- * session fails instead of in the code below.
- *
- * See http://ietf.org/internet-drafts/draft-crhertel-smb-url-07.txt
- */
int
-smb_ctx_getnbname(struct smb_ctx *ctx, struct sockaddr *sap)
+smb_ctx_setfullserver(struct smb_ctx *ctx, const char *name)
{
- char server[SMB_MAXSRVNAMELEN + 1];
- char workgroup[SMB_MAXUSERNAMELEN + 1];
- int error;
-#if 0
- char *dot;
-#endif
+ char *p = strdup(name);
- server[0] = workgroup[0] = '\0';
- error = nbns_getnodestatus(sap, ctx->ct_nb, server, workgroup);
- if (error == 0) {
- /*
- * Used to set our domain name to be the same as
- * the server's domain name. Unnecessary at best,
- * and wrong for accounts in a trusted domain.
- */
-#ifdef APPLE
- if (workgroup[0] && !ctx->ct_ssn.ioc_workgroup[0])
- smb_ctx_setworkgroup(ctx, workgroup, 0);
-#endif
- if (server[0])
- smb_ctx_setserver(ctx, server);
- } else {
- if (smb_verbose)
- smb_error(dgettext(TEXT_DOMAIN,
- "Failed to get NetBIOS node status."), 0);
- if (ctx->ct_ssn.ioc_srvname[0] == (char)0)
- smb_ctx_setserver(ctx, "*SMBSERVER");
- }
-#if 0
- if (server[0] == (char)0) {
- dot = strchr(ctx->ct_fullserver, '.');
- if (dot)
- *dot = '\0';
- if (strlen(ctx->ct_fullserver) <= SMB_MAXSRVNAMELEN) {
- /*
- * don't uppercase the server name. it comes from
- * NBNS and uppercasing can clobber the characters
- */
- strcpy(ctx->ct_ssn.ioc_srvname, ctx->ct_fullserver);
- error = 0;
- } else {
- error = -1;
- }
- if (dot)
- *dot = '.';
- }
-#endif
- return (error);
+ if (p == NULL)
+ return (ENOMEM);
+ if (ctx->ct_fullserver)
+ free(ctx->ct_fullserver);
+ ctx->ct_fullserver = p;
+ return (0);
}
/* this routine does not uppercase the server name */
-void
+int
smb_ctx_setserver(struct smb_ctx *ctx, const char *name)
{
/* don't uppercase the server name */
- if (strlen(name) > SMB_MAXSRVNAMELEN) { /* NB limit is 15 */
- ctx->ct_ssn.ioc_srvname[0] = '\0';
- } else
- strcpy(ctx->ct_ssn.ioc_srvname, name);
+ strlcpy(ctx->ct_srvname, name,
+ sizeof (ctx->ct_srvname));
+ return (0);
}
int
smb_ctx_setuser(struct smb_ctx *ctx, const char *name, int from_cmd)
{
- if (strlen(name) >= SMB_MAXUSERNAMELEN) {
+ if (strlen(name) >= sizeof (ctx->ct_user)) {
smb_error(dgettext(TEXT_DOMAIN,
"user name '%s' too long"), 0, name);
return (ENAMETOOLONG);
@@ -667,7 +698,8 @@ smb_ctx_setuser(struct smb_ctx *ctx, const char *name, int from_cmd)
return (0);
/* don't uppercase the username, just copy it. */
- strcpy(ctx->ct_ssn.ioc_user, name);
+ strlcpy(ctx->ct_user, name,
+ sizeof (ctx->ct_user));
/* Mark this as "from the command line". */
if (from_cmd)
@@ -686,10 +718,10 @@ smb_ctx_setuser(struct smb_ctx *ctx, const char *name, int from_cmd)
* See smb_ctx_init() for notes about this.
*/
int
-smb_ctx_setworkgroup(struct smb_ctx *ctx, const char *name, int from_cmd)
+smb_ctx_setdomain(struct smb_ctx *ctx, const char *name, int from_cmd)
{
- if (strlen(name) >= SMB_MAXUSERNAMELEN) {
+ if (strlen(name) >= sizeof (ctx->ct_domain)) {
smb_error(dgettext(TEXT_DOMAIN,
"workgroup name '%s' too long"), 0, name);
return (ENAMETOOLONG);
@@ -702,7 +734,8 @@ smb_ctx_setworkgroup(struct smb_ctx *ctx, const char *name, int from_cmd)
if (!from_cmd && (ctx->ct_flags & SMBCF_CMD_DOM))
return (0);
- strcpy(ctx->ct_ssn.ioc_workgroup, name);
+ strlcpy(ctx->ct_domain, name,
+ sizeof (ctx->ct_domain));
/* Mark this as "from the command line". */
if (from_cmd)
@@ -714,26 +747,41 @@ smb_ctx_setworkgroup(struct smb_ctx *ctx, const char *name, int from_cmd)
int
smb_ctx_setpassword(struct smb_ctx *ctx, const char *passwd, int from_cmd)
{
+ int err;
- if (passwd == NULL) /* XXX Huh? */
+ if (passwd == NULL)
return (EINVAL);
- if (strlen(passwd) >= SMB_MAXPASSWORDLEN) {
+ if (strlen(passwd) >= sizeof (ctx->ct_password)) {
smb_error(dgettext(TEXT_DOMAIN, "password too long"), 0);
return (ENAMETOOLONG);
}
/*
- * Don't overwrite a value from the command line
- * with one from anywhere else.
+ * If called again after comand line parsing,
+ * don't overwrite a value from the command line
+ * with one from any stored config.
*/
if (!from_cmd && (ctx->ct_flags & SMBCF_CMD_PW))
return (0);
+ memset(ctx->ct_password, 0, sizeof (ctx->ct_password));
if (strncmp(passwd, "$$1", 3) == 0)
- smb_simpledecrypt(ctx->ct_ssn.ioc_password, passwd);
+ smb_simpledecrypt(ctx->ct_password, passwd);
else
- strcpy(ctx->ct_ssn.ioc_password, passwd);
- strcpy(ctx->ct_sh.ioc_password, ctx->ct_ssn.ioc_password);
+ strlcpy(ctx->ct_password, passwd,
+ sizeof (ctx->ct_password));
+
+ /*
+ * Compute LM hash, NT hash.
+ */
+ if (ctx->ct_password[0]) {
+ err = ntlm_compute_nt_hash(ctx->ct_nthash, ctx->ct_password);
+ if (err != 0)
+ return (err);
+ err = ntlm_compute_lm_hash(ctx->ct_lmhash, ctx->ct_password);
+ if (err != 0)
+ return (err);
+ }
/* Mark this as "from the command line". */
if (from_cmd)
@@ -742,10 +790,37 @@ smb_ctx_setpassword(struct smb_ctx *ctx, const char *passwd, int from_cmd)
return (0);
}
+/*
+ * Use this to set NTLM auth. info (hashes)
+ * when we don't have the password.
+ */
+int
+smb_ctx_setpwhash(smb_ctx_t *ctx,
+ const uchar_t *nthash, const uchar_t *lmhash)
+{
+
+ /* Need ct_password to be non-null. */
+ if (ctx->ct_password[0] == '\0')
+ strlcpy(ctx->ct_password, "$HASH",
+ sizeof (ctx->ct_password));
+
+ /*
+ * Compute LM hash, NT hash.
+ */
+ memcpy(ctx->ct_nthash, nthash, NTLM_HASH_SZ);
+
+ /* The LM hash is optional */
+ if (lmhash) {
+ memcpy(ctx->ct_nthash, nthash, NTLM_HASH_SZ);
+ }
+
+ return (0);
+}
+
int
smb_ctx_setshare(struct smb_ctx *ctx, const char *share, int stype)
{
- if (strlen(share) >= SMB_MAXSHARENAMELEN) {
+ if (strlen(share) >= SMBIOC_MAX_NAME) {
smb_error(dgettext(TEXT_DOMAIN,
"share name '%s' too long"), 0, share);
return (ENAMETOOLONG);
@@ -754,10 +829,9 @@ smb_ctx_setshare(struct smb_ctx *ctx, const char *share, int stype)
free(ctx->ct_origshare);
if ((ctx->ct_origshare = strdup(share)) == NULL)
return (ENOMEM);
- nls_str_upper(ctx->ct_sh.ioc_share, share);
- if (share[0] != 0)
- ctx->ct_parsedlevel = SMBL_SHARE;
- ctx->ct_sh.ioc_stype = stype;
+
+ ctx->ct_shtype_req = stype;
+
return (0);
}
@@ -766,9 +840,9 @@ smb_ctx_setsrvaddr(struct smb_ctx *ctx, const char *addr)
{
if (addr == NULL || addr[0] == 0)
return (EINVAL);
- if (ctx->ct_srvaddr)
- free(ctx->ct_srvaddr);
- if ((ctx->ct_srvaddr = strdup(addr)) == NULL)
+ if (ctx->ct_srvaddr_s)
+ free(ctx->ct_srvaddr_s);
+ if ((ctx->ct_srvaddr_s = strdup(addr)) == NULL)
return (ENOMEM);
return (0);
}
@@ -784,7 +858,7 @@ smb_parse_owner(char *pair, uid_t *uid, gid_t *gid)
cp = strchr(pair, ':');
if (cp) {
*cp++ = '\0';
- if (*cp) {
+ if (*cp && gid) {
if (getgrnam_r(cp, &gr, buf, sizeof (buf)) != NULL) {
*gid = gr.gr_gid;
} else
@@ -824,11 +898,8 @@ smb_ctx_opt(struct smb_ctx *ctx, int opt, const char *arg)
error = smb_ctx_setsrvaddr(ctx, arg);
break;
case 'M':
- ctx->ct_ssn.ioc_rights = strtol(arg, &cp, 8);
- if (*cp == '/') {
- ctx->ct_sh.ioc_rights = strtol(cp + 1, &cp, 8);
- ctx->ct_flags |= SMBCF_SRIGHTS;
- }
+ /* share connect rights - ignored */
+ ctx->ct_flags |= SMBCF_SRIGHTS;
break;
case 'N':
ctx->ct_flags |= SMBCF_NOPWD;
@@ -836,61 +907,40 @@ smb_ctx_opt(struct smb_ctx *ctx, int opt, const char *arg)
case 'O':
p = strdup(arg);
cp = strchr(p, '/');
- if (cp) {
- *cp++ = '\0';
- error = smb_parse_owner(cp, &ctx->ct_sh.ioc_owner,
- &ctx->ct_sh.ioc_group);
- }
- if (*p && error == 0) {
- error = smb_parse_owner(cp, &ctx->ct_ssn.ioc_owner,
- &ctx->ct_ssn.ioc_group);
- }
+ if (cp)
+ *cp = '\0';
+ error = smb_parse_owner(cp, &ctx->ct_owner, NULL);
free(p);
break;
case 'P':
-/* ctx->ct_ssn.ioc_opt |= SMBCOPT_PERMANENT; */
+/* ctx->ct_vopt |= SMBCOPT_PERMANENT; */
break;
case 'R':
- ctx->ct_ssn.ioc_retrycount = atoi(arg);
+ /* retry count - ignored */
break;
case 'T':
- ctx->ct_ssn.ioc_timeout = atoi(arg);
+ /* timeout - ignored */
break;
- case 'W':
+ case 'D': /* domain */
+ case 'W': /* workgroup (legacy alias) */
nls_str_upper(tmp, arg);
- error = smb_ctx_setworkgroup(ctx, tmp, TRUE);
+ error = smb_ctx_setdomain(ctx, tmp, TRUE);
break;
}
return (error);
}
-#if 0
-static void
-smb_hexdump(const uchar_t *buf, int len) {
- int ofs = 0;
-
- while (len--) {
- if (ofs % 16 == 0)
- printf("\n%02X: ", ofs);
- printf("%02x ", *buf++);
- ofs++;
- }
- printf("\n");
-}
-#endif
-
+/*
+ * Original code injected iconv tables into the kernel.
+ * Not sure if we'll need this or not... REVISIT
+ */
+#ifdef KICONV_SUPPORT
static int
smb_addiconvtbl(const char *to, const char *from, const uchar_t *tbl)
{
- int error;
+ int error = 0;
- /*
- * Not able to find out what is the work of this routine till
- * now. Still investigating.
- * REVISIT
- */
-#ifdef KICONV_SUPPORT
error = kiconv_add_xlat_table(to, from, tbl);
if (error && error != EEXIST) {
smb_error(dgettext(TEXT_DOMAIN,
@@ -898,47 +948,44 @@ smb_addiconvtbl(const char *to, const char *from, const uchar_t *tbl)
error, from, to);
return (error);
}
-#endif
- return (0);
+ return (error);
}
+#endif /* KICONV_SUPPORT */
/*
- * Verify context before connect operation(s),
+ * Verify context info. before connect operation(s),
* lookup specified server and try to fill all forgotten fields.
+ * Legacy name used by commands.
*/
int
smb_ctx_resolve(struct smb_ctx *ctx)
{
struct smbioc_ossn *ssn = &ctx->ct_ssn;
- struct smbioc_oshare *sh = &ctx->ct_sh;
- struct nb_name nn;
- struct sockaddr *sap;
- struct sockaddr_nb *salocal, *saserver;
- char *cp;
+ int error = 0;
+#ifdef KICONV_SUPPORT
uchar_t cstbl[256];
uint_t i;
- int error = 0;
- int browseok = ctx->ct_flags & SMBCF_BROWSEOK;
- int renego = 0;
+#endif
ctx->ct_flags &= ~SMBCF_RESOLVED;
- if (isatty(STDIN_FILENO))
- browseok = 0;
- if (ctx->ct_fullserver == NULL || ctx->ct_fullserver[0] == 0) {
+
+ if (ctx->ct_fullserver == NULL) {
smb_error(dgettext(TEXT_DOMAIN,
"no server name specified"), 0);
return (EINVAL);
}
- if (ctx->ct_minlevel >= SMBL_SHARE && sh->ioc_share[0] == 0 &&
- !browseok) {
+
+ if (ctx->ct_minlevel >= SMBL_SHARE &&
+ ctx->ct_origshare == NULL) {
smb_error(dgettext(TEXT_DOMAIN,
"no share name specified for %s@%s"),
- 0, ssn->ioc_user, ssn->ioc_srvname);
+ 0, ssn->ssn_user, ctx->ct_fullserver);
return (EINVAL);
}
error = nb_ctx_resolve(ctx->ct_nb);
if (error)
return (error);
+#ifdef KICONV_SUPPORT
if (ssn->ioc_localcs[0] == 0)
strcpy(ssn->ioc_localcs, "default"); /* XXX: locale name ? */
error = smb_addiconvtbl("tolower", ssn->ioc_localcs, nls_lower);
@@ -963,177 +1010,57 @@ smb_ctx_resolve(struct smb_ctx *ctx)
if (error)
return (error);
}
- /*
- * If we have an explicit address set for the server in
- * an "addr=X" setting in .nsmbrc or SMF, just try using a
- * gethostbyname() lookup for it.
- */
- if (ctx->ct_srvaddr) {
- error = nb_resolvehost_in(ctx->ct_srvaddr, &sap);
- if (error == 0)
- (void) smb_ctx_getnbname(ctx, sap);
- } else
- error = -1;
-
- /*
- * Next try a gethostbyname() lookup on the original user-
- * specified server name. This is similar to Windows
- * NBT option "Use DNS for name resolution."
- */
- if (error && ctx->ct_fullserver) {
- error = nb_resolvehost_in(ctx->ct_fullserver, &sap);
- if (error == 0)
- (void) smb_ctx_getnbname(ctx, sap);
- }
+#endif /* KICONV_SUPPORT */
/*
- * Finally, try the shorter, upper-cased ssn->ioc_srvname
- * with a NBNS/WINS lookup if the "nbns_enable" property is
- * true (the default). nbns_resolvename() may unicast to the
- * "nbns" server or broadcast on the subnet.
+ * Lookup the IP address.
+ * Puts a list in ct_addrinfo
*/
- if (error && ssn->ioc_srvname[0] &&
- ctx->ct_nb->nb_flags & NBCF_NS_ENABLE) {
- error = nbns_resolvename(ssn->ioc_srvname,
- ctx->ct_nb, &sap);
- /*
- * Used to get the NetBIOS node status here.
- * Not necessary (we have the NetBIOS name).
- */
- }
+ error = smb_ctx_getaddr(ctx);
if (error) {
smb_error(dgettext(TEXT_DOMAIN,
"can't get server address"), error);
return (error);
}
+ assert(ctx->ct_addrinfo != NULL);
- /* XXX: no nls_str_upper(ssn->ioc_srvname) here? */
-
- assert(sizeof (nn.nn_name) == sizeof (ssn->ioc_srvname));
- memcpy(nn.nn_name, ssn->ioc_srvname, NB_NAMELEN);
- nn.nn_type = NBT_SERVER;
- nn.nn_scope = ctx->ct_nb->nb_scope;
-
- error = nb_sockaddr(sap, &nn, &saserver);
- memcpy(&ctx->ct_srvinaddr, sap, sizeof (struct sockaddr_in));
- nb_snbfree(sap);
- if (error) {
- smb_error(dgettext(TEXT_DOMAIN,
- "can't allocate server address"), error);
- return (error);
- }
- /* We know it's a NetBIOS address here. */
- bcopy(saserver, &ssn->ioc_server.nb,
- sizeof (struct sockaddr_nb));
- if (ctx->ct_locname[0] == 0) {
- error = nb_getlocalname(ctx->ct_locname,
- SMB_MAXUSERNAMELEN + 1);
- if (error) {
- smb_error(dgettext(TEXT_DOMAIN,
- "can't get local name"), error);
- return (error);
- }
- nls_str_upper(ctx->ct_locname, ctx->ct_locname);
- }
-
- /* XXX: no nls_str_upper(ctx->ct_locname); here? */
-
- memcpy(nn.nn_name, ctx->ct_locname, NB_NAMELEN);
- nn.nn_type = NBT_WKSTA;
- nn.nn_scope = ctx->ct_nb->nb_scope;
-
- error = nb_sockaddr(NULL, &nn, &salocal);
- if (error) {
- nb_snbfree((struct sockaddr *)saserver);
- smb_error(dgettext(TEXT_DOMAIN,
- "can't allocate local address"), error);
- return (error);
- }
-
- /* We know it's a NetBIOS address here. */
- bcopy(salocal, &ssn->ioc_local.nb,
- sizeof (struct sockaddr_nb));
-
- error = smb_ctx_findvc(ctx, SMBL_VC, 0);
- if (error == 0) {
- /* re-use and existing VC */
- ctx->ct_flags |= SMBCF_RESOLVED | SMBCF_SSNACTIVE;
- return (0);
- }
-
- /* Make a new connection via smb_ctx_negotiate()... */
- error = smb_ctx_negotiate(ctx, SMBL_SHARE, SMBLK_CREATE,
- ssn->ioc_workgroup);
- if (error)
- return (error);
- ctx->ct_flags &= ~SMBCF_AUTHREQ;
- if (!ctx->ct_secblob && browseok && !sh->ioc_share[0] &&
- !(ctx->ct_flags & SMBCF_XXX)) {
- /* assert: anon share list is subset of overall server shares */
- error = smb_browse(ctx, 1);
- if (error) /* user cancel or other error? */
- return (error);
+ /*
+ * If we have a user name but no password,
+ * check for a keychain entry.
+ * XXX: Only for auth NTLM?
+ */
+ if (ctx->ct_user[0] == '\0') {
/*
- * A share was selected, authenticate button was pressed,
- * or anon-authentication failed getting browse list.
+ * No user name (anonymous session).
+ * The minauth checks do not apply.
*/
- }
- if ((ctx->ct_secblob == NULL) && (ctx->ct_flags & SMBCF_AUTHREQ ||
- (ssn->ioc_password[0] == '\0' &&
- !(ctx->ct_flags & SMBCF_NOPWD)))) {
-reauth:
+ ctx->ct_authflags = SMB_AT_ANON;
+ } else {
/*
- * This function is implemented in both
- * ui-apple.c and ui-sun.c so let's try to
- * keep the same interface. Not sure why
- * they didn't just pass ssn here.
+ * Have a user name.
+ * If we don't have a p/w yet,
+ * try the keychain.
*/
- error = smb_get_authentication(
- ssn->ioc_workgroup, sizeof (ssn->ioc_workgroup) - 1,
- ssn->ioc_user, sizeof (ssn->ioc_user) - 1,
- ssn->ioc_password, sizeof (ssn->ioc_password) - 1,
- ssn->ioc_srvname, ctx);
- if (error)
- return (error);
- }
- /*
- * if we have a session it is either anonymous
- * or from a stale authentication. re-negotiating
- * gets us ready for a fresh session
- */
- if (ctx->ct_flags & SMBCF_SSNACTIVE || renego) {
- renego = 0;
- /* don't clobber workgroup name, pass null arg */
- error = smb_ctx_negotiate(ctx, SMBL_SHARE, SMBLK_CREATE, NULL);
- if (error)
- return (error);
- }
- if (browseok && !sh->ioc_share[0]) {
- ctx->ct_flags &= ~SMBCF_AUTHREQ;
- error = smb_browse(ctx, 0);
- if (ctx->ct_flags & SMBCF_KCFOUND && smb_autherr(error)) {
- smb_error(dgettext(TEXT_DOMAIN,
- "smb_ctx_resolve: bad keychain entry"), 0);
- ctx->ct_flags |= SMBCF_KCBAD;
- renego = 1;
- goto reauth;
- }
- if (error) /* auth, user cancel, or other error */
- return (error);
+ if (ctx->ct_password[0] == '\0')
+ (void) smb_get_keychain(ctx);
/*
- * Re-authenticate button was pressed?
+ * If we're doing p/w based auth,
+ * that means not using Kerberos.
*/
- if (ctx->ct_flags & SMBCF_AUTHREQ)
- goto reauth;
- if (!sh->ioc_share[0] && !(ctx->ct_flags & SMBCF_XXX)) {
- smb_error(dgettext(TEXT_DOMAIN,
- "no share specified for %s@%s"),
- 0, ssn->ioc_user, ssn->ioc_srvname);
- return (EINVAL);
- }
+ if (ctx->ct_password[0] != '\0')
+ ctx->ct_authflags &= ~SMB_AT_KRB5;
+ /*
+ * Mask out disallowed auth types.
+ */
+ ctx->ct_authflags &= ctx->ct_minauth;
+ }
+ if (ctx->ct_authflags == 0) {
+ smb_error(dgettext(TEXT_DOMAIN,
+ "no valid auth. types"), 0);
+ return (ENOTSUP);
}
- ctx->ct_flags |= SMBCF_RESOLVED;
+ ctx->ct_flags |= SMBCF_RESOLVED;
if (smb_debug)
dump_ctx("after smb_ctx_resolve", ctx);
@@ -1143,50 +1070,23 @@ reauth:
int
smb_open_driver()
{
- char buf[20];
- int err, fd, i;
+ int err, fd;
uint32_t version;
- /*
- * First try to open as clone
- */
fd = open("/dev/"NSMB_NAME, O_RDWR);
- if (fd >= 0)
- goto opened;
-
- err = errno; /* from open */
-#ifdef APPLE
- /*
- * well, no clone capabilities available - we have to scan
- * all devices in order to get free one
- */
- for (i = 0; i < 1024; i++) {
- snprintf(buf, sizeof (buf), "/dev/%s%d", NSMB_NAME, i);
- fd = open(buf, O_RDWR);
- if (fd >= 0)
- goto opened;
- if (i && POWEROF2(i+1))
- smb_error(dgettext(TEXT_DOMAIN,
- "%d failures to open smb device"), errno, i+1);
+ if (fd < 0) {
+ err = errno;
+ smb_error(dgettext(TEXT_DOMAIN,
+ "failed to open driver"), err);
+ return (-1);
}
- err = ENOENT;
-#endif
- smb_error(dgettext(TEXT_DOMAIN,
- "failed to open %s"), err, "/dev/" NSMB_NAME);
- return (-1);
-opened:
/*
* Check the driver version (paranoia)
* Do this BEFORE any other ioctl calls.
*/
- if (ioctl(fd, SMBIOC_GETVERS, &version) < 0) {
- err = errno;
- smb_error(dgettext(TEXT_DOMAIN,
- "failed to get driver version"), err);
- close(fd);
- return (-1);
- }
+ if (ioctl(fd, SMBIOC_GETVERS, &version) < 0)
+ version = 0;
if (version != NSMB_VERSION) {
smb_error(dgettext(TEXT_DOMAIN,
"incorrect driver version"), 0);
@@ -1194,18 +1094,21 @@ opened:
return (-1);
}
+ /* This handle controls per-process resources. */
+ (void) fcntl(fd, F_SETFD, FD_CLOEXEC);
+
return (fd);
}
-static int
+int
smb_ctx_gethandle(struct smb_ctx *ctx)
{
- int err, fd;
+ int fd;
- if (ctx->ct_fd != -1) {
+ if (ctx->ct_dev_fd != -1) {
rpc_cleanup_smbctx(ctx);
- close(ctx->ct_fd);
- ctx->ct_fd = -1;
+ close(ctx->ct_dev_fd);
+ ctx->ct_dev_fd = -1;
ctx->ct_flags &= ~SMBCF_SSNACTIVE;
}
@@ -1213,733 +1116,195 @@ smb_ctx_gethandle(struct smb_ctx *ctx)
if (fd < 0)
return (ENODEV);
- ctx->ct_fd = fd;
+ ctx->ct_dev_fd = fd;
return (0);
}
-int
-smb_ctx_ioctl(struct smb_ctx *ctx, int inum, struct smbioc_lookup *rqp)
-{
- size_t siz = DEF_SEC_TOKEN_LEN;
- int rc = 0;
- struct sockaddr sap1, sap2;
- int i;
-
- if (rqp->ioc_ssn.ioc_outtok)
- free(rqp->ioc_ssn.ioc_outtok);
- rqp->ioc_ssn.ioc_outtoklen = siz;
- rqp->ioc_ssn.ioc_outtok = malloc(siz+1);
- if (rqp->ioc_ssn.ioc_outtok == NULL)
- return (ENOMEM);
- bzero(rqp->ioc_ssn.ioc_outtok, siz+1);
- /* Note: No longer put length in outtok[0] */
- /* *((int *)rqp->ioc_ssn.ioc_outtok) = (int)siz; */
-
- if (ioctl(ctx->ct_fd, inum, rqp) == -1) {
- rc = errno;
- goto out;
- }
- if (rqp->ioc_ssn.ioc_outtoklen <= siz)
- goto out;
-
- /*
- * Operation completed, but our output token wasn't large enough.
- * The re-call below only pulls the token from the kernel.
- */
- siz = rqp->ioc_ssn.ioc_outtoklen;
- free(rqp->ioc_ssn.ioc_outtok);
- rqp->ioc_ssn.ioc_outtok = malloc(siz + 1);
- if (rqp->ioc_ssn.ioc_outtok == NULL) {
- rc = ENOMEM;
- goto out;
- }
- bzero(rqp->ioc_ssn.ioc_outtok, siz+1);
- /* Note: No longer put length in outtok[0] */
- /* *((int *)rqp->ioc_ssn.ioc_outtok) = siz; */
- if (ioctl(ctx->ct_fd, inum, rqp) == -1)
- rc = errno;
-out:
- return (rc);
-}
-
-int
-smb_ctx_findvc(struct smb_ctx *ctx, int level, int flags)
-{
- struct smbioc_lookup rq;
- int error = 0;
-
- if ((error = smb_ctx_gethandle(ctx)))
- return (error);
-
- bzero(&rq, sizeof (rq));
- bcopy(&ctx->ct_ssn, &rq.ioc_ssn, sizeof (struct smbioc_ossn));
- bcopy(&ctx->ct_sh, &rq.ioc_sh, sizeof (struct smbioc_oshare));
-
- rq.ioc_flags = flags;
- rq.ioc_level = level;
-
- return (smb_ctx_ioctl(ctx, SMBIOC_FINDVC, &rq));
-}
/*
- * adds a GSSAPI wrapper
+ * Find or create a connection + logon session
*/
-char *
-smb_ctx_tkt2gtok(uchar_t *tkt, ulong_t tktlen,
- uchar_t **gtokp, ulong_t *gtoklenp)
-{
- ulong_t bloblen = tktlen;
- ulong_t len;
- uchar_t krbapreq[2] = "\x01\x00"; /* see RFC 1964 */
- char *failure;
- uchar_t *blob = NULL; /* result */
- uchar_t *b;
-
- bloblen += sizeof (krbapreq);
- bloblen += g_stcMechOIDList[spnego_mech_oid_Kerberos_V5].iLen;
- len = bloblen;
- bloblen = ASNDerCalcTokenLength(bloblen, bloblen);
- failure = dgettext(TEXT_DOMAIN, "smb_ctx_tkt2gtok malloc");
- if (!(blob = malloc(bloblen)))
- goto out;
- b = blob;
- b += ASNDerWriteToken(b, SPNEGO_NEGINIT_APP_CONSTRUCT, NULL, len);
- b += ASNDerWriteOID(b, spnego_mech_oid_Kerberos_V5);
- memcpy(b, krbapreq, sizeof (krbapreq));
- b += sizeof (krbapreq);
- failure = dgettext(TEXT_DOMAIN, "smb_ctx_tkt2gtok insanity check");
- if (b + tktlen != blob + bloblen)
- goto out;
- memcpy(b, tkt, tktlen);
- *gtoklenp = bloblen;
- *gtokp = blob;
- failure = NULL;
-out:;
- if (blob && failure)
- free(blob);
- return (failure);
-}
-
-
-/*
- * Initialization for Kerberos, pulled out of smb_ctx_principal2tkt.
- * This just gets our cached credentials, if we have any.
- * Based on the "klist" command.
- */
-char *
-smb_ctx_krb5init(struct smb_ctx *ctx)
+int
+smb_ctx_get_ssn(struct smb_ctx *ctx)
{
- char *failure;
- krb5_error_code kerr;
- krb5_context kctx = NULL;
- krb5_ccache kcc = NULL;
- krb5_principal kprin = NULL;
-
- kerr = krb5_init_context(&kctx);
- if (kerr) {
- failure = "krb5_init_context";
- goto out;
- }
- ctx->ct_krb5ctx = kctx;
-
- /* non-default would instead use krb5_cc_resolve */
- kerr = krb5_cc_default(kctx, &kcc);
- if (kerr) {
- failure = "krb5_cc_default";
- goto out;
- }
- ctx->ct_krb5cc = kcc;
+ int err = 0;
- /*
- * Get the client principal (ticket),
- * or find out if we don't have one.
- */
- kerr = krb5_cc_get_principal(kctx, kcc, &kprin);
- if (kerr) {
- failure = "krb5_cc_get_principal";
- goto out;
- }
- ctx->ct_krb5cp = kprin;
-
- if (smb_verbose) {
- fprintf(stderr, gettext("Ticket cache: %s:%s\n"),
- krb5_cc_get_type(kctx, kcc),
- krb5_cc_get_name(kctx, kcc));
- }
- failure = NULL;
-
-out:
- return (failure);
-}
-
-
-/*
- * See "Windows 2000 Kerberos Interoperability" paper by
- * Christopher Nebergall. RC4 HMAC is the W2K default but
- * Samba support lagged (not due to Samba itself, but due to OS'
- * Kerberos implementations.)
- *
- * Only session enc type should matter, not ticket enc type,
- * per Sam Hartman on krbdev.
- *
- * Preauthentication failure topics in krb-protocol may help here...
- * try "John Brezak" and/or "Clifford Neuman" too.
- */
-static krb5_enctype kenctypes[] = {
- ENCTYPE_ARCFOUR_HMAC, /* defined in Tiger krb5.h */
- ENCTYPE_DES_CBC_MD5,
- ENCTYPE_DES_CBC_CRC,
- ENCTYPE_NULL
-};
+ if ((ctx->ct_flags & SMBCF_RESOLVED) == 0)
+ return (EINVAL);
-/*
- * Obtain a kerberos ticket...
- * (if TLD != "gov" then pray first)
- */
-char *
-smb_ctx_principal2tkt(
- struct smb_ctx *ctx, char *prin,
- uchar_t **tktp, ulong_t *tktlenp)
-{
- char *failure;
- krb5_context kctx = NULL;
- krb5_error_code kerr;
- krb5_ccache kcc = NULL;
- krb5_principal kprin = NULL, cprn = NULL;
- krb5_creds kcreds, *kcredsp = NULL;
- krb5_auth_context kauth = NULL;
- krb5_data kdata, kdata0;
- uchar_t *tkt;
-
- memset((char *)&kcreds, 0, sizeof (kcreds));
- kdata0.length = 0;
-
- /* These shoud have been done in smb_ctx_krb5init() */
- if (ctx->ct_krb5ctx == NULL ||
- ctx->ct_krb5cc == NULL ||
- ctx->ct_krb5cp == NULL) {
- failure = "smb_ctx_krb5init";
- goto out;
+ if (ctx->ct_dev_fd < 0) {
+ if ((err = smb_ctx_gethandle(ctx)))
+ return (err);
}
- kctx = ctx->ct_krb5ctx;
- kcc = ctx->ct_krb5cc;
- cprn = ctx->ct_krb5cp;
- failure = "krb5_set_default_tgs_enctypes";
- if ((kerr = krb5_set_default_tgs_enctypes(kctx, kenctypes)))
- goto out;
/*
- * The following is an unrolling of krb5_mk_req. Something like:
- * krb5_mk_req(kctx, &kauth, 0, service(prin), hostname(prin),
- * &kdata0, kcc, &kdata);)
- * ...except we needed krb5_parse_name not krb5_sname_to_principal.
+ * Check whether the driver already has a VC
+ * we can use. If so, we're done!
*/
- failure = "krb5_parse_name";
- if ((kerr = krb5_parse_name(kctx, prin, &kprin)))
- goto out;
- failure = "krb5_copy_principal(server)";
- if ((kerr = krb5_copy_principal(kctx, kprin, &kcreds.server)))
- goto out;
- failure = "krb5_copy_principal(client)";
- if ((kerr = krb5_copy_principal(kctx, cprn, &kcreds.client)))
- goto out;
- failure = "krb5_get_credentials";
- if ((kerr = krb5_get_credentials(kctx, 0, kcc, &kcreds, &kcredsp)))
- goto out;
- failure = "krb5_mk_req_extended";
- if ((kerr = krb5_mk_req_extended(kctx, &kauth, 0, &kdata0, kcredsp,
- &kdata)))
- goto out;
- failure = "malloc";
- if (!(tkt = malloc(kdata.length))) {
- krb5_free_data_contents(kctx, &kdata);
- goto out;
- }
- *tktlenp = kdata.length;
- memcpy(tkt, kdata.data, kdata.length);
- krb5_free_data_contents(kctx, &kdata);
- *tktp = tkt;
- failure = NULL;
-out:;
- if (kerr) {
- if (!failure)
- failure = "smb_ctx_principal2tkt";
+ err = smb_ctx_findvc(ctx);
+ if (err == 0) {
+ DPRINT("found an existing VC");
+ } else {
/*
- * Avoid logging the typical "No credentials cache found"
+ * This calls the IOD to create a new session.
*/
- if (kerr != KRB5_FCC_NOFILE ||
- strcmp(failure, "krb5_cc_get_principal"))
- com_err(__progname, kerr, failure);
- }
- if (kauth)
- krb5_auth_con_free(kctx, kauth);
- if (kcredsp)
- krb5_free_creds(kctx, kcredsp);
- if (kcreds.server || kcreds.client)
- krb5_free_cred_contents(kctx, &kcreds);
- if (kprin)
- krb5_free_principal(kctx, kprin);
-
- /* Free kctx in smb_ctx_done */
-
- return (failure);
-}
+ DPRINT("setup a new VC");
+ err = smb_ctx_newvc(ctx);
+ if (err != 0)
+ return (err);
-char *
-smb_ctx_principal2blob(
- struct smb_ctx *ctx,
- smbioc_ossn_t *ssn,
- char *prin)
-{
- int rc = 0;
- char *failure;
- uchar_t *tkt = NULL;
- ulong_t tktlen;
- uchar_t *gtok = NULL; /* gssapi token */
- ulong_t gtoklen; /* gssapi token length */
- SPNEGO_TOKEN_HANDLE stok = NULL; /* spnego token */
- void *blob = NULL; /* result */
- ulong_t bloblen; /* result length */
-
- if ((failure = smb_ctx_principal2tkt(ctx, prin, &tkt, &tktlen)))
- goto out;
- if ((failure = smb_ctx_tkt2gtok(tkt, tktlen, &gtok, &gtoklen)))
- goto out;
- /*
- * RFC says to send NegTokenTarg now. So does MS docs. But
- * win2k gives ERRbaduid if we do... we must send
- * another NegTokenInit now!
- */
- failure = "spnegoCreateNegTokenInit";
- if ((rc = spnegoCreateNegTokenInit(spnego_mech_oid_Kerberos_V5_Legacy,
- 0, gtok, gtoklen, NULL, 0, &stok)))
- goto out;
- failure = "spnegoTokenGetBinary(NULL)";
- rc = spnegoTokenGetBinary(stok, NULL, &bloblen);
- if (rc != SPNEGO_E_BUFFER_TOO_SMALL)
- goto out;
- failure = "malloc";
- if (!(blob = malloc((size_t)bloblen)))
- goto out;
- /* No longer store length at start of blob. */
- /* *blob = bloblen; */
- failure = "spnegoTokenGetBinary";
- if ((rc = spnegoTokenGetBinary(stok, blob, &bloblen)))
- goto out;
- ssn->ioc_intoklen = bloblen;
- ssn->ioc_intok = blob;
- failure = NULL;
-out:;
- if (rc) {
- /* XXX better is to embed rc in failure */
- smb_error(dgettext(TEXT_DOMAIN,
- "spnego principal2blob error %d"), 0, -rc);
- if (!failure)
- failure = "spnego";
+ /*
+ * Call findvc again. The new VC sould be
+ * found in the driver this time.
+ */
+ err = smb_ctx_findvc(ctx);
}
- if (blob && failure)
- free(blob);
- if (stok)
- spnegoFreeData(stok);
- if (gtok)
- free(gtok);
- if (tkt)
- free(tkt);
- return (failure);
-}
-
-#if 0
-void
-prblob(uchar_t *b, size_t len)
-{
- while (len--)
- fprintf(stderr, "%02x", *b++);
- fprintf(stderr, "\n");
+ return (err);
}
-#endif
-
/*
- * We navigate the SPNEGO & ASN1 encoding to find a kerberos principal
- * Note: driver no longer puts length at start of blob.
+ * Get the string representation of a share "use" type,
+ * as needed for the "service" in tree connect.
*/
-char *
-smb_ctx_blob2principal(
- struct smb_ctx *ctx,
- smbioc_ossn_t *ssn,
- char **prinp)
+static const char *
+smb_use_type_str(smb_use_shtype_t stype)
{
- uchar_t *blob = ssn->ioc_outtok;
- size_t len = ssn->ioc_outtoklen;
- int rc = 0;
- SPNEGO_TOKEN_HANDLE stok = NULL;
- int indx = 0;
- char *failure;
- uchar_t flags = 0;
- unsigned long plen = 0;
- uchar_t *prin;
-
-#if 0
- fprintf(stderr, "blob from negotiate:\n");
- prblob(blob, len);
-#endif
+ const char *pp;
- /* Skip the GUID */
- assert(len >= SMB_GUIDLEN);
- blob += SMB_GUIDLEN;
- len -= SMB_GUIDLEN;
-
- failure = "spnegoInitFromBinary";
- if ((rc = spnegoInitFromBinary(blob, len, &stok)))
- goto out;
- /*
- * Needn't use new Kerberos OID - the Legacy one is fine.
- */
- failure = "spnegoIsMechTypeAvailable";
- if (spnegoIsMechTypeAvailable(stok, spnego_mech_oid_Kerberos_V5_Legacy,
- &indx))
- goto out;
- /*
- * Ignoring optional context flags for now. May want to pass
- * them to krb5 layer. XXX
- */
- if (!spnegoGetContextFlags(stok, &flags))
- fprintf(stderr, dgettext(TEXT_DOMAIN,
- "spnego context flags 0x%x\n"), flags);
- failure = "spnegoGetMechListMIC(NULL)";
- rc = spnegoGetMechListMIC(stok, NULL, &plen);
- if (rc != SPNEGO_E_BUFFER_TOO_SMALL)
- goto out;
- failure = "malloc";
- if (!(prin = malloc(plen + 1)))
- goto out;
- failure = "spnegoGetMechListMIC";
- if ((rc = spnegoGetMechListMIC(stok, prin, &plen))) {
- free(prin);
- goto out;
- }
- prin[plen] = '\0';
- *prinp = (char *)prin;
- failure = NULL;
-out:;
- if (stok)
- spnegoFreeData(stok);
- if (rc) {
- /* XXX better is to embed rc in failure */
- smb_error(dgettext(TEXT_DOMAIN,
- "spnego blob2principal error %d"), 0, -rc);
- if (!failure)
- failure = "spnego";
+ switch (stype) {
+ default:
+ case USE_WILDCARD:
+ pp = "?????";
+ break;
+ case USE_DISKDEV:
+ pp = "A:";
+ break;
+ case USE_SPOOLDEV:
+ pp = "LPT1:";
+ break;
+ case USE_CHARDEV:
+ pp = "COMM";
+ break;
+ case USE_IPC:
+ pp = "IPC";
+ break;
}
- return (failure);
+ return (pp);
}
-
+/*
+ * Find or create a tree connection
+ */
int
-smb_ctx_negotiate(struct smb_ctx *ctx, int level, int flags, char *workgroup)
+smb_ctx_get_tree(struct smb_ctx *ctx)
{
- struct smbioc_lookup rq;
- int error = 0;
- char *failure = NULL;
- char *principal = NULL;
- char c;
- int i;
- ssize_t *outtoklen;
- uchar_t *blob;
+ smbioc_tcon_t *tcon = NULL;
+ const char *stype;
+ int cmd, err = 0;
- /*
- * We leave ct_secblob set iff extended security
- * negotiation succeeds.
- */
- if (ctx->ct_secblob) {
- free(ctx->ct_secblob);
- ctx->ct_secblob = NULL;
- }
-#ifdef XXX
- if ((ctx->ct_flags & SMBCF_RESOLVED) == 0) {
- smb_error(dgettext(TEXT_DOMAIN,
- "smb_ctx_lookup() data is not resolved"), 0);
+ if (ctx->ct_dev_fd < 0 ||
+ ctx->ct_origshare == NULL) {
return (EINVAL);
}
-#endif
- if ((error = smb_ctx_gethandle(ctx)))
- return (error);
-
- bzero(&rq, sizeof (rq));
- bcopy(&ctx->ct_ssn, &rq.ioc_ssn, sizeof (struct smbioc_ossn));
- bcopy(&ctx->ct_sh, &rq.ioc_sh, sizeof (struct smbioc_oshare));
-
- /*
- * Find out if we have a Kerberos ticket,
- * and only offer SPNEGO if we have one.
- */
- failure = smb_ctx_krb5init(ctx);
- if (failure) {
- if (smb_verbose)
- smb_error(failure, 0);
- goto out;
- }
-
- rq.ioc_flags = flags;
- rq.ioc_level = level;
- rq.ioc_ssn.ioc_opt |= SMBVOPT_EXT_SEC;
- error = smb_ctx_ioctl(ctx, SMBIOC_NEGOTIATE, &rq);
- if (error) {
- failure = dgettext(TEXT_DOMAIN, "negotiate failed");
- smb_error(failure, error);
- if (error == ETIMEDOUT)
- return (error);
- goto out;
- }
- /*
- * If the server capabilities did not include
- * SMB_CAP_EXT_SECURITY then the driver clears
- * the flag SMBVOPT_EXT_SEC for us.
- * XXX: should add the capabilities to ioc_ssn
- * XXX: see comment in driver - smb_usr.c
- */
- failure = dgettext(TEXT_DOMAIN, "SPNEGO unsupported");
- if ((rq.ioc_ssn.ioc_opt & SMBVOPT_EXT_SEC) == 0) {
- if (smb_verbose)
- smb_error(failure, 0);
- /*
- * Do regular (old style) NTLM or NTLMv2
- * Nothing more to do here in negotiate.
- */
- return (0);
- }
-
- /*
- * Capabilities DO include SMB_CAP_EXT_SECURITY,
- * so this should be an SPNEGO security blob.
- * Parse the ASN.1/DER, prepare response(s).
- * XXX: Handle STATUS_MORE_PROCESSING_REQUIRED?
- * XXX: Requires additional session setup calls.
- */
- if (rq.ioc_ssn.ioc_outtoklen <= SMB_GUIDLEN)
- goto out;
- /* some servers send padding junk */
- blob = rq.ioc_ssn.ioc_outtok;
- if (blob[0] == 0)
- goto out;
- failure = smb_ctx_blob2principal(
- ctx, &rq.ioc_ssn, &principal);
- if (failure)
- goto out;
- failure = smb_ctx_principal2blob(
- ctx, &rq.ioc_ssn, principal);
- if (failure)
- goto out;
+ cmd = SMBIOC_TREE_CONNECT;
+ tcon = malloc(sizeof (*tcon));
+ if (tcon == NULL)
+ return (ENOMEM);
+ bzero(tcon, sizeof (*tcon));
+ tcon->tc_flags = SMBLK_CREATE;
+ tcon->tc_opt = 0;
- /* Success! Save the blob to send next. */
- ctx->ct_secblob = rq.ioc_ssn.ioc_intok;
- ctx->ct_secbloblen = rq.ioc_ssn.ioc_intoklen;
- rq.ioc_ssn.ioc_intok = NULL;
+ /* The share name */
+ strlcpy(tcon->tc_sh.sh_name, ctx->ct_origshare,
+ sizeof (tcon->tc_sh.sh_name));
-out:
- if (principal)
- free(principal);
- if (rq.ioc_ssn.ioc_intok)
- free(rq.ioc_ssn.ioc_intok);
- if (rq.ioc_ssn.ioc_outtok)
- free(rq.ioc_ssn.ioc_outtok);
- if (!failure)
- return (0); /* Success! */
+ /* The share "use" type. */
+ stype = smb_use_type_str(ctx->ct_shtype_req);
+ strlcpy(tcon->tc_sh.sh_type_req, stype,
+ sizeof (tcon->tc_sh.sh_type_req));
/*
- * Negotiate failed with "extended security".
+ * Todo: share passwords for share-level security.
*
- * XXX: If we are doing SPNEGO correctly,
- * we should never get here unless the user
- * supplied invalid authentication data,
- * or we saw some kind of protocol error.
- *
- * XXX: The error message below should be
- * XXX: unconditional (remove "if verbose")
- * XXX: but not until we have "NTLMSSP"
- * Avoid spew for anticipated failure modes
- * but enable this with the verbose flag
+ * The driver does the actual TCON call.
*/
- if (smb_verbose) {
- smb_error(dgettext(TEXT_DOMAIN,
- "%s (extended security negotiate)"), error, failure);
+ if (ioctl(ctx->ct_dev_fd, cmd, tcon) == -1) {
+ err = errno;
+ goto out;
}
/*
- * XXX: Try again using NTLM (or NTLMv2)
- * XXX: Normal clients don't do this.
- * XXX: Should just return an error, but
- * keep the fall-back to NTLM for now.
- *
- * Start over with a new connection.
+ * Check the returned share type
*/
- if ((error = smb_ctx_gethandle(ctx)))
- return (error);
- bzero(&rq, sizeof (rq));
- bcopy(&ctx->ct_ssn, &rq.ioc_ssn, sizeof (struct smbioc_ossn));
- bcopy(&ctx->ct_sh, &rq.ioc_sh, sizeof (struct smbioc_oshare));
- rq.ioc_flags = flags;
- rq.ioc_level = level;
- /* Note: NO SMBVOPT_EXT_SEC */
- error = smb_ctx_ioctl(ctx, SMBIOC_NEGOTIATE, &rq);
- if (error) {
- failure = dgettext(TEXT_DOMAIN, "negotiate failed");
- smb_error(failure, error);
- rpc_cleanup_smbctx(ctx);
- close(ctx->ct_fd);
- ctx->ct_fd = -1;
- return (error);
+ DPRINT("ret. sh_type: \"%s\"", tcon->tc_sh.sh_type_ret);
+ if (ctx->ct_shtype_req != USE_WILDCARD &&
+ 0 != strcmp(stype, tcon->tc_sh.sh_type_ret)) {
+ smb_error(dgettext(TEXT_DOMAIN,
+ "%s: incompatible share type"),
+ 0, ctx->ct_origshare);
+ err = EINVAL;
}
- /*
- * Used to copy the workgroup out of the SMB_NEGOTIATE response
- * here, to default our domain name to be the same as the server.
- * Not a good idea: Unnecessary at best, and sometimes wrong, i.e.
- * when our account is in a trusted domain.
- */
+out:
+ if (tcon != NULL)
+ free(tcon);
- return (error);
+ return (err);
}
-
+/*
+ * Return the hflags2 word for an smb_ctx.
+ */
int
-smb_ctx_tdis(struct smb_ctx *ctx)
+smb_ctx_flags2(struct smb_ctx *ctx)
{
- struct smbioc_lookup rq; /* XXX may be used, someday */
- int error = 0;
+ uint16_t flags2;
- if (ctx->ct_fd < 0) {
+ if (ioctl(ctx->ct_dev_fd, SMBIOC_FLAGS2, &flags2) == -1) {
smb_error(dgettext(TEXT_DOMAIN,
- "tree disconnect without handle?!"), 0);
- return (EINVAL);
- }
- if (!(ctx->ct_flags & SMBCF_SSNACTIVE)) {
- smb_error(dgettext(TEXT_DOMAIN,
- "tree disconnect without session?!"), 0);
- return (EINVAL);
- }
- bzero(&rq, sizeof (rq));
- bcopy(&ctx->ct_ssn, &rq.ioc_ssn, sizeof (struct smbioc_ossn));
- bcopy(&ctx->ct_sh, &rq.ioc_sh, sizeof (struct smbioc_oshare));
- if (ioctl(ctx->ct_fd, SMBIOC_TDIS, &rq) == -1) {
- error = errno;
- smb_error(dgettext(TEXT_DOMAIN,
- "tree disconnect failed"), error);
+ "can't get flags2 for a session"), errno);
+ return (-1);
}
- return (error);
+ return (flags2);
}
-
+/*
+ * Get the transport level session key.
+ * Must already have an active SMB session.
+ */
int
-smb_ctx_lookup(struct smb_ctx *ctx, int level, int flags)
+smb_ctx_get_ssnkey(struct smb_ctx *ctx, uchar_t *key, size_t len)
{
- struct smbioc_lookup rq;
- int error = 0;
- char *failure = NULL;
-
- if ((ctx->ct_flags & SMBCF_RESOLVED) == 0) {
- smb_error(dgettext(TEXT_DOMAIN,
- "smb_ctx_lookup() data is not resolved"), 0);
- return (EINVAL);
- }
- if (ctx->ct_fd < 0) {
- smb_error(dgettext(TEXT_DOMAIN,
- "handle from smb_ctx_nego() gone?!"), 0);
+ if (len < SMBIOC_HASH_SZ)
return (EINVAL);
- }
- if (!(flags & SMBLK_CREATE))
- return (0);
- bzero(&rq, sizeof (rq));
- bcopy(&ctx->ct_ssn, &rq.ioc_ssn, sizeof (struct smbioc_ossn));
- bcopy(&ctx->ct_sh, &rq.ioc_sh, sizeof (struct smbioc_oshare));
- rq.ioc_flags = flags;
- rq.ioc_level = level;
-
- /*
- * Iff we have a security blob, we're using
- * extended security...
- */
- if (ctx->ct_secblob) {
- rq.ioc_ssn.ioc_opt |= SMBVOPT_EXT_SEC;
- if (!(ctx->ct_flags & SMBCF_SSNACTIVE)) {
- rq.ioc_ssn.ioc_intok = ctx->ct_secblob;
- rq.ioc_ssn.ioc_intoklen = ctx->ct_secbloblen;
- error = smb_ctx_ioctl(ctx, SMBIOC_SSNSETUP, &rq);
- }
- rq.ioc_ssn.ioc_intok = NULL;
- if (error) {
- failure = dgettext(TEXT_DOMAIN,
- "session setup failed");
- } else {
- ctx->ct_flags |= SMBCF_SSNACTIVE;
- if ((error = smb_ctx_ioctl(ctx, SMBIOC_TCON, &rq)))
- failure = dgettext(TEXT_DOMAIN,
- "tree connect failed");
- }
- if (rq.ioc_ssn.ioc_intok)
- free(rq.ioc_ssn.ioc_intok);
- if (rq.ioc_ssn.ioc_outtok)
- free(rq.ioc_ssn.ioc_outtok);
- if (!failure)
- return (0);
- smb_error(dgettext(TEXT_DOMAIN,
- "%s (extended security lookup2)"), error, failure);
- /* unwise to failback to NTLM now */
- return (error);
- }
- /*
- * Otherwise we're doing plain old NTLM
- */
- if ((ctx->ct_flags & SMBCF_SSNACTIVE) == 0) {
- /*
- * This is the magic that tells the driver to
- * copy the password from the keychain, and
- * whether to use the system name or the
- * account domain to lookup the keychain.
- */
- if (ctx->ct_flags & SMBCF_KCFOUND)
- rq.ioc_ssn.ioc_opt |= SMBVOPT_USE_KEYCHAIN;
- if (ctx->ct_flags & SMBCF_KCDOMAIN)
- rq.ioc_ssn.ioc_opt |= SMBVOPT_KC_DOMAIN;
- if (ioctl(ctx->ct_fd, SMBIOC_SSNSETUP, &rq) < 0) {
- error = errno;
- failure = dgettext(TEXT_DOMAIN, "session setup");
- goto out;
- }
- ctx->ct_flags |= SMBCF_SSNACTIVE;
- }
- if (ioctl(ctx->ct_fd, SMBIOC_TCON, &rq) == -1) {
- error = errno;
- failure = dgettext(TEXT_DOMAIN, "tree connect");
- }
+ if (ioctl(ctx->ct_dev_fd, SMBIOC_GETSSNKEY, key) == -1)
+ return (errno);
-out:
- if (failure) {
- error = errno;
- smb_error(dgettext(TEXT_DOMAIN,
- "%s phase failed"), error, failure);
- }
- return (error);
+ return (0);
}
+
/*
- * Return the hflags2 word for an smb_ctx.
+ * RC file parsing stuff
*/
-int
-smb_ctx_flags2(struct smb_ctx *ctx)
-{
- uint16_t flags2;
- if (ioctl(ctx->ct_fd, SMBIOC_FLAGS2, &flags2) == -1) {
- smb_error(dgettext(TEXT_DOMAIN,
- "can't get flags2 for a session"), errno);
- return (-1);
- }
- return (flags2);
-}
+struct nv {
+ char *name;
+ int value;
+} minauth_table[] = {
+ /* Allowed auth. types */
+ { "kerberos", SMB_AT_KRB5 },
+ { "ntlmv2", SMB_AT_KRB5|SMB_AT_NTLM2 },
+ { "ntlm", SMB_AT_KRB5|SMB_AT_NTLM2|SMB_AT_NTLM1 },
+ { "lm", SMB_AT_KRB5|SMB_AT_NTLM2|SMB_AT_NTLM1|SMB_AT_LM1 },
+ { "none", SMB_AT_KRB5|SMB_AT_NTLM2|SMB_AT_NTLM1|SMB_AT_LM1|
+ SMB_AT_ANON },
+ { NULL }
+};
+
/*
* level values:
@@ -1954,7 +1319,7 @@ smb_ctx_readrcsection(struct smb_ctx *ctx, const char *sname, int level)
char *p;
int error;
-#ifdef NOT_DEFINED
+#ifdef KICONV_SUPPORT
if (level > 0) {
rc_getstringptr(smb_rc, sname, "charsets", &p);
if (p) {
@@ -1970,56 +1335,19 @@ smb_ctx_readrcsection(struct smb_ctx *ctx, const char *sname, int level)
if (level <= 1) {
/* Section is: [default] or [server] */
- rc_getint(smb_rc, sname, "timeout",
- &ctx->ct_ssn.ioc_timeout);
-
-#ifdef NOT_DEFINED
- rc_getint(smb_rc, sname, "retry_count",
- &ctx->ct_ssn.ioc_retrycount);
- rc_getstringptr(smb_rc, sname, "use_negprot_domain", &p);
- if (p && strcmp(p, "NO") == 0)
- ctx->ct_flags |= SMBCF_NONEGDOM;
-#endif
-
rc_getstringptr(smb_rc, sname, "minauth", &p);
if (p) {
/*
* "minauth" was set in this section; override
* the current minimum authentication setting.
*/
- ctx->ct_ssn.ioc_opt &= ~SMBVOPT_MINAUTH;
- if (strcmp(p, "kerberos") == 0) {
- /*
- * Don't fall back to NTLMv2, NTLMv1, or
- * a clear text password.
- */
- ctx->ct_ssn.ioc_opt |= SMBVOPT_MINAUTH_KERBEROS;
- } else if (strcmp(p, "ntlmv2") == 0) {
- /*
- * Don't fall back to NTLMv1 or a clear
- * text password.
- */
- ctx->ct_ssn.ioc_opt |= SMBVOPT_MINAUTH_NTLMV2;
- } else if (strcmp(p, "ntlm") == 0) {
- /*
- * Don't send the LM response over the wire.
- */
- ctx->ct_ssn.ioc_opt |= SMBVOPT_MINAUTH_NTLM;
- } else if (strcmp(p, "lm") == 0) {
- /*
- * Fail if the server doesn't do encrypted
- * passwords.
- */
- ctx->ct_ssn.ioc_opt |= SMBVOPT_MINAUTH_LM;
- } else if (strcmp(p, "none") == 0) {
- /*
- * Anything goes.
- * (The following statement should be
- * optimized away.)
- */
- /* LINTED */
- ctx->ct_ssn.ioc_opt |= SMBVOPT_MINAUTH_NONE;
- } else {
+ struct nv *nvp;
+ for (nvp = minauth_table; nvp->name; nvp++)
+ if (strcmp(p, nvp->name) == 0)
+ break;
+ if (nvp->name)
+ ctx->ct_minauth = nvp->value;
+ else {
/*
* Unknown minimum authentication level.
*/
@@ -2036,15 +1364,15 @@ smb_ctx_readrcsection(struct smb_ctx *ctx, const char *sname, int level)
* "signing" was set in this section; override
* the current signing settings.
*/
- ctx->ct_ssn.ioc_opt &= ~SMBVOPT_SIGNING_MASK;
+ ctx->ct_vopt &= ~SMBVOPT_SIGNING_MASK;
if (strcmp(p, "disabled") == 0) {
/* leave flags zero (expr for lint) */
- (void) ctx->ct_ssn.ioc_opt;
+ (void) ctx->ct_vopt;
} else if (strcmp(p, "enabled") == 0) {
- ctx->ct_ssn.ioc_opt |=
+ ctx->ct_vopt |=
SMBVOPT_SIGNING_ENABLED;
} else if (strcmp(p, "required") == 0) {
- ctx->ct_ssn.ioc_opt |=
+ ctx->ct_vopt |=
SMBVOPT_SIGNING_ENABLED |
SMBVOPT_SIGNING_REQUIRED;
} else {
@@ -2068,7 +1396,7 @@ smb_ctx_readrcsection(struct smb_ctx *ctx, const char *sname, int level)
rc_getstringptr(smb_rc, sname, "workgroup", &p);
if (p) {
nls_str_upper(p, p);
- error = smb_ctx_setworkgroup(ctx, p, 0);
+ error = smb_ctx_setdomain(ctx, p, 0);
if (error)
smb_error(dgettext(TEXT_DOMAIN,
"workgroup specification in the "
@@ -2077,7 +1405,7 @@ smb_ctx_readrcsection(struct smb_ctx *ctx, const char *sname, int level)
rc_getstringptr(smb_rc, sname, "domain", &p);
if (p) {
nls_str_upper(p, p);
- error = smb_ctx_setworkgroup(ctx, p, 0);
+ error = smb_ctx_setdomain(ctx, p, 0);
if (error)
smb_error(dgettext(TEXT_DOMAIN,
"domain specification in the "
@@ -2132,11 +1460,25 @@ smb_ctx_readrcsection(struct smb_ctx *ctx, const char *sname, int level)
int
smb_ctx_readrc(struct smb_ctx *ctx)
{
- char sname[SMB_MAXSRVNAMELEN + SMB_MAXUSERNAMELEN +
- SMB_MAXSHARENAMELEN + 4];
+ char *home;
+ char *sname = NULL;
+ int sname_max;
+ int err = 0;
+
+ if ((home = getenv("HOME")) == NULL)
+ home = ctx->ct_home;
+ if ((err = smb_open_rcfile(home)) != 0) {
+ DPRINT("smb_open_rcfile, err=%d", err);
+ /* ignore any error here */
+ return (0);
+ }
- if (smb_open_rcfile(ctx) != 0)
+ sname_max = 3 * SMBIOC_MAX_NAME + 4;
+ sname = malloc(sname_max);
+ if (sname == NULL) {
+ err = ENOMEM;
goto done;
+ }
/*
* default parameters (level=0)
@@ -2148,47 +1490,51 @@ smb_ctx_readrc(struct smb_ctx *ctx)
* If we don't have a server name, we can't read any of the
* [server...] sections.
*/
- if (ctx->ct_ssn.ioc_srvname[0] == 0)
+ if (ctx->ct_fullserver == NULL)
goto done;
-
/*
* SERVER parameters.
*/
- smb_ctx_readrcsection(ctx, ctx->ct_ssn.ioc_srvname, 1);
+ smb_ctx_readrcsection(ctx, ctx->ct_fullserver, 1);
/*
* If we don't have a user name, we can't read any of the
* [server:user...] sections.
*/
- if (ctx->ct_ssn.ioc_user[0] == 0)
+ if (ctx->ct_user[0] == 0)
goto done;
-
/*
* SERVER:USER parameters
*/
- snprintf(sname, sizeof (sname), "%s:%s",
- ctx->ct_ssn.ioc_srvname,
- ctx->ct_ssn.ioc_user);
+ snprintf(sname, sname_max, "%s:%s",
+ ctx->ct_fullserver,
+ ctx->ct_user);
smb_ctx_readrcsection(ctx, sname, 2);
+
/*
* If we don't have a share name, we can't read any of the
* [server:user:share] sections.
*/
- if (ctx->ct_sh.ioc_share[0] != 0) {
- /*
- * SERVER:USER:SHARE parameters
- */
- snprintf(sname, sizeof (sname), "%s:%s:%s",
- ctx->ct_ssn.ioc_srvname,
- ctx->ct_ssn.ioc_user,
- ctx->ct_sh.ioc_share);
- smb_ctx_readrcsection(ctx, sname, 3);
- }
+ if (ctx->ct_origshare == NULL)
+ goto done;
+ /*
+ * SERVER:USER:SHARE parameters
+ */
+ snprintf(sname, sname_max, "%s:%s:%s",
+ ctx->ct_fullserver,
+ ctx->ct_user,
+ ctx->ct_origshare);
+ smb_ctx_readrcsection(ctx, sname, 3);
done:
+ if (sname)
+ free(sname);
+ smb_close_rcfile();
if (smb_debug)
dump_ctx("after smb_ctx_readrc", ctx);
+ if (err)
+ DPRINT("err=%d\n", err);
- return (0);
+ return (err);
}
diff --git a/usr/src/lib/libsmbfs/smb/derparse.c b/usr/src/lib/libsmbfs/smb/derparse.c
index cc7d61c6bd..0180e064ed 100644
--- a/usr/src/lib/libsmbfs/smb/derparse.c
+++ b/usr/src/lib/libsmbfs/smb/derparse.c
@@ -1,4 +1,3 @@
-/*
// Copyright (C) 2002 Microsoft Corporation
// All rights reserved.
//
@@ -23,10 +22,6 @@
//
/////////////////////////////////////////////////////////////
-*/
-
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <stdlib.h>
#include <stdio.h>
#include <memory.h>
@@ -34,12 +29,11 @@
#include "spnego.h"
#include "derparse.h"
-/*
//
// The GSS Mechanism OID enumeration values (SPNEGO_MECH_OID) control which offset in
// the array below, that a mechanism can be found.
//
-*/
+
#pragma error_messages (off,E_INITIALIZATION_TYPE_MISMATCH)
MECH_OID g_stcMechOIDList [] =
{
@@ -55,7 +49,6 @@ MECH_OID g_stcMechOIDList [] =
};
#pragma error_messages (default,E_INITIALIZATION_TYPE_MISMATCH)
-/*
/////////////////////////////////////////////////////////////////////////////
//
// Function:
@@ -78,7 +71,6 @@ MECH_OID g_stcMechOIDList [] =
// process lengths that take more than 4 bytes.
//
////////////////////////////////////////////////////////////////////////////
-*/
int ASNDerGetLength( unsigned char* pbLengthData, long nBoundaryLength, long* pnLength,
long* pnNumLengthBytes )
@@ -180,7 +172,6 @@ int ASNDerGetLength( unsigned char* pbLengthData, long nBoundaryLength, long* pn
}
-/*
/////////////////////////////////////////////////////////////////////////////
//
// Function:
@@ -206,7 +197,6 @@ int ASNDerGetLength( unsigned char* pbLengthData, long nBoundaryLength, long* pn
// length must also not exceed the specified boundary length .
//
////////////////////////////////////////////////////////////////////////////
-*/
int ASNDerCheckToken( unsigned char* pbTokenData, unsigned char nToken,
long nLengthWithToken, long nBoundaryLength,
@@ -271,7 +261,6 @@ int ASNDerCheckToken( unsigned char* pbTokenData, unsigned char nToken,
return nReturn;
}
-/*
/////////////////////////////////////////////////////////////////////////////
//
// Function:
@@ -292,7 +281,6 @@ int ASNDerCheckToken( unsigned char* pbTokenData, unsigned char nToken,
// Checks the data pointed to by pbTokenData for the specified OID.
//
////////////////////////////////////////////////////////////////////////////
-*/
int ASNDerCheckOID( unsigned char* pbTokenData, SPNEGO_MECH_OID nMechOID, long nBoundaryLength,
long* pnTokenLength )
@@ -327,7 +315,6 @@ int ASNDerCheckOID( unsigned char* pbTokenData, SPNEGO_MECH_OID nMechOID, long n
return nReturn;
}
-/*
/////////////////////////////////////////////////////////////////////////////
//
// Function:
@@ -345,7 +332,6 @@ int ASNDerCheckOID( unsigned char* pbTokenData, SPNEGO_MECH_OID nMechOID, long n
// enough to describea length.
//
////////////////////////////////////////////////////////////////////////////
-*/
int ASNDerCalcNumLengthBytes( long nLength )
{
diff --git a/usr/src/lib/libsmbfs/smb/file.c b/usr/src/lib/libsmbfs/smb/file.c
index e7a02e77df..74630fdd91 100644
--- a/usr/src/lib/libsmbfs/smb/file.c
+++ b/usr/src/lib/libsmbfs/smb/file.c
@@ -33,7 +33,7 @@
*/
/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -56,35 +56,32 @@
#include <sys/types.h>
#include <sys/file.h>
+#include <netsmb/smb.h>
#include <netsmb/smb_lib.h>
-#include <cflib.h>
-#include "charsets.h"
#include "private.h"
int
-smb_fh_close(struct smb_ctx *ctx, smbfh fh)
+smb_fh_close(struct smb_ctx *ctx, int fh)
{
struct smb_rq *rqp;
struct mbdata *mbp;
- int serr;
+ int error;
- serr = smb_rq_init(ctx, SMB_COM_CLOSE, 0, &rqp);
- if (serr != 0)
- return (serr);
+ error = smb_rq_init(ctx, SMB_COM_CLOSE, &rqp);
+ if (error != 0)
+ return (error);
mbp = smb_rq_getrequest(rqp);
- mb_put_uint16le(mbp, fh);
+ smb_rq_wstart(rqp);
+ mb_put_uint16le(mbp, (uint16_t)fh);
mb_put_uint32le(mbp, 0); /* time stamp */
smb_rq_wend(rqp);
- serr = smb_rq_simple(rqp);
- if (serr != 0) {
- smb_rq_done(rqp);
- return (serr);
- }
- mbp = smb_rq_getreply(rqp);
+ mb_put_uint16le(mbp, 0); /* byte count */
+
+ error = smb_rq_simple(rqp);
smb_rq_done(rqp);
- return (serr);
+ return (error);
}
int
@@ -93,45 +90,32 @@ smb_fh_ntcreate(
int flags, int req_acc, int efattr,
int share_acc, int open_disp,
int create_opts, int impersonation,
- smbfh *fhp, uint32_t *action_taken)
+ int *fhp, uint32_t *action_taken)
{
struct smb_rq *rqp;
struct mbdata *mbp;
+ char *pathsizep;
+ int pathstart, pathsize;
+ int error, flags2, uc;
+ uint16_t fh;
uint8_t wc;
- size_t pathlen, pathsize;
- int error, flags2;
- uint16_t *upath = NULL;
flags2 = smb_ctx_flags2(ctx);
if (flags2 == -1)
return (EIO);
+ uc = flags2 & SMB_FLAGS2_UNICODE;
- error = smb_rq_init(ctx, SMB_COM_NT_CREATE_ANDX, 42, &rqp);
+ error = smb_rq_init(ctx, SMB_COM_NT_CREATE_ANDX, &rqp);
if (error != 0)
return (error);
- if (flags2 & SMB_FLAGS2_UNICODE) {
- upath = convert_utf8_to_leunicode(path);
- if (upath == NULL) {
- smb_error(dgettext(TEXT_DOMAIN,
- "%s: failed converting to UCS-2"), 0, path);
- error = EINVAL;
- goto out;
- }
- pathlen = unicode_strlen(upath);
- pathsize = (pathlen + 1) * 2;
- } else {
- pathlen = strlen(path);
- pathsize = pathlen + 1;
- }
-
mbp = smb_rq_getrequest(rqp);
- mb_put_uint8(mbp, 0xff); /* secondary command */
- mb_put_uint8(mbp, 0); /* MBZ */
+ smb_rq_wstart(rqp);
+ mb_put_uint16le(mbp, 0xff); /* secondary command */
mb_put_uint16le(mbp, 0); /* offset to next command (none) */
- mb_put_uint8(mbp, 0); /* MBZ */
- mb_put_uint16le(mbp, pathsize); /* path size (bytes) */
- mb_put_uint32le(mbp, 0); /* create flags (oplock) */
+ mb_put_uint8(mbp, 0); /* MBZ (pad?) */
+ mb_fit(mbp, 2, &pathsizep); /* path size - fill in below */
+ mb_put_uint32le(mbp, flags); /* create flags (oplock) */
mb_put_uint32le(mbp, 0); /* FID - basis for path if not root */
mb_put_uint32le(mbp, req_acc);
mb_put_uint64le(mbp, 0); /* initial alloc. size */
@@ -139,16 +123,28 @@ smb_fh_ntcreate(
mb_put_uint32le(mbp, share_acc); /* share access mode */
mb_put_uint32le(mbp, open_disp); /* open disposition */
mb_put_uint32le(mbp, create_opts); /* create_options */
- mb_put_uint32le(mbp, NTCREATEX_IMPERSONATION_IMPERSONATION); /* (?) */
+ mb_put_uint32le(mbp, impersonation);
mb_put_uint8(mbp, 0); /* security flags (?) */
smb_rq_wend(rqp);
+ smb_rq_bstart(rqp);
+ if (uc) {
+ /*
+ * We're about to put a unicode string. We know
+ * we're misaligned at this point, and need to
+ * save the mb_count at the start of the string,
+ * not at the alignment padding placed before it.
+ * So add the algnment padding by hand here.
+ */
+ mb_put_uint8(mbp, 0);
+ }
+ pathstart = mbp->mb_count;
+ mb_put_dstring(mbp, path, uc);
+ smb_rq_bend(rqp);
- /* XXX: Need a "put string" function. */
- if (flags2 & SMB_FLAGS2_UNICODE) {
- mb_put_uint8(mbp, 0); /* pad byte - align(2) for Unicode */
- mb_put_mem(mbp, (char *)upath, pathsize);
- } else
- mb_put_mem(mbp, path, pathsize);
+ /* Now go back and fill in pathsizep */
+ pathsize = mbp->mb_count - pathstart;
+ pathsizep[0] = pathsize & 0xFF;
+ pathsizep[1] = (pathsize >> 8);
error = smb_rq_simple(rqp);
if (error)
@@ -159,8 +155,8 @@ smb_fh_ntcreate(
* spec says 26 for word count, but 34 words are defined
* and observed from win2000
*/
- wc = rqp->rq_wcount;
- if (wc < 26) {
+ error = mb_get_uint8(mbp, &wc);
+ if (error || wc < 26) {
smb_error(dgettext(TEXT_DOMAIN,
"%s: open failed, bad word count"), 0, path);
error = EBADRPC;
@@ -170,7 +166,7 @@ smb_fh_ntcreate(
mb_get_uint8(mbp, NULL); /* mbz */
mb_get_uint16le(mbp, NULL); /* andxoffset */
mb_get_uint8(mbp, NULL); /* oplock lvl granted */
- mb_get_uint16le(mbp, fhp); /* FID */
+ mb_get_uint16le(mbp, &fh); /* FID */
mb_get_uint32le(mbp, action_taken);
#if 0 /* skip decoding the rest */
mb_get_uint64le(mbp, NULL); /* creation time */
@@ -183,14 +179,16 @@ smb_fh_ntcreate(
mb_get_uint16le(mbp, NULL); /* file type */
mb_get_uint16le(mbp, NULL); /* device state */
mb_get_uint8(mbp, NULL); /* directory (boolean) */
-#endif /* skip decoding */
+#endif
+
+ /* success! */
+ *fhp = fh;
+ error = 0;
out:
- if (upath)
- free(upath);
smb_rq_done(rqp);
- return (0);
+ return (error);
}
/*
@@ -198,7 +196,7 @@ out:
* Converts Unix-style open call to NTCreate.
*/
int
-smb_fh_open(struct smb_ctx *ctx, const char *path, int oflag, smbfh *fhp)
+smb_fh_open(struct smb_ctx *ctx, const char *path, int oflag, int *fhp)
{
int error, mode, open_disp, req_acc, share_acc;
char *p, *ntpath = NULL;
@@ -273,7 +271,7 @@ smb_fh_open(struct smb_ctx *ctx, const char *path, int oflag, smbfh *fhp)
}
int
-smb_fh_read(struct smb_ctx *ctx, smbfh fh, off_t offset, size_t count,
+smb_fh_read(struct smb_ctx *ctx, int fh, off_t offset, size_t count,
char *dst)
{
struct smbioc_rw rwrq;
@@ -283,14 +281,14 @@ smb_fh_read(struct smb_ctx *ctx, smbfh fh, off_t offset, size_t count,
rwrq.ioc_base = dst;
rwrq.ioc_cnt = count;
rwrq.ioc_offset = offset;
- if (ioctl(ctx->ct_fd, SMBIOC_READ, &rwrq) == -1) {
+ if (ioctl(ctx->ct_dev_fd, SMBIOC_READ, &rwrq) == -1) {
return (-1);
}
return (rwrq.ioc_cnt);
}
int
-smb_fh_write(struct smb_ctx *ctx, smbfh fh, off_t offset, size_t count,
+smb_fh_write(struct smb_ctx *ctx, int fh, off_t offset, size_t count,
const char *src)
{
struct smbioc_rw rwrq;
@@ -300,7 +298,7 @@ smb_fh_write(struct smb_ctx *ctx, smbfh fh, off_t offset, size_t count,
rwrq.ioc_base = (char *)src;
rwrq.ioc_cnt = count;
rwrq.ioc_offset = offset;
- if (ioctl(ctx->ct_fd, SMBIOC_WRITE, &rwrq) == -1) {
+ if (ioctl(ctx->ct_dev_fd, SMBIOC_WRITE, &rwrq) == -1) {
return (-1);
}
return (rwrq.ioc_cnt);
@@ -315,7 +313,7 @@ smb_fh_write(struct smb_ctx *ctx, smbfh fh, off_t offset, size_t count,
* and on output *rdlen is the received length.
*/
int
-smb_fh_xactnp(struct smb_ctx *ctx, smbfh fh,
+smb_fh_xactnp(struct smb_ctx *ctx, int fh,
int tdlen, const char *tdata, /* transmit */
int *rdlen, char *rdata, /* receive */
int *more)
diff --git a/usr/src/lib/libsmbfs/smb/findvc.c b/usr/src/lib/libsmbfs/smb/findvc.c
new file mode 100644
index 0000000000..cfe2cad9e7
--- /dev/null
+++ b/usr/src/lib/libsmbfs/smb/findvc.c
@@ -0,0 +1,139 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Find existing an VC given a list of addresses.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <libintl.h>
+#include <xti.h>
+#include <assert.h>
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/byteorder.h>
+#include <sys/socket.h>
+#include <sys/fcntl.h>
+
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+
+#include <netsmb/smb.h>
+#include <netsmb/smb_lib.h>
+#include <netsmb/netbios.h>
+#include <netsmb/nb_lib.h>
+#include <netsmb/smb_dev.h>
+
+#include "charsets.h"
+#include "private.h"
+
+/*
+ * Ask the driver if it has a VC with this IP address.
+ */
+static int
+findvc(struct smb_ctx *ctx, struct addrinfo *ai)
+{
+ smbioc_ossn_t *ssn = &ctx->ct_ssn;
+
+ /*
+ * Copy the passed address into ssn_srvaddr,
+ * but first sanity-check lengths. Also,
+ * zero it first to avoid trailing junk.
+ */
+ if (ai->ai_addrlen > sizeof (ssn->ssn_srvaddr))
+ return (EINVAL);
+ bzero(&ssn->ssn_srvaddr, sizeof (ssn->ssn_srvaddr));
+ bcopy(ai->ai_addr, &ssn->ssn_srvaddr, ai->ai_addrlen);
+
+ if (ioctl(ctx->ct_dev_fd, SMBIOC_SSN_FIND, ssn) == -1)
+ return (errno);
+
+ return (0);
+}
+
+/*
+ * Find (and reuse) an existing VC.
+ * See also: newvc.c
+ */
+int
+smb_ctx_findvc(struct smb_ctx *ctx)
+{
+ struct addrinfo *ai;
+ int err;
+
+ /* Should already have the address list. */
+ if ((ctx->ct_flags & SMBCF_RESOLVED) == 0)
+ return (EINVAL);
+
+ for (ai = ctx->ct_addrinfo; ai; ai = ai->ai_next) {
+
+ switch (ai->ai_family) {
+
+ case AF_INET:
+ case AF_INET6:
+ case AF_NETBIOS:
+ err = findvc(ctx, ai);
+ break;
+
+ default:
+ DPRINT("skipped family %d", ai->ai_family);
+ err = EPROTONOSUPPORT;
+ break;
+ }
+
+ if (err == 0) {
+ /* re-use an existing VC */
+ ctx->ct_flags |= SMBCF_SSNACTIVE;
+ return (0);
+ }
+ }
+
+ return (ENOENT);
+}
+
+/*
+ * Forcibly disconnect the current session, even if
+ * there are others using it! This is used by the
+ * SMB server netlogon when it wants to setup a new
+ * logon session and does not want any re-use.
+ */
+int
+smb_ctx_kill(struct smb_ctx *ctx)
+{
+
+ if (ioctl(ctx->ct_dev_fd, SMBIOC_SSN_KILL, NULL) == -1)
+ return (errno);
+
+ return (0);
+}
diff --git a/usr/src/lib/libsmbfs/smb/getaddr.c b/usr/src/lib/libsmbfs/smb/getaddr.c
new file mode 100644
index 0000000000..2847d858cb
--- /dev/null
+++ b/usr/src/lib/libsmbfs/smb/getaddr.c
@@ -0,0 +1,215 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Functions to get list of addresses (TCP and/or NetBIOS)
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <libintl.h>
+#include <xti.h>
+#include <assert.h>
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/byteorder.h>
+#include <sys/socket.h>
+#include <sys/fcntl.h>
+
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+
+#include <netsmb/smb.h>
+#include <netsmb/smb_lib.h>
+#include <netsmb/netbios.h>
+#include <netsmb/nb_lib.h>
+#include <netsmb/smb_dev.h>
+
+#include "charsets.h"
+#include "private.h"
+
+void
+dump_addrinfo(struct addrinfo *ai)
+{
+ int i;
+
+ if (ai == NULL) {
+ printf("ai==NULL\n");
+ return;
+ }
+
+ for (i = 0; ai; i++, ai = ai->ai_next) {
+ printf("ai[%d]: af=%d, len=%d", i,
+ ai->ai_family, ai->ai_addrlen);
+ dump_sockaddr(ai->ai_addr);
+ if (ai->ai_canonname) {
+ printf("ai[%d]: cname=\"%s\"\n",
+ i, ai->ai_canonname);
+ }
+ }
+}
+
+void
+dump_sockaddr(struct sockaddr *sa)
+{
+ char paddrbuf[INET6_ADDRSTRLEN];
+ struct sockaddr_in *sin;
+ struct sockaddr_in6 *sin6;
+ int af = sa->sa_family;
+ const char *ip;
+
+ printf(" saf=%d,", af);
+ switch (af) {
+ case AF_NETBIOS: /* see nbns_rq.c */
+ case AF_INET:
+ sin = (void *)sa;
+ ip = inet_ntop(AF_INET, &sin->sin_addr,
+ paddrbuf, sizeof (paddrbuf));
+ break;
+ case AF_INET6:
+ sin6 = (void *)sa;
+ ip = inet_ntop(AF_INET6, &sin6->sin6_addr,
+ paddrbuf, sizeof (paddrbuf));
+ break;
+ default:
+ ip = "?";
+ break;
+ }
+ printf(" IP=%s\n", ip);
+}
+
+
+/*
+ * SMB client name resolution - normal, and/or NetBIOS.
+ * Returns an EAI_xxx error number like getaddrinfo(3)
+ */
+int
+smb_ctx_getaddr(struct smb_ctx *ctx)
+{
+ struct nb_ctx *nbc = ctx->ct_nb;
+ struct addrinfo hints, *res;
+ char *srvaddr_str;
+ int gaierr, gaierr2;
+
+ if (ctx->ct_fullserver == NULL || ctx->ct_fullserver[0] == '\0')
+ return (EAI_NONAME);
+
+ if (ctx->ct_addrinfo != NULL) {
+ freeaddrinfo(ctx->ct_addrinfo);
+ ctx->ct_addrinfo = NULL;
+ }
+
+ /*
+ * If the user specified an address, use it,
+ * and don't do NetBIOS lookup.
+ */
+ if (ctx->ct_srvaddr_s) {
+ srvaddr_str = ctx->ct_srvaddr_s;
+ nbc->nb_flags &= ~NBCF_NS_ENABLE;
+ } else
+ srvaddr_str = ctx->ct_fullserver;
+
+ /*
+ * Default the server name we'll use in the
+ * protocol (i.e. NTLM, tree connect).
+ * If we get a canonical name, we'll
+ * overwrite this below.
+ */
+ strlcpy(ctx->ct_srvname, ctx->ct_fullserver,
+ sizeof (ctx->ct_srvname));
+
+ /*
+ * Try to lookup the host address using the
+ * normal name-to-IP address mechanisms.
+ * If that fails, we MAY try NetBIOS.
+ */
+ memset(&hints, 0, sizeof (hints));
+ hints.ai_flags = AI_CANONNAME;
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ gaierr = getaddrinfo(srvaddr_str, NULL, &hints, &res);
+ if (gaierr == 0) {
+#if 1
+ /*
+ * XXX Temporarily work-around CR 6831339:
+ * getaddrinfo() sets ai_canonname incorrectly
+ */
+ char tmphost[256];
+ gaierr2 = getnameinfo(res->ai_addr, res->ai_addrlen,
+ tmphost, sizeof (tmphost),
+ NULL, 0, NI_NAMEREQD);
+ if (gaierr2 == 0) {
+ DPRINT("cname: %s", tmphost);
+ strlcpy(ctx->ct_srvname, tmphost,
+ sizeof (ctx->ct_srvname));
+ }
+#else
+ if (res->ai_canonname)
+ strlcpy(ctx->ct_srvname, res->ai_canonname,
+ sizeof (ctx->ct_srvname));
+#endif
+ ctx->ct_addrinfo = res;
+ return (0);
+ }
+
+ /*
+ * If regular IP name lookup failed, try NetBIOS,
+ * but only if given a valid NetBIOS name and if
+ * NetBIOS name lookup is enabled.
+ *
+ * Note: we only have ssn_srvname if the full name
+ * was also a valid NetBIOS name.
+ */
+ if (nbc->nb_flags & NBCF_NS_ENABLE) {
+ gaierr2 = nbns_getaddrinfo(ctx->ct_fullserver, nbc, &res);
+ if (gaierr2 == 0) {
+ if (res->ai_canonname)
+ strlcpy(ctx->ct_srvname,
+ res->ai_canonname,
+ sizeof (ctx->ct_srvname));
+ ctx->ct_addrinfo = res;
+ return (0);
+ }
+ }
+
+ /*
+ * Return the original error from getaddrinfo
+ */
+ if (smb_verbose) {
+ smb_error(dgettext(TEXT_DOMAIN,
+ "getaddrinfo: %s: %s"), 0,
+ ctx->ct_fullserver,
+ gai_strerror(gaierr));
+ }
+ return (gaierr);
+}
diff --git a/usr/src/lib/libsmbfs/smb/iod_cl.c b/usr/src/lib/libsmbfs/smb/iod_cl.c
new file mode 100644
index 0000000000..b28ffdb39a
--- /dev/null
+++ b/usr/src/lib/libsmbfs/smb/iod_cl.c
@@ -0,0 +1,195 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Client-side interface to the IO Daemon (IOD)
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <libintl.h>
+#include <door.h>
+
+#include <sys/byteorder.h>
+#include <sys/types.h>
+#include <sys/fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+
+#include <netsmb/smb_lib.h>
+#include <netsmb/netbios.h>
+#include <netsmb/nb_lib.h>
+#include <netsmb/smb_dev.h>
+
+#include "charsets.h"
+#include "private.h"
+
+static const char smbiod_path[] = "/usr/lib/smbfs/smbiod";
+
+/*
+ * This is constant for the life of a process,
+ * and initialized at startup, so no locks.
+ */
+static char door_path[40];
+
+char *
+smb_iod_door_path(void)
+{
+ static const char fmtR[] = "/var/run/smbiod-%d";
+ static const char fmtU[] = "/tmp/.smbiod-%d";
+ const char *fmt;
+ uid_t uid;
+
+ if (door_path[0] == '\0') {
+ uid = getuid();
+ fmt = (uid == 0) ? fmtR : fmtU;
+ snprintf(door_path, sizeof (door_path), fmt, uid);
+ }
+
+ return (door_path);
+}
+
+/*
+ * Open the door (client side) and
+ * find out if the service is there.
+ */
+int
+smb_iod_open_door(int *fdp)
+{
+ door_arg_t da;
+ char *path;
+ int fd, rc;
+ int err = 0;
+
+ path = smb_iod_door_path();
+ fd = open(path, O_RDONLY, 0);
+ if (fd < 0)
+ return (errno);
+
+ /*
+ * Make sure the IOD is running.
+ * Pass NULL args.
+ */
+ memset(&da, 0, sizeof (da));
+ da.rbuf = (void *) &err;
+ da.rsize = sizeof (err);
+ rc = door_call(fd, &da);
+ if (rc < 0) {
+ err = errno;
+ close(fd);
+ return (err);
+ }
+ if (err != 0) {
+ close(fd);
+ return (err);
+ }
+
+ /* This handle controls per-process resources. */
+ (void) fcntl(fd, F_SETFD, FD_CLOEXEC);
+
+ *fdp = fd;
+ return (0);
+}
+
+/*
+ * Start the IOD and wait until we can
+ * open its client-side door.
+ */
+static int
+start_iod(int *fdp)
+{
+ int err, pid, t;
+
+ pid = vfork();
+ if (pid < 0)
+ return (errno);
+
+ /*
+ * child: start smbiod
+ */
+ if (pid == 0) {
+ char *argv[2];
+ argv[0] = "smbiod";
+ argv[1] = NULL;
+ execv(smbiod_path, argv);
+ return (errno);
+ }
+
+ /*
+ * parent: wait for smbiod to start
+ */
+ for (t = 0; t < 10; t++) {
+ sleep(1);
+ err = smb_iod_open_door(fdp);
+ if (err == 0)
+ break;
+ }
+
+ return (err);
+}
+
+
+/*
+ * Start smbiod if necessary, and then
+ * ask it to connect using the info in ctx.
+ */
+int
+smb_iod_cl_newvc(smb_ctx_t *ctx)
+{
+ door_arg_t da;
+ int fd, err = 0;
+
+ err = smb_iod_open_door(&fd);
+ if (err != 0) {
+ err = start_iod(&fd);
+ if (err)
+ return (err);
+ }
+
+ da.data_ptr = (void *) &ctx->ct_iod_ssn;
+ da.data_size = sizeof (ctx->ct_iod_ssn);
+ da.desc_ptr = NULL;
+ da.desc_num = 0;
+ da.rbuf = (void *) &err;
+ da.rsize = sizeof (err);
+ if (door_call(fd, &da) < 0) {
+ err = errno;
+ DPRINT("door_call, err=%d", err);
+ }
+ close(fd);
+
+ return (err);
+}
diff --git a/usr/src/lib/libsmbfs/smb/iod_wk.c b/usr/src/lib/libsmbfs/smb/iod_wk.c
new file mode 100644
index 0000000000..89f3eb3fa0
--- /dev/null
+++ b/usr/src/lib/libsmbfs/smb/iod_wk.c
@@ -0,0 +1,176 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Functions called by the IO deamon (IOD).
+ * Here in the library to simplify testing.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <libintl.h>
+
+#include <sys/byteorder.h>
+#include <sys/types.h>
+#include <sys/fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+
+#include <netsmb/smb.h>
+#include <netsmb/smb_lib.h>
+#include <netsmb/netbios.h>
+#include <netsmb/nb_lib.h>
+#include <netsmb/smb_dev.h>
+
+#include "charsets.h"
+#include "private.h"
+
+/*
+ * Be the reader thread for this VC.
+ */
+int
+smb_iod_work(smb_ctx_t *ctx)
+{
+ smbioc_ssn_work_t *work = &ctx->ct_work;
+ int vcst, err = 0;
+
+ DPRINT("server: %s", ctx->ct_srvname);
+
+ /* Calle should have opened these */
+ if (ctx->ct_tran_fd == -1 || ctx->ct_dev_fd == -1) {
+ err = EINVAL;
+ goto out;
+ }
+
+ /*
+ * This is the reader / reconnect loop.
+ *
+ * We could start with state "idle", but
+ * we know someone wants a connection to
+ * this server, so start in "vcactive".
+ *
+ * XXX: Add some syslog calls in here?
+ */
+ vcst = SMBIOD_ST_VCACTIVE;
+
+ for (;;) {
+
+ switch (vcst) {
+ case SMBIOD_ST_IDLE:
+ /*
+ * Wait for driver requests to arrive
+ * for this VC, then return here.
+ * Next state is normally RECONNECT.
+ */
+ DPRINT("state: idle");
+ if (ioctl(ctx->ct_dev_fd,
+ SMBIOC_IOD_IDLE, &vcst) == -1) {
+ err = errno;
+ DPRINT("ioc_idle: err %d", err);
+ goto out;
+ }
+ continue;
+
+ case SMBIOD_ST_RECONNECT:
+ DPRINT("state: reconnect");
+ err = smb_iod_connect(ctx);
+ if (err == 0) {
+ vcst = SMBIOD_ST_VCACTIVE;
+ continue;
+ }
+ DPRINT("_iod_connect: err %d", err);
+ /*
+ * If the error was EAUTH, retry is
+ * not likely to succeed either, so
+ * just exit this thread. The user
+ * will need to run smbutil to get
+ * a new thread with new auth info.
+ */
+ if (err == EAUTH)
+ goto out;
+ vcst = SMBIOD_ST_RCFAILED;
+ continue;
+
+ case SMBIOD_ST_RCFAILED:
+ DPRINT("state: rcfailed");
+ /*
+ * Reconnect failed. Kill off any
+ * requests waiting in the driver,
+ * then get ready to try again.
+ * Next state is normally IDLE.
+ */
+ if (ioctl(ctx->ct_dev_fd,
+ SMBIOC_IOD_RCFAIL, &vcst) == -1) {
+ err = errno;
+ DPRINT("ioc_rcfail: err %d", err);
+ goto out;
+ }
+ continue;
+
+ case SMBIOD_ST_VCACTIVE:
+ DPRINT("state: active");
+ if (ioctl(ctx->ct_dev_fd,
+ SMBIOC_IOD_WORK, work) == -1) {
+ err = errno;
+ DPRINT("ioc_work: err %d", err);
+ goto out;
+ }
+ vcst = work->wk_out_state;
+ continue;
+
+ case SMBIOD_ST_DEAD:
+ DPRINT("state: dead");
+ err = 0;
+ goto out;
+
+ default:
+ DPRINT("state: BAD(%d)", vcst);
+ err = EFAULT;
+ goto out;
+ }
+ }
+
+out:
+ if (ctx->ct_tran_fd != -1) {
+ close(ctx->ct_tran_fd);
+ ctx->ct_tran_fd = -1;
+ }
+ if (ctx->ct_dev_fd != -1) {
+ close(ctx->ct_dev_fd);
+ ctx->ct_dev_fd = -1;
+ }
+
+ return (err);
+}
diff --git a/usr/src/lib/libsmbfs/smb/keychain.c b/usr/src/lib/libsmbfs/smb/keychain.c
index 72a979022f..da19fd4d0b 100644
--- a/usr/src/lib/libsmbfs/smb/keychain.c
+++ b/usr/src/lib/libsmbfs/smb/keychain.c
@@ -20,12 +20,10 @@
*/
/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
/*
* External interface to the libsmbfs/netsmb keychain
* storage mechanism. This interface is consumed by
@@ -37,15 +35,19 @@
#include <errno.h>
#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <libintl.h>
+#include <cflib.h>
#include <netsmb/smb_dev.h>
#include <netsmb/smb_lib.h>
#include <netsmb/smb_keychain.h>
-#include <cflib.h>
+#include "charsets.h"
+#include "private.h"
+#include "ntlm.h"
/* common func. for add/del/chk */
static int
@@ -54,50 +56,68 @@ smbfs_keychain_cmn(
uid_t uid,
const char *dom,
const char *usr,
- const char *pass)
+ uchar_t *lmhash,
+ uchar_t *nthash)
{
smbioc_pk_t pk;
- int err, fd;
+ int err, fd, sz;
memset(&pk, 0, sizeof (pk));
-
pk.pk_uid = uid;
+ err = 0;
+ fd = -1;
switch (cmd) {
case SMBIOC_PK_ADD:
- if (pass == NULL)
- return (SMB_KEYCHAIN_BADPASSWD);
- if (strlcpy(pk.pk_pass, pass, sizeof (pk.pk_pass)) >=
- sizeof (pk.pk_pass))
- return (SMB_KEYCHAIN_BADPASSWD);
+ /*
+ * Add password hashes to the keychain.
+ */
+ if (lmhash == NULL || nthash == NULL) {
+ err = SMB_KEYCHAIN_BADPASSWD;
+ goto out;
+ }
+ memcpy(pk.pk_lmhash, lmhash, SMBIOC_HASH_SZ);
+ memcpy(pk.pk_nthash, nthash, SMBIOC_HASH_SZ);
/* FALLTHROUGH */
case SMBIOC_PK_CHK:
case SMBIOC_PK_DEL:
- if (dom == NULL)
- return (SMB_KEYCHAIN_BADDOMAIN);
- if (strlcpy(pk.pk_dom, dom, sizeof (pk.pk_dom)) >=
- sizeof (pk.pk_dom))
- return (SMB_KEYCHAIN_BADDOMAIN);
- if (usr == NULL)
- return (SMB_KEYCHAIN_BADUSER);
- if (strlcpy(pk.pk_usr, usr, sizeof (pk.pk_usr)) >=
- sizeof (pk.pk_usr))
- return (SMB_KEYCHAIN_BADUSER);
+ /*
+ * Copy domain and user.
+ */
+ if (dom == NULL) {
+ err = SMB_KEYCHAIN_BADDOMAIN;
+ goto out;
+ }
+ sz = sizeof (pk.pk_dom);
+ if (strlcpy(pk.pk_dom, dom, sz) >= sz) {
+ err = SMB_KEYCHAIN_BADDOMAIN;
+ goto out;
+ }
+ if (usr == NULL) {
+ err = SMB_KEYCHAIN_BADUSER;
+ goto out;
+ }
+ sz = sizeof (pk.pk_usr);
+ if (strlcpy(pk.pk_usr, usr, sz) >= sz) {
+ err = SMB_KEYCHAIN_BADUSER;
+ goto out;
+ }
break;
case SMBIOC_PK_DEL_OWNER: /* all owned by the caller */
case SMBIOC_PK_DEL_EVERYONE: /* all owned by everyone */
/*
* These two do not copyin any args, but we'll
- * pass &pk here anyway just so we can use the
+ * pass pk here anyway just so we can use the
* common code path below.
*/
break;
default:
- return (SMB_KEYCHAIN_UNKNOWN);
+ err = SMB_KEYCHAIN_UNKNOWN;
+ goto out;
}
fd = smb_open_driver();
@@ -107,28 +127,57 @@ smbfs_keychain_cmn(
}
err = 0;
- if (ioctl(fd, cmd, &pk) < 0)
+ if (ioctl(fd, cmd, &pk) < 0) {
err = errno;
+ goto out;
+ }
+
+ if (cmd == SMBIOC_PK_CHK) {
+ if (lmhash != NULL)
+ memcpy(lmhash, pk.pk_lmhash, SMBIOC_HASH_SZ);
+ if (nthash != NULL)
+ memcpy(nthash, pk.pk_nthash, SMBIOC_HASH_SZ);
+ }
- close(fd);
out:
- memset(&pk, 0, sizeof (pk));
+ if (fd != -1)
+ close(fd);
+
return (err);
}
-/* Add a password to the keychain. */
+/*
+ * Add a password to the keychain.
+ *
+ * Note: pass is a cleartext password.
+ * We use it here to compute the LM hash and NT hash,
+ * and then store ONLY the hashes.
+ */
int
smbfs_keychain_add(uid_t uid, const char *dom, const char *usr,
const char *pass)
{
- return (smbfs_keychain_cmn(SMBIOC_PK_ADD, uid, dom, usr, pass));
+ uchar_t lmhash[SMBIOC_HASH_SZ];
+ uchar_t nthash[SMBIOC_HASH_SZ];
+ int err, cmd = SMBIOC_PK_ADD;
+
+ if (pass == NULL)
+ return (SMB_KEYCHAIN_BADPASSWD);
+
+ if ((err = ntlm_compute_lm_hash(lmhash, pass)) != 0)
+ return (err);
+ if ((err = ntlm_compute_nt_hash(nthash, pass)) != 0)
+ return (err);
+
+ err = smbfs_keychain_cmn(cmd, uid, dom, usr, lmhash, nthash);
+ return (err);
}
/* Delete a password from the keychain. */
int
smbfs_keychain_del(uid_t uid, const char *dom, const char *usr)
{
- return (smbfs_keychain_cmn(SMBIOC_PK_DEL, uid, dom, usr, NULL));
+ return (smbfs_keychain_cmn(SMBIOC_PK_DEL, uid, dom, usr, NULL, NULL));
}
/*
@@ -138,7 +187,22 @@ smbfs_keychain_del(uid_t uid, const char *dom, const char *usr)
int
smbfs_keychain_chk(const char *dom, const char *usr)
{
- return (smbfs_keychain_cmn(SMBIOC_PK_CHK, (uid_t)-1, dom, usr, NULL));
+ uid_t uid = (uid_t)-1;
+ return (smbfs_keychain_cmn(SMBIOC_PK_CHK, uid, dom, usr, NULL, NULL));
+}
+
+/*
+ * Get the stored hashes
+ */
+int
+smbfs_keychain_get(const char *dom, const char *usr,
+ uchar_t *lmhash, uchar_t *nthash)
+{
+ uid_t uid = (uid_t)-1;
+ int err, cmd = SMBIOC_PK_CHK;
+
+ err = smbfs_keychain_cmn(cmd, uid, dom, usr, lmhash, nthash);
+ return (err);
}
/*
@@ -147,7 +211,9 @@ smbfs_keychain_chk(const char *dom, const char *usr)
int
smbfs_keychain_del_owner()
{
- return (smbfs_keychain_cmn(SMBIOC_PK_DEL_OWNER, getuid(), 0, 0, 0));
+ int cmd = SMBIOC_PK_DEL_OWNER;
+ uid_t uid = getuid();
+ return (smbfs_keychain_cmn(cmd, uid, NULL, NULL, NULL, NULL));
}
/*
@@ -157,7 +223,51 @@ smbfs_keychain_del_owner()
int
smbfs_keychain_del_everyone()
{
- return (smbfs_keychain_cmn(SMBIOC_PK_DEL_EVERYONE, getuid(), 0, 0, 0));
+ int cmd = SMBIOC_PK_DEL_EVERYONE;
+ uid_t uid = getuid();
+ return (smbfs_keychain_cmn(cmd, uid, NULL, NULL, NULL, NULL));
+}
+
+/*
+ * Private function to get keychain p/w hashes.
+ */
+int
+smb_get_keychain(struct smb_ctx *ctx)
+{
+ int err;
+
+ if (ctx->ct_fullserver == NULL) {
+ DPRINT("ct_fullserver == NULL");
+ return (EINVAL);
+ }
+
+ /*
+ * 1st: try lookup using system name
+ */
+ err = smbfs_keychain_get(ctx->ct_fullserver, ctx->ct_user,
+ ctx->ct_lmhash, ctx->ct_nthash);
+ if (!err) {
+ ctx->ct_flags |= SMBCF_KCFOUND;
+ DPRINT("found keychain entry for"
+ " server/user: %s/%s\n",
+ ctx->ct_fullserver, ctx->ct_user);
+ return (0);
+ }
+
+ /*
+ * 2nd: try lookup using domain name
+ */
+ err = smbfs_keychain_get(ctx->ct_domain, ctx->ct_user,
+ ctx->ct_lmhash, ctx->ct_nthash);
+ if (!err) {
+ ctx->ct_flags |= (SMBCF_KCFOUND | SMBCF_KCDOMAIN);
+ DPRINT("found keychain entry for"
+ " domain/user: %s/%s\n",
+ ctx->ct_domain, ctx->ct_user);
+ return (0);
+ }
+
+ return (err);
}
@@ -173,27 +283,33 @@ int
smbfs_default_dom_usr(const char *home, const char *server,
char *dom, int maxdom, char *usr, int maxusr)
{
- struct smb_ctx sctx, *ctx = &sctx;
+ struct smb_ctx *ctx;
int err;
- err = smb_ctx_init(ctx, 0, NULL, SMBL_VC, SMBL_VC, SMB_ST_ANY);
+ err = smb_ctx_alloc(&ctx);
if (err)
return (err);
+
if (server)
- smb_ctx_setserver(ctx, server);
- if (home && *home)
- ctx->ct_home = (char *)home;
+ smb_ctx_setfullserver(ctx, server);
+
+ if (home && *home) {
+ if (ctx->ct_home)
+ free(ctx->ct_home);
+ ctx->ct_home = strdup(home);
+ }
+
err = smb_ctx_readrc(ctx);
if (err)
- return (err);
- if (smb_rc)
- rc_close(smb_rc);
+ goto out;
if (dom)
- strlcpy(dom, ctx->ct_ssn.ioc_workgroup, maxdom);
+ strlcpy(dom, ctx->ct_domain, maxdom);
if (usr)
- strlcpy(usr, ctx->ct_ssn.ioc_user, maxusr);
+ strlcpy(usr, ctx->ct_user, maxusr);
- return (0);
+out:
+ smb_ctx_free(ctx);
+ return (err);
}
diff --git a/usr/src/lib/libsmbfs/smb/krb5ssp.c b/usr/src/lib/libsmbfs/smb/krb5ssp.c
new file mode 100644
index 0000000000..fbbd08398b
--- /dev/null
+++ b/usr/src/lib/libsmbfs/smb/krb5ssp.c
@@ -0,0 +1,535 @@
+/*
+ * Copyright (c) 2000, Boris Popov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Boris Popov.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Kerberos V Security Support Provider
+ *
+ * Based on code previously in ctx.c (from Boris Popov?)
+ * but then mostly rewritten at Sun.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <strings.h>
+#include <netdb.h>
+#include <libintl.h>
+#include <xti.h>
+#include <assert.h>
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/byteorder.h>
+#include <sys/socket.h>
+#include <sys/fcntl.h>
+
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+
+#include <netsmb/smb.h>
+#include <netsmb/smb_lib.h>
+#include <netsmb/mchain.h>
+
+#include "private.h"
+#include "charsets.h"
+#include "spnego.h"
+#include "derparse.h"
+#include "ssp.h"
+
+#include <kerberosv5/krb5.h>
+#include <kerberosv5/com_err.h>
+
+/* RFC 1964 token ID codes */
+#define KRB_AP_REQ 1
+#define KRB_AP_REP 2
+#define KRB_ERROR 3
+
+extern MECH_OID g_stcMechOIDList [];
+
+typedef struct krb5ssp_state {
+ /* Filled in by krb5ssp_init_client */
+ krb5_context ss_krb5ctx; /* krb5 context (ptr) */
+ krb5_ccache ss_krb5cc; /* credentials cache (ptr) */
+ krb5_principal ss_krb5clp; /* client principal (ptr) */
+ /* Filled in by krb5ssp_get_tkt */
+ krb5_auth_context ss_auth; /* auth ctx. w/ server (ptr) */
+} krb5ssp_state_t;
+
+
+/*
+ * adds a GSSAPI wrapper
+ */
+int
+krb5ssp_tkt2gtok(uchar_t *tkt, ulong_t tktlen,
+ uchar_t **gtokp, ulong_t *gtoklenp)
+{
+ ulong_t len;
+ ulong_t bloblen = tktlen;
+ uchar_t krbapreq[2] = { KRB_AP_REQ, 0 };
+ uchar_t *blob = NULL; /* result */
+ uchar_t *b;
+
+ bloblen += sizeof (krbapreq);
+ bloblen += g_stcMechOIDList[spnego_mech_oid_Kerberos_V5].iLen;
+ len = bloblen;
+ bloblen = ASNDerCalcTokenLength(bloblen, bloblen);
+ if ((blob = malloc(bloblen)) == NULL) {
+ DPRINT("malloc");
+ return (ENOMEM);
+ }
+
+ b = blob;
+ b += ASNDerWriteToken(b, SPNEGO_NEGINIT_APP_CONSTRUCT, NULL, len);
+ b += ASNDerWriteOID(b, spnego_mech_oid_Kerberos_V5);
+ memcpy(b, krbapreq, sizeof (krbapreq));
+ b += sizeof (krbapreq);
+
+ assert(b + tktlen == blob + bloblen);
+ memcpy(b, tkt, tktlen);
+ *gtoklenp = bloblen;
+ *gtokp = blob;
+ return (0);
+}
+
+/*
+ * See "Windows 2000 Kerberos Interoperability" paper by
+ * Christopher Nebergall. RC4 HMAC is the W2K default but
+ * Samba support lagged (not due to Samba itself, but due to OS'
+ * Kerberos implementations.)
+ *
+ * Only session enc type should matter, not ticket enc type,
+ * per Sam Hartman on krbdev.
+ *
+ * Preauthentication failure topics in krb-protocol may help here...
+ * try "John Brezak" and/or "Clifford Neuman" too.
+ */
+static krb5_enctype kenctypes[] = {
+ ENCTYPE_ARCFOUR_HMAC, /* defined in krb5.h */
+ ENCTYPE_DES_CBC_MD5,
+ ENCTYPE_DES_CBC_CRC,
+ ENCTYPE_NULL
+};
+
+static const int rq_opts =
+ AP_OPTS_USE_SUBKEY | AP_OPTS_MUTUAL_REQUIRED;
+
+/*
+ * Obtain a kerberos ticket for the host we're connecting to.
+ * (This does the KRB_TGS exchange.)
+ */
+static int
+krb5ssp_get_tkt(krb5ssp_state_t *ss, char *server,
+ uchar_t **tktp, ulong_t *tktlenp)
+{
+ krb5_context kctx = ss->ss_krb5ctx;
+ krb5_ccache kcc = ss->ss_krb5cc;
+ krb5_data indata = {0};
+ krb5_data outdata = {0};
+ krb5_error_code kerr = 0;
+ const char *fn = NULL;
+ uchar_t *tkt;
+
+ /* Should have these from krb5ssp_init_client. */
+ if (kctx == NULL || kcc == NULL) {
+ fn = "null kctx or kcc";
+ kerr = EINVAL;
+ goto out;
+ }
+
+ kerr = krb5_set_default_tgs_enctypes(kctx, kenctypes);
+ if (kerr != 0) {
+ fn = "krb5_set_default_tgs_enctypes";
+ goto out;
+ }
+
+ /* Override the krb5 library default. */
+ indata.data = "";
+
+ kerr = krb5_mk_req(kctx, &ss->ss_auth, rq_opts, "cifs", server,
+ &indata, kcc, &outdata);
+ if (kerr != 0) {
+ fn = "krb5_mk_req";
+ goto out;
+ }
+ if ((tkt = malloc(outdata.length)) == NULL) {
+ kerr = ENOMEM;
+ fn = "malloc signing key";
+ goto out;
+ }
+ memcpy(tkt, outdata.data, outdata.length);
+ *tktp = tkt;
+ *tktlenp = outdata.length;
+ kerr = 0;
+
+out:
+ if (kerr) {
+ if (fn == NULL)
+ fn = "?";
+ DPRINT("%s err 0x%x: %s", fn, kerr, error_message(kerr));
+ if (kerr <= 0 || kerr > ESTALE)
+ kerr = EAUTH;
+ }
+
+ if (outdata.data)
+ krb5_free_data_contents(kctx, &outdata);
+
+ /* Free kctx in krb5ssp_destroy */
+ return (kerr);
+}
+
+
+/*
+ * Build an RFC 1964 KRB_AP_REQ message
+ * The caller puts on the SPNEGO wrapper.
+ */
+int
+krb5ssp_put_request(struct ssp_ctx *sp, struct mbdata *out_mb)
+{
+ int err;
+ struct smb_ctx *ctx = sp->smb_ctx;
+ krb5ssp_state_t *ss = sp->sp_private;
+ uchar_t *tkt = NULL;
+ ulong_t tktlen;
+ uchar_t *gtok = NULL; /* gssapi token */
+ ulong_t gtoklen; /* gssapi token length */
+ char *prin = ctx->ct_srvname;
+
+ if ((err = krb5ssp_get_tkt(ss, prin, &tkt, &tktlen)) != 0)
+ goto out;
+ if ((err = krb5ssp_tkt2gtok(tkt, tktlen, &gtok, &gtoklen)) != 0)
+ goto out;
+
+ if ((err = mb_init(out_mb, gtoklen)) != 0)
+ goto out;
+ if ((err = mb_put_mem(out_mb, gtok, gtoklen)) != 0)
+ goto out;
+
+ if (ctx->ct_vcflags & SMBV_WILL_SIGN)
+ ctx->ct_hflags2 |= SMB_FLAGS2_SECURITY_SIGNATURE;
+
+out:
+ if (gtok)
+ free(gtok);
+ if (tkt)
+ free(tkt);
+
+ return (err);
+}
+
+/*
+ * Unwrap a GSS-API encapsulated RFC 1964 reply message,
+ * i.e. type KRB_AP_REP or KRB_ERROR.
+ */
+int
+krb5ssp_get_reply(struct ssp_ctx *sp, struct mbdata *in_mb)
+{
+ krb5ssp_state_t *ss = sp->sp_private;
+ mbuf_t *m = in_mb->mb_top;
+ int err = EBADRPC;
+ int dlen, rc;
+ long actual_len, token_len;
+ uchar_t *data;
+ krb5_data ap = {0};
+ krb5_ap_rep_enc_part *reply = NULL;
+
+ /* cheating: this mbuf is contiguous */
+ assert(m->m_data == in_mb->mb_pos);
+ data = (uchar_t *)m->m_data;
+ dlen = m->m_len;
+
+ /*
+ * Peel off the GSS-API wrapper. Looks like:
+ * AppToken: 60 81 83
+ * OID(KRB5): 06 09 2a 86 48 86 f7 12 01 02 02
+ * KRB_AP_REP: 02 00
+ */
+ rc = ASNDerCheckToken(data, SPNEGO_NEGINIT_APP_CONSTRUCT,
+ 0, dlen, &token_len, &actual_len);
+ if (rc != SPNEGO_E_SUCCESS) {
+ DPRINT("no AppToken? rc=0x%x", rc);
+ goto out;
+ }
+ if (dlen < actual_len)
+ goto out;
+ data += actual_len;
+ dlen -= actual_len;
+
+ /* OID (KRB5) */
+ rc = ASNDerCheckOID(data, spnego_mech_oid_Kerberos_V5,
+ dlen, &actual_len);
+ if (rc != SPNEGO_E_SUCCESS) {
+ DPRINT("no OID? rc=0x%x", rc);
+ goto out;
+ }
+ if (dlen < actual_len)
+ goto out;
+ data += actual_len;
+ dlen -= actual_len;
+
+ /* KRB_AP_REP or KRB_ERROR */
+ if (data[0] != KRB_AP_REP) {
+ DPRINT("KRB5 type: %d", data[1]);
+ goto out;
+ }
+ if (dlen < 2)
+ goto out;
+ data += 2;
+ dlen -= 2;
+
+ /*
+ * Now what's left should be a krb5 reply
+ * NB: ap is NOT allocated, so don't free it.
+ */
+ ap.length = dlen;
+ ap.data = (char *)data;
+ rc = krb5_rd_rep(ss->ss_krb5ctx, ss->ss_auth, &ap, &reply);
+ if (rc != 0) {
+ DPRINT("krb5_rd_rep: err 0x%x (%s)",
+ rc, error_message(rc));
+ err = EAUTH;
+ goto out;
+ }
+
+ /*
+ * Have the decoded reply. Save anything?
+ *
+ * NB: If this returns an error, we will get
+ * no more calls into this back-end module.
+ */
+ err = 0;
+
+out:
+ if (reply != NULL)
+ krb5_free_ap_rep_enc_part(ss->ss_krb5ctx, reply);
+ if (err)
+ DPRINT("ret %d", err);
+
+ return (err);
+}
+
+/*
+ * krb5ssp_final
+ *
+ * Called after successful authentication.
+ * Setup the MAC key for signing.
+ */
+int
+krb5ssp_final(struct ssp_ctx *sp)
+{
+ struct smb_ctx *ctx = sp->smb_ctx;
+ krb5ssp_state_t *ss = sp->sp_private;
+ krb5_keyblock *ssn_key = NULL;
+ int err, len;
+
+ /*
+ * Save the session key, used for SMB signing
+ * and possibly other consumers (RPC).
+ */
+ err = krb5_auth_con_getlocalsubkey(
+ ss->ss_krb5ctx, ss->ss_auth, &ssn_key);
+ if (err != 0) {
+ DPRINT("_getlocalsubkey, err=0x%x (%s)",
+ err, error_message(err));
+ if (err <= 0 || err > ESTALE)
+ err = EAUTH;
+ goto out;
+ }
+ memset(ctx->ct_ssn_key, 0, SMBIOC_HASH_SZ);
+ if ((len = ssn_key->length) > SMBIOC_HASH_SZ)
+ len = SMBIOC_HASH_SZ;
+ memcpy(ctx->ct_ssn_key, ssn_key->contents, len);
+
+ /*
+ * Set the MAC key on the first successful auth.
+ */
+ if ((ctx->ct_hflags2 & SMB_FLAGS2_SECURITY_SIGNATURE) &&
+ (ctx->ct_mackey == NULL)) {
+ ctx->ct_mackeylen = ssn_key->length;
+ ctx->ct_mackey = malloc(ctx->ct_mackeylen);
+ if (ctx->ct_mackey == NULL) {
+ ctx->ct_mackeylen = 0;
+ err = ENOMEM;
+ goto out;
+ }
+ memcpy(ctx->ct_mackey, ssn_key->contents,
+ ctx->ct_mackeylen);
+ /*
+ * Apparently, the server used seq. no. zero
+ * for our previous message, so next is two.
+ */
+ ctx->ct_mac_seqno = 2;
+ }
+ err = 0;
+
+out:
+ if (ssn_key)
+ krb5_free_keyblock(ss->ss_krb5ctx, ssn_key);
+
+ return (err);
+}
+
+/*
+ * krb5ssp_next_token
+ *
+ * See ssp.c: ssp_ctx_next_token
+ */
+int
+krb5ssp_next_token(struct ssp_ctx *sp, struct mbdata *in_mb,
+ struct mbdata *out_mb)
+{
+ int err;
+
+ /*
+ * Note: in_mb == NULL on the first call.
+ */
+ if (in_mb) {
+ err = krb5ssp_get_reply(sp, in_mb);
+ if (err)
+ goto out;
+ }
+
+ if (out_mb) {
+ err = krb5ssp_put_request(sp, out_mb);
+ } else
+ err = krb5ssp_final(sp);
+
+out:
+ if (err)
+ DPRINT("ret: %d", err);
+ return (err);
+}
+
+/*
+ * krb5ssp_ctx_destroy
+ *
+ * Destroy mechanism-specific data.
+ */
+void
+krb5ssp_destroy(struct ssp_ctx *sp)
+{
+ krb5ssp_state_t *ss;
+ krb5_context kctx;
+
+ ss = sp->sp_private;
+ if (ss == NULL)
+ return;
+ sp->sp_private = NULL;
+
+ if ((kctx = ss->ss_krb5ctx) != NULL) {
+ /* from krb5ssp_get_tkt */
+ if (ss->ss_auth)
+ krb5_auth_con_free(kctx, ss->ss_auth);
+ /* from krb5ssp_init_client */
+ if (ss->ss_krb5clp)
+ krb5_free_principal(kctx, ss->ss_krb5clp);
+ if (ss->ss_krb5cc)
+ krb5_cc_close(kctx, ss->ss_krb5cc);
+ krb5_free_context(kctx);
+ }
+
+ free(ss);
+}
+
+/*
+ * krb5ssp_init_clnt
+ *
+ * Initialize a new Kerberos SSP client context.
+ *
+ * The user must already have a TGT in their credential cache,
+ * as shown by the "klist" command.
+ */
+int
+krb5ssp_init_client(struct ssp_ctx *sp)
+{
+ krb5ssp_state_t *ss;
+ krb5_error_code kerr;
+ krb5_context kctx = NULL;
+ krb5_ccache kcc = NULL;
+ krb5_principal kprin = NULL;
+
+ if ((sp->smb_ctx->ct_authflags & SMB_AT_KRB5) == 0) {
+ DPRINT("KRB5 not in authflags");
+ return (ENOTSUP);
+ }
+
+ ss = calloc(1, sizeof (*ss));
+ if (ss == NULL)
+ return (ENOMEM);
+
+ sp->sp_nexttok = krb5ssp_next_token;
+ sp->sp_destroy = krb5ssp_destroy;
+ sp->sp_private = ss;
+
+ kerr = krb5_init_context(&kctx);
+ if (kerr) {
+ DPRINT("krb5_init_context, kerr 0x%x", kerr);
+ goto errout;
+ }
+ ss->ss_krb5ctx = kctx;
+
+ /* non-default would instead use krb5_cc_resolve */
+ kerr = krb5_cc_default(kctx, &kcc);
+ if (kerr) {
+ DPRINT("krb5_cc_default, kerr 0x%x", kerr);
+ goto errout;
+ }
+ ss->ss_krb5cc = kcc;
+
+ /*
+ * Get the client principal (ticket),
+ * or discover that we don't have one.
+ */
+ kerr = krb5_cc_get_principal(kctx, kcc, &kprin);
+ if (kerr) {
+ DPRINT("krb5_cc_get_principal, kerr 0x%x", kerr);
+ goto errout;
+ }
+ ss->ss_krb5clp = kprin;
+
+ /* Success! */
+ DPRINT("Ticket cache: %s:%s",
+ krb5_cc_get_type(kctx, kcc),
+ krb5_cc_get_name(kctx, kcc));
+ return (0);
+
+errout:
+ krb5ssp_destroy(sp);
+ return (ENOTSUP);
+}
diff --git a/usr/src/lib/libsmbfs/smb/llib-lsmbfs b/usr/src/lib/libsmbfs/smb/llib-lsmbfs
index 05e1967055..e8e05e4272 100644
--- a/usr/src/lib/libsmbfs/smb/llib-lsmbfs
+++ b/usr/src/lib/libsmbfs/smb/llib-lsmbfs
@@ -20,13 +20,18 @@
*/
/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
/*LINTLIBRARY*/
/*PROTOLIB1*/
+#include <netsmb/smbfs_api.h>
+#include <netsmb/smbfs_acl.h>
+
#include <netsmb/smb_lib.h>
+#include <netsmb/smb_keychain.h>
+#include <netsmb/smb_netshareenum.h>
+#include <netsmb/smb_rap.h>
+
diff --git a/usr/src/lib/libsmbfs/smb/mapfile-vers b/usr/src/lib/libsmbfs/smb/mapfile-vers
index f927c7bcb8..3486333120 100644
--- a/usr/src/lib/libsmbfs/smb/mapfile-vers
+++ b/usr/src/lib/libsmbfs/smb/mapfile-vers
@@ -36,7 +36,7 @@
# MAPFILE HEADER END
#
-SUNWprivate_1.0 {
+SUNWprivate {
global:
convert_leunicode_to_utf8;
convert_unicode_to_utf8;
@@ -58,27 +58,37 @@ SUNWprivate_1.0 {
nls_str_toloc;
nls_str_upper;
- rc_close;
- rc_open;
+ smb_close_rcfile;
+ smb_ctx_alloc;
smb_ctx_done;
smb_ctx_flags2;
+ smb_ctx_free;
+ smb_ctx_get_ssn;
+ smb_ctx_get_ssnkey;
+ smb_ctx_get_tree;
+ smb_ctx_gethandle;
smb_ctx_init;
- smb_ctx_lookup;
+ smb_ctx_kill;
smb_ctx_opt;
+ smb_ctx_parseunc;
smb_ctx_readrc;
smb_ctx_resolve;
+ smb_ctx_scan_argv;
smb_ctx_set_close_hook;
+ smb_ctx_setauthflags;
+ smb_ctx_setdomain;
smb_ctx_setfullserver;
smb_ctx_setpassword;
+ smb_ctx_setpwhash;
+ smb_ctx_setscope;
smb_ctx_setserver;
smb_ctx_setshare;
smb_ctx_setsrvaddr;
smb_ctx_setuser;
- smb_ctx_setworkgroup;
+ smb_ctx_setwins;
- smb_ctx_tdis;
smb_debug = NODIRECT; # data
smb_error;
#
@@ -89,14 +99,20 @@ SUNWprivate_1.0 {
smb_fh_write;
smb_fh_xactnp;
#
+ smb_get_authentication;
smb_getprogname;
+ smb_iod_connect;
+ smb_iod_door_path;
+ smb_iod_open_door;
+ smb_iod_work;
smb_lib_init;
smb_netshareenum; # will move to libnetapi
smb_open_rcfile;
+ smb_printer_open;
+ smb_printer_close;
smb_simplecrypt;
smb_simpledecrypt;
smb_strerror;
- smb_rc = NODIRECT; # data
#
# Functions to support the Remote Access Protocol (RAP)
smb_rap_create;
@@ -126,7 +142,7 @@ SUNWprivate_1.0 {
smbfs_keychain_del_everyone;
smbfs_keychain_del_owner;
- unpercent;
+ smbutil_std_opts;
local:
*;
};
diff --git a/usr/src/lib/libsmbfs/smb/mbuf.c b/usr/src/lib/libsmbfs/smb/mbuf.c
index 2e995dad50..9380ec12e9 100644
--- a/usr/src/lib/libsmbfs/smb/mbuf.c
+++ b/usr/src/lib/libsmbfs/smb/mbuf.c
@@ -33,7 +33,7 @@
*/
/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -47,17 +47,11 @@
#include <libintl.h>
#include <assert.h>
-#include <netsmb/smb.h>
#include <netsmb/smb_lib.h>
#include <netsmb/mchain.h>
#include "private.h"
-
-#ifdef APPLE
-#define __func__ ""
-#define MBERROR(format, args...) \
- printf("%s(%d): "format, __func__, __LINE__, ## args)
-#endif
+#include "charsets.h"
static int
m_get(size_t len, struct mbuf **mpp)
@@ -85,7 +79,7 @@ m_free(struct mbuf *m)
free(m);
}
-static void
+void
m_freem(struct mbuf *m0)
{
struct mbuf *m;
@@ -97,7 +91,7 @@ m_freem(struct mbuf *m0)
}
}
-static size_t
+size_t
m_totlen(struct mbuf *m0)
{
struct mbuf *m = m0;
@@ -226,61 +220,64 @@ int
mb_put_uint8(struct mbdata *mbp, uint8_t x)
{
uint8_t y = x;
- return (mb_put_mem(mbp, (char *)&y, sizeof (y)));
+ return (mb_put_mem(mbp, &y, sizeof (y)));
}
int
mb_put_uint16be(struct mbdata *mbp, uint16_t x)
{
uint16_t y = htobes(x);
- return (mb_put_mem(mbp, (char *)&y, sizeof (y)));
+ return (mb_put_mem(mbp, &y, sizeof (y)));
}
int
mb_put_uint16le(struct mbdata *mbp, uint16_t x)
{
uint16_t y = htoles(x);
- return (mb_put_mem(mbp, (char *)&y, sizeof (y)));
+ return (mb_put_mem(mbp, &y, sizeof (y)));
}
int
mb_put_uint32be(struct mbdata *mbp, uint32_t x)
{
uint32_t y = htobel(x);
- return (mb_put_mem(mbp, (char *)&y, sizeof (y)));
+ return (mb_put_mem(mbp, &y, sizeof (y)));
}
int
mb_put_uint32le(struct mbdata *mbp, uint32_t x)
{
uint32_t y = htolel(x);
- return (mb_put_mem(mbp, (char *)&y, sizeof (y)));
+ return (mb_put_mem(mbp, &y, sizeof (y)));
}
int
mb_put_uint64be(struct mbdata *mbp, uint64_t x)
{
uint64_t y = htobeq(x);
- return (mb_put_mem(mbp, (char *)&y, sizeof (y)));
+ return (mb_put_mem(mbp, &y, sizeof (y)));
}
int
mb_put_uint64le(struct mbdata *mbp, uint64_t x)
{
uint64_t y = htoleq(x);
- return (mb_put_mem(mbp, (char *)&y, sizeof (y)));
+ return (mb_put_mem(mbp, &y, sizeof (y)));
}
int
-mb_put_mem(struct mbdata *mbp, const char *source, size_t size)
+mb_put_mem(struct mbdata *mbp, const void *vmem, size_t size)
{
struct mbuf *m;
+ const char *src;
char *dst;
size_t cplen;
int error;
if (size == 0)
return (0);
+
+ src = vmem;
m = mbp->mb_cur;
if ((error = m_getm(m, size, &m)) != 0)
return (error);
@@ -293,9 +290,9 @@ mb_put_mem(struct mbdata *mbp, const char *source, size_t size)
if (cplen > size)
cplen = size;
dst = mtod(m, char *) + m->m_len;
- if (source) {
- bcopy(source, dst, cplen);
- source += cplen;
+ if (src) {
+ bcopy(src, dst, cplen);
+ src += cplen;
} else
bzero(dst, cplen);
size -= cplen;
@@ -307,10 +304,26 @@ mb_put_mem(struct mbdata *mbp, const char *source, size_t size)
return (0);
}
+/*
+ * Append another mbuf to the mbuf chain.
+ * If what we're appending is smaller than
+ * the current trailing space, just copy.
+ * This always consumes the passed mbuf.
+ */
int
mb_put_mbuf(struct mbdata *mbp, struct mbuf *m)
{
- mbp->mb_cur->m_next = m;
+ struct mbuf *cm = mbp->mb_cur;
+ int ts = M_TRAILINGSPACE(cm);
+
+ if (m->m_next == NULL && m->m_len <= ts) {
+ /* just copy */
+ mb_put_mem(mbp, m->m_data, m->m_len);
+ m_freem(m);
+ return (0);
+ }
+
+ cm->m_next = m;
while (m) {
mbp->mb_count += m->m_len;
if (m->m_next == NULL)
@@ -322,17 +335,62 @@ mb_put_mbuf(struct mbdata *mbp, struct mbuf *m)
return (0);
}
+/*
+ * Convenience function to put an OEM or Unicode string,
+ * null terminated, and aligned if necessary.
+ */
int
-mb_put_pstring(struct mbdata *mbp, const char *s)
+mb_put_dstring(struct mbdata *mbp, const char *s, int uc)
{
- int error, len = strlen(s);
-
- if (len > 255) {
- len = 255;
+ int err;
+
+ if (uc) {
+ /* Put Unicode. align(2) first. */
+ if (mbp->mb_count & 1)
+ mb_put_uint8(mbp, 0);
+ err = mb_put_ustring(mbp, s);
+ } else {
+ /* Put ASCII (really OEM) */
+ err = mb_put_astring(mbp, s);
}
- if ((error = mb_put_uint8(mbp, len)) != 0)
- return (error);
- return (mb_put_mem(mbp, s, len));
+
+ return (err);
+}
+
+/*
+ * Put an ASCII string (really OEM), given a UTF-8 string.
+ */
+int
+mb_put_astring(struct mbdata *mbp, const char *s)
+{
+ char *abuf;
+ int err, len;
+
+ abuf = convert_utf8_to_wincs(s);
+ if (abuf == NULL)
+ return (ENOMEM);
+ len = strlen(abuf) + 1;
+ err = mb_put_mem(mbp, abuf, len);
+ free(abuf);
+ return (err);
+}
+
+/*
+ * Put UCS-2LE, given a UTF-8 string.
+ */
+int
+mb_put_ustring(struct mbdata *mbp, const char *s)
+{
+ uint16_t *ubuf;
+ int err, len;
+
+ ubuf = convert_utf8_to_leunicode(s);
+ if (ubuf == NULL)
+ return (ENOMEM);
+ len = unicode_strlen(ubuf) + 1;
+ err = mb_put_mem(mbp, ubuf, (len << 1));
+ free(ubuf);
+ return (err);
}
/*
@@ -343,111 +401,114 @@ mb_put_pstring(struct mbdata *mbp, const char *s)
int
mb_get_uint8(struct mbdata *mbp, uint8_t *x)
{
- return (mb_get_mem(mbp, (char *)x, 1));
+ return (mb_get_mem(mbp, x, 1));
}
int
mb_get_uint16(struct mbdata *mbp, uint16_t *x)
{
- return (mb_get_mem(mbp, (char *)x, 2));
+ return (mb_get_mem(mbp, x, 2));
}
int
mb_get_uint16le(struct mbdata *mbp, uint16_t *x)
{
uint16_t v;
- int error = mb_get_uint16(mbp, &v);
+ int err;
+ if ((err = mb_get_mem(mbp, &v, 2)) != 0)
+ return (err);
if (x != NULL)
*x = letohs(v);
- return (error);
+ return (0);
}
int
mb_get_uint16be(struct mbdata *mbp, uint16_t *x) {
uint16_t v;
- int error = mb_get_uint16(mbp, &v);
+ int err;
+ if ((err = mb_get_mem(mbp, &v, 2)) != 0)
+ return (err);
if (x != NULL)
*x = betohs(v);
- return (error);
+ return (0);
}
int
mb_get_uint32(struct mbdata *mbp, uint32_t *x)
{
- return (mb_get_mem(mbp, (char *)x, 4));
+ return (mb_get_mem(mbp, x, 4));
}
int
mb_get_uint32be(struct mbdata *mbp, uint32_t *x)
{
uint32_t v;
- int error;
+ int err;
- error = mb_get_uint32(mbp, &v);
+ if ((err = mb_get_mem(mbp, &v, 4)) != 0)
+ return (err);
if (x != NULL)
*x = betohl(v);
- return (error);
+ return (0);
}
int
mb_get_uint32le(struct mbdata *mbp, uint32_t *x)
{
uint32_t v;
- int error;
+ int err;
- error = mb_get_uint32(mbp, &v);
+ if ((err = mb_get_mem(mbp, &v, 4)) != 0)
+ return (err);
if (x != NULL)
*x = letohl(v);
- return (error);
+ return (0);
}
int
mb_get_uint64(struct mbdata *mbp, uint64_t *x)
{
- return (mb_get_mem(mbp, (char *)x, 8));
+ return (mb_get_mem(mbp, x, 8));
}
int
mb_get_uint64be(struct mbdata *mbp, uint64_t *x)
{
uint64_t v;
- int error;
+ int err;
- error = mb_get_uint64(mbp, &v);
+ if ((err = mb_get_mem(mbp, &v, 8)) != 0)
+ return (err);
if (x != NULL)
*x = betohq(v);
- return (error);
+ return (0);
}
int
mb_get_uint64le(struct mbdata *mbp, uint64_t *x)
{
uint64_t v;
- int error;
+ int err;
- error = mb_get_uint64(mbp, &v);
+ if ((err = mb_get_mem(mbp, &v, 8)) != 0)
+ return (err);
if (x != NULL)
*x = letohq(v);
- return (error);
+ return (0);
}
int
-mb_get_mem(struct mbdata *mbp, char *target, size_t size)
+mb_get_mem(struct mbdata *mbp, void *vmem, size_t size)
{
struct mbuf *m = mbp->mb_cur;
+ char *dst = vmem;
uint_t count;
while (size > 0) {
if (m == NULL) {
-#ifdef DEBUG
- printf(
- dgettext(TEXT_DOMAIN, "incomplete copy\n"));
-#endif
-#ifdef APPLE
- MBERROR("incomplete copy\n");
-#endif
+ /* DPRINT("incomplete copy"); */
return (EBADRPC);
}
count = mb_left(m, mbp->mb_pos);
@@ -460,15 +521,181 @@ mb_get_mem(struct mbdata *mbp, char *target, size_t size)
if (count > size)
count = size;
size -= count;
- if (target) {
+ if (dst) {
if (count == 1) {
- *target++ = *mbp->mb_pos;
+ *dst++ = *mbp->mb_pos;
} else {
- bcopy(mbp->mb_pos, target, count);
- target += count;
+ bcopy(mbp->mb_pos, dst, count);
+ dst += count;
}
}
mbp->mb_pos += count;
}
return (0);
}
+
+/*
+ * Get the next SIZE bytes as a separate mblk.
+ * Nothing fancy here - just copy.
+ */
+int
+mb_get_mbuf(struct mbdata *mbp, int size, struct mbuf **ret)
+{
+ mbuf_t *m;
+ int err;
+
+ err = m_get(size, &m);
+ if (err)
+ return (err);
+
+ err = mb_get_mem(mbp, m->m_data, size);
+ if (err) {
+ m_freem(m);
+ return (err);
+ }
+ m->m_len = size;
+ *ret = m;
+
+ return (0);
+}
+
+/*
+ * Get a string from the mbuf chain,
+ * either Unicode or OEM chars.
+ */
+int
+mb_get_string(struct mbdata *mbp, char **str_pp, int uc)
+{
+ int err;
+
+ if (uc)
+ err = mb_get_ustring(mbp, str_pp);
+ else
+ err = mb_get_astring(mbp, str_pp);
+ return (err);
+}
+
+/*
+ * Get an ASCII (really OEM) string from the mbuf chain
+ * and convert it to UTF-8
+ * Similar to mb_get_ustring below.
+ */
+int
+mb_get_astring(struct mbdata *real_mbp, char **str_pp)
+{
+ struct mbdata tmp_mb, *mbp;
+ char *tstr, *ostr;
+ int err, i, slen;
+ uint8_t ch;
+
+ /*
+ * First, figure out the string length.
+ * Use a copy of the real_mbp so we don't
+ * actually consume it here, then search for
+ * the null (or end of data).
+ */
+ bcopy(real_mbp, &tmp_mb, sizeof (tmp_mb));
+ mbp = &tmp_mb;
+ slen = 0;
+ for (;;) {
+ err = mb_get_uint8(mbp, &ch);
+ if (err)
+ break;
+ if (ch == 0)
+ break;
+ slen++;
+ }
+
+ /*
+ * Now read the (OEM) string for real.
+ * No need to re-check errors.
+ */
+ tstr = malloc(slen + 1);
+ if (tstr == NULL)
+ return (ENOMEM);
+ mbp = real_mbp;
+ for (i = 0; i < slen; i++) {
+ mb_get_uint8(mbp, &ch);
+ tstr[i] = ch;
+ }
+ tstr[i] = 0;
+ mb_get_uint8(mbp, NULL);
+
+ /*
+ * Convert OEM to UTF-8
+ */
+ ostr = convert_wincs_to_utf8(tstr);
+ free(tstr);
+ if (ostr == NULL)
+ return (ENOMEM);
+
+ *str_pp = ostr;
+ return (0);
+}
+
+/*
+ * Get a UCS-2LE string from the mbuf chain, and
+ * convert it to UTF-8.
+ *
+ * Similar to mb_get_astring below.
+ */
+int
+mb_get_ustring(struct mbdata *real_mbp, char **str_pp)
+{
+ struct mbdata tmp_mb, *mbp;
+ uint16_t *tstr;
+ char *ostr;
+ int err, i, slen;
+ uint16_t ch;
+
+ /*
+ * First, align(2) on the real_mbp
+ */
+ if (((uintptr_t)real_mbp->mb_pos) & 1)
+ mb_get_uint8(real_mbp, NULL);
+
+ /*
+ * Next, figure out the string length.
+ * Use a copy of the real_mbp so we don't
+ * actually consume it here, then search for
+ * the null (or end of data).
+ */
+ bcopy(real_mbp, &tmp_mb, sizeof (tmp_mb));
+ mbp = &tmp_mb;
+ slen = 0;
+ for (;;) {
+ err = mb_get_uint16le(mbp, &ch);
+ if (err)
+ break;
+ if (ch == 0)
+ break;
+ slen++;
+ }
+
+ /*
+ * Now read the (UCS-2) string for real.
+ * No need to re-check errors. Note:
+ * This puts the UCS-2 in NATIVE order!
+ */
+ tstr = calloc(slen + 1, 2);
+ if (tstr == NULL)
+ return (ENOMEM);
+ mbp = real_mbp;
+ for (i = 0; i < slen; i++) {
+ mb_get_uint16le(mbp, &ch);
+ tstr[i] = ch;
+ }
+ tstr[i] = 0;
+ mb_get_uint16le(mbp, NULL);
+
+ /*
+ * Convert UCS-2 (native!) to UTF-8
+ */
+ ostr = convert_unicode_to_utf8(tstr);
+ free(tstr);
+ if (ostr == NULL)
+ return (ENOMEM);
+
+ *str_pp = ostr;
+ return (0);
+}
diff --git a/usr/src/lib/libsmbfs/smb/nb.c b/usr/src/lib/libsmbfs/smb/nb.c
index f60ae0b314..f280420c54 100644
--- a/usr/src/lib/libsmbfs/smb/nb.c
+++ b/usr/src/lib/libsmbfs/smb/nb.c
@@ -32,29 +32,61 @@
* $Id: nb.c,v 1.1.1.2 2001/07/06 22:38:42 conrad Exp $
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
#include <sys/param.h>
#include <sys/socket.h>
-#include <ctype.h>
-#include <netdb.h>
#include <errno.h>
+#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
-#include <stdio.h>
#include <unistd.h>
#include <libintl.h>
+#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
+#include <cflib.h>
#include <netsmb/netbios.h>
#include <netsmb/smb_lib.h>
#include <netsmb/nb_lib.h>
-#include <cflib.h>
+int nb_ctx_setwins(struct nb_ctx *, const char *, const char *);
+
+
+/*
+ * API for library consumer to set wins1, wins2
+ */
+int
+smb_ctx_setwins(struct smb_ctx *ctx, const char *wins1, const char *wins2)
+{
+ struct nb_ctx *nb = ctx->ct_nb;
+
+ if (nb == NULL)
+ return (EINVAL);
+
+ return (nb_ctx_setwins(nb, wins1, wins2));
+}
+
+/*
+ * API for library consumer to set NB scope.
+ */
+int
+smb_ctx_setscope(struct smb_ctx *ctx, const char *scope)
+{
+ struct nb_ctx *nb = ctx->ct_nb;
+
+ if (nb == NULL)
+ return (EINVAL);
+
+ return (nb_ctx_setscope(nb, scope));
+}
int
nb_ctx_create(struct nb_ctx **ctxpp)
@@ -81,36 +113,37 @@ nb_ctx_done(struct nb_ctx *ctx)
free(ctx);
}
-static int
-nb_ctx_setwins(in_addr_t *ina_p, const char *str)
+int
+nb_ctx_setwins(struct nb_ctx *ctx, const char *wins1, const char *wins2)
{
struct in_addr ina;
- struct sockaddr *sap;
int error;
- if (str == NULL || str[0] == 0)
- return (EINVAL);
+ if (wins1 == NULL) {
+ ctx->nb_wins1 = 0;
+ ctx->nb_wins2 = 0;
+ return (0);
+ }
- if (inet_aton(str, &ina)) {
- *ina_p = ina.s_addr;
- } else {
- error = nb_resolvehost_in(str, &sap);
+ error = nb_resolvehost_in(wins1, &ina);
+ if (error) {
+ smb_error(dgettext(TEXT_DOMAIN, "can't resolve %s"),
+ error, wins1);
+ return (error);
+ }
+ ctx->nb_wins1 = ina.s_addr;
+
+ if (wins2 == NULL)
+ ctx->nb_wins2 = 0;
+ else {
+ error = nb_resolvehost_in(wins2, &ina);
if (error) {
smb_error(dgettext(TEXT_DOMAIN, "can't resolve %s"),
- error, str);
+ error, wins2);
return (error);
}
- if (sap->sa_family != AF_INET) {
- smb_error(dgettext(TEXT_DOMAIN,
- "unsupported address family %d"), 0,
- sap->sa_family);
- return (EINVAL);
- }
- /*LINTED*/
- *ina_p = ((struct sockaddr_in *)sap)->sin_addr.s_addr;
- free(sap);
+ ctx->nb_wins2 = ina.s_addr;
}
-
return (0);
}
@@ -126,10 +159,9 @@ nb_ctx_setns(struct nb_ctx *ctx, const char *addr)
{
int error;
- error = nb_ctx_setwins(&ctx->nb_wins1, addr);
+ error = nb_ctx_setwins(ctx, addr, NULL);
if (error)
return (error);
- ctx->nb_wins2 = 0;
/* Deal with explicit request for broadcast. */
if (ctx->nb_wins1 == INADDR_BROADCAST) {
@@ -182,23 +214,35 @@ int
nb_ctx_readrcsection(struct rcfile *rcfile, struct nb_ctx *ctx,
const char *sname, int level)
{
- char *p;
+ char *wins1, *wins2;
int error;
int nbns_enable;
int nbns_broadcast;
if (level > 1)
return (EINVAL);
+
+ /* External callers pass NULL to get the default. */
+ if (rcfile == NULL)
+ rcfile = smb_rc;
+
#ifdef NOT_DEFINED
rc_getint(rcfile, sname, "nbtimeout", &ctx->nb_timo);
rc_getstringptr(rcfile, sname, "nbscope", &p);
if (p)
nb_ctx_setscope(ctx, p);
#endif
- /* "nbns" will be "wins1" some day, and we'll have a "wins2" also */
- rc_getstringptr(rcfile, sname, "nbns", &p);
- if (p) {
- error = nb_ctx_setwins(&ctx->nb_wins1, p);
+ /*
+ * Get "wins1", "wins2" config strings.
+ * Also support legacy "nbns".
+ */
+ rc_getstringptr(rcfile, sname, "wins1", &wins1);
+ if (wins1 == NULL)
+ rc_getstringptr(rcfile, sname, "nbns", &wins1);
+ rc_getstringptr(rcfile, sname, "wins2", &wins2);
+
+ if (wins1 != NULL) {
+ error = nb_ctx_setwins(ctx, wins1, wins2);
if (error) {
smb_error(dgettext(TEXT_DOMAIN,
"invalid address specified in the section %s"),
diff --git a/usr/src/lib/libsmbfs/smb/nb_name.c b/usr/src/lib/libsmbfs/smb/nb_name.c
index af13efa7b4..604d9142cd 100644
--- a/usr/src/lib/libsmbfs/smb/nb_name.c
+++ b/usr/src/lib/libsmbfs/smb/nb_name.c
@@ -50,7 +50,7 @@
#include "private.h"
int
-nb_snballoc(int namelen, struct sockaddr_nb **dst)
+nb_snballoc(struct sockaddr_nb **dst)
{
struct sockaddr_nb *snb;
int slen;
@@ -73,6 +73,9 @@ nb_snbfree(struct sockaddr *snb)
/*
* Create a full NETBIOS address
+ * Passed names should already be upper case.
+ * Stores the names truncated or blank padded.
+ * NetBIOS name encoding happens later.
*/
int
nb_sockaddr(struct sockaddr *peer, struct nb_name *np,
@@ -81,53 +84,23 @@ nb_sockaddr(struct sockaddr *peer, struct nb_name *np,
{
struct sockaddr_nb *snb;
struct sockaddr_in *sin;
- struct hostent *hst;
- int nmlen, error;
+ int error;
if (peer && (peer->sa_family != AF_INET))
return (EPROTONOSUPPORT);
-#if NOT_DEFINED /* moved encoding into kernel */
- nmlen = nb_name_len(np);
- if (nmlen < NB_ENCNAMELEN)
- return (EINVAL);
-#else
- nmlen = NB_NAMELEN;
-#endif
- error = nb_snballoc(nmlen, &snb);
+ error = nb_snballoc(&snb);
if (error)
return (error);
- /*
- * Moved toupper() work to callers.
- *
- * Moved NetBIOS name encoding into the driver
- * so we have readable names right up until the
- * point where we marshall them in to a message.
- * Just makes debugging easier.
- */
-#if NOT_DEFINED
- if (nmlen != nb_name_encode(np, snb->snb_name))
- printf(dgettext(TEXT_DOMAIN,
- "a bug somewhere in the nb_name* code\n"));
- /* XXX */
-#else
- /*
- * OK, nb_snballoc() did bzero, set snb_family.
- * Hacks for "*" moved here from nb_name_encode(),
- * but belongs where nn_name is filled in...
- * XXX fix later
- */
if (strcmp(np->nn_name, "*") == 0) {
/* Star is special: No blanks, type, etc. */
snb->snb_name[0] = '*';
} else {
/* Normal name: pad with blanks, add type. */
- assert(NB_NAMELEN == 16);
snprintf(snb->snb_name, NB_NAMELEN,
"%-15.15s", np->nn_name);
snb->snb_name[15] = (char)np->nn_type;
}
-#endif
if (peer) {
/*LINTED*/
@@ -182,38 +155,20 @@ nb_encname_len(const uchar_t *str)
}
int
-nb_name_encode(struct nb_name *np, uchar_t *dst)
+nb_name_encode(struct mbdata *mbp, struct nb_name *nn)
{
- char *name;
- uchar_t *plen;
- uchar_t ch, *cp = dst;
- char *p, buf1[NB_NAMELEN+1];
+ char *plen;
+ uchar_t ch;
+ char *p, namebuf[NB_NAMELEN+1];
int i, lblen;
- /*
- * XXX: I'd rather see this part moved into
- * callers of this function, leaving just
- * the pure NB encoding here. -GWR
- */
- name = np->nn_name;
- if (name[0] == '*') {
- /* Star is special: No blanks, type, etc. */
- bzero(buf1, NB_NAMELEN);
- buf1[0] = '*';
- } else {
- /* Normal name: pad with blanks, add type. */
- assert(NB_NAMELEN == 16);
- snprintf(buf1, NB_NAMELEN,
- "%-15.15s", name);
- buf1[15] = (char)np->nn_type;
- }
- name = buf1;
+ bcopy(nn->nn_name, namebuf, NB_NAMELEN);
+ namebuf[NB_NAMELEN-1] = (char)nn->nn_type;
+ namebuf[NB_NAMELEN] = '\0'; /* for debug */
/*
* Do the NetBIOS "first-level encoding" here.
- * (RFC1002 explains this wierdness...)
- * See similar code in kernel nsmb module:
- * uts/common/fs/smbclnt/netsmb/smb_trantcp.c
+ * (RFC1002 explains this weirdness...)
*
* Here is what we marshall:
* uint8_t NAME_LENGTH (always 32)
@@ -223,13 +178,13 @@ nb_name_encode(struct nb_name *np, uchar_t *dst)
*/
/* NAME_LENGTH */
- *cp++ = (2 * NB_NAMELEN);
+ mb_put_uint8(mbp, (2 * NB_NAMELEN));
/* ENCODED_NAME */
for (i = 0; i < NB_NAMELEN; i++) {
- ch = name[i];
- *cp++ = 'A' + ((ch >> 4) & 0xF);
- *cp++ = 'A' + ((ch) & 0xF);
+ ch = namebuf[i];
+ mb_put_uint8(mbp, 'A' + ((ch >> 4) & 0xF));
+ mb_put_uint8(mbp, 'A' + ((ch) & 0xF));
}
/*
@@ -241,32 +196,38 @@ nb_name_encode(struct nb_name *np, uchar_t *dst)
* start of each string. This keeps a pointer
* to the location and fills it in after the
* length of the string is determined.
+ *
+ * One string of length zero terminates.
+ * With no scope string, the zero-length
+ * string is the only thing there.
*/
-#if NOT_DEFINED /* XXX: not yet */
- if (np->nn_scope) {
- plen = cp++;
- *plen = 0; /* fill in later */
- lblen = 0;
- for (p = np->nn_scope; ; p++) {
- if (*p == '.' || *p == 0) {
- *plen = lblen;
- if (*p == 0)
- break;
- plen = cp++;
- *plen = 0;
- lblen = 0;
- } else {
- if (lblen < NB_MAXLABLEN) {
- *cp++ = *p;
- lblen++;
- }
+ if (nn->nn_scope == NULL) {
+ mb_put_uint8(mbp, 0);
+ return (0);
+ }
+
+ mb_fit(mbp, 1, &plen);
+ *plen = 0; /* will update below */
+ lblen = 0;
+ for (p = nn->nn_scope; ; p++) {
+ if (*p == '\0') {
+ *plen = lblen;
+ if (lblen)
+ mb_put_uint8(mbp, 0);
+ break;
+ }
+ if (*p == '.') {
+ *plen = lblen;
+ mb_fit(mbp, 1, &plen);
+ *plen = 0;
+ lblen = 0;
+ } else {
+ if (lblen < NB_MAXLABLEN) {
+ mb_put_uint8(mbp, *p);
+ lblen++;
}
}
- } else
-#endif /* XXX: not yet */
- {
- *cp++ = 0;
}
- return (cp - dst);
+ return (0);
}
diff --git a/usr/src/lib/libsmbfs/smb/nb_net.c b/usr/src/lib/libsmbfs/smb/nb_net.c
index 29109c3093..ec0db6cffc 100644
--- a/usr/src/lib/libsmbfs/smb/nb_net.c
+++ b/usr/src/lib/libsmbfs/smb/nb_net.c
@@ -38,13 +38,14 @@
#include <sys/sockio.h>
#include <net/if.h>
#include <ctype.h>
-#include <netdb.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <stdio.h>
#include <unistd.h>
+#include <netdb.h>
+#include <nss_dbdefs.h>
#include <err.h>
@@ -59,32 +60,14 @@
*/
int
-nb_getlocalname(char *name, size_t maxlen)
-{
- char buf[1024], *cp;
-
- if (gethostname(buf, sizeof (buf)) != 0)
- return (errno);
- cp = strchr(buf, '.');
- if (cp)
- *cp = 0;
- strlcpy(name, buf, maxlen);
- return (0);
-}
-
-int
-nb_resolvehost_in(const char *name, struct sockaddr **dest)
+nb_resolvehost_in(const char *name, struct in_addr *ia)
{
- struct hostent *h;
- struct sockaddr_in *sinp;
- in_addr_t addr;
- struct in_addr in;
- int len;
- char **p;
+ char he_buf[NSS_BUFLEN_HOSTS];
+ struct hostent he, *h;
+ int err;
-
- h = gethostbyname(name);
- if (!h) {
+ h = gethostbyname_r(name, &he, he_buf, sizeof (he_buf), &err);
+ if (h == NULL) {
#ifdef DEBUG
warnx("can't get server address `%s': ", name);
#endif
@@ -102,19 +85,7 @@ nb_resolvehost_in(const char *name, struct sockaddr **dest)
#endif
return (EAFNOSUPPORT);
}
- len = sizeof (struct sockaddr_in);
- sinp = malloc(len);
- if (sinp == NULL)
- return (ENOMEM);
- bzero(sinp, len);
- /*
- * There is no sin_len in sockaddr_in structure on Solaris.
- * sinp->sin_len = len;
- */
- sinp->sin_family = h->h_addrtype;
- memcpy(&sinp->sin_addr.s_addr, *h->h_addr_list,\
- sizeof (sinp->sin_addr.s_addr));
- sinp->sin_port = htons(SMB_TCP_PORT);
- *dest = (struct sockaddr *)sinp;
+
+ memcpy(ia, h->h_addr, sizeof (*ia));
return (0);
}
diff --git a/usr/src/lib/libsmbfs/smb/nb_ssn.c b/usr/src/lib/libsmbfs/smb/nb_ssn.c
new file mode 100644
index 0000000000..bd53ce6fce
--- /dev/null
+++ b/usr/src/lib/libsmbfs/smb/nb_ssn.c
@@ -0,0 +1,330 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * NetBIOS session service functions
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <libintl.h>
+#include <xti.h>
+#include <assert.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/poll.h>
+
+#include <netsmb/netbios.h>
+#include <netsmb/smb_lib.h>
+#include <netsmb/nb_lib.h>
+#include <netsmb/mchain.h>
+
+#include "private.h"
+#include "charsets.h"
+
+static int nb_ssn_send(struct smb_ctx *, struct mbdata *, int, int);
+static int nb_ssn_recv(struct smb_ctx *, struct mbdata *, int *, int *);
+static int nb_ssn_pollin(struct smb_ctx *, int);
+
+/*
+ * Send a data message.
+ */
+int
+smb_ssn_send(struct smb_ctx *ctx, struct mbdata *mbp)
+{
+ return (nb_ssn_send(ctx, mbp, 0, mbp->mb_count));
+}
+
+/*
+ * Send a NetBIOS message, after
+ * prepending the 4-byte header.
+ */
+static int
+nb_ssn_send(struct smb_ctx *ctx, struct mbdata *mbp,
+ int mtype, int mlen)
+{
+ mbuf_t *m = mbp->mb_top;
+ int fd = ctx->ct_tran_fd;
+ int err, flags;
+ uint32_t hdr, hdrbuf;
+
+ if (m == NULL)
+ return (EINVAL);
+
+ /*
+ * Prepend the NetBIOS header.
+ * Using mbuf trickery to ensure it's
+ * not separated from the body.
+ */
+ hdr = (mtype << 24) | mlen;
+ hdrbuf = htonl(hdr);
+ m->m_data -= 4;
+ m->m_len += 4;
+ bcopy(&hdrbuf, m->m_data, 4);
+
+ /* Send it. */
+ while (m) {
+ flags = (m->m_next) ? T_MORE : T_PUSH;
+ if (t_snd(fd, m->m_data, m->m_len, flags) < 0) {
+ if (t_errno == TSYSERR)
+ err = errno;
+ else
+ err = EPROTO;
+ DPRINT("t_snd: t_errno %d, err %d", t_errno, err);
+ return (err);
+ }
+ m = m->m_next;
+ }
+ return (0);
+}
+
+/*
+ * Receive a data message. Discard anything else.
+ * Caller must deal with EAGAIN, EINTR.
+ */
+int
+smb_ssn_recv(struct smb_ctx *ctx, struct mbdata *mbp)
+{
+ int err, mtype, mlen;
+ err = nb_ssn_recv(ctx, mbp, &mtype, &mlen);
+ if (err)
+ return (err);
+ if (mtype != NB_SSN_MESSAGE) {
+ DPRINT("discard type 0x%x", mtype);
+ mb_done(mbp);
+ return (EAGAIN);
+ }
+ if (mlen == 0) {
+ DPRINT("zero length");
+ mb_done(mbp);
+ return (EAGAIN);
+ }
+
+ return (0);
+}
+
+/*
+ * Receive a NetBIOS message, any type.
+ * Give caller type and length.
+ */
+static int
+nb_ssn_recv(struct smb_ctx *ctx, struct mbdata *mb,
+ int *mtype, int *mlen)
+{
+ char *buf;
+ uint32_t hdr, hdrbuf;
+ int cnt, len, err, moreflag;
+ int fd = ctx->ct_tran_fd;
+ int tmo = smb_recv_timeout * 1000;
+
+ /*
+ * Start by getting the header
+ * (four bytes)
+ */
+ if ((err = nb_ssn_pollin(ctx, tmo)) != 0) {
+ DPRINT("pollin err %d", err);
+ return (err);
+ }
+ moreflag = 0;
+ cnt = t_rcv(fd, &hdrbuf, sizeof (hdrbuf), &moreflag);
+ if (cnt < 0) {
+ err = get_xti_err(fd);
+ DPRINT("t_errno %d err %d", t_errno, err);
+ return (err);
+ }
+
+ if (cnt != sizeof (hdrbuf)) {
+ DPRINT("hdr cnt %d", cnt);
+ return (EPROTO);
+ }
+
+ /*
+ * Decode the header, get the length.
+ */
+ hdr = ntohl(hdrbuf);
+ *mtype = (hdr >> 24) & 0xff;
+ *mlen = hdr & 0xffffff;
+
+ if (mlen == 0)
+ return (0);
+
+ /*
+ * Get a message buffer, read the payload
+ */
+ if ((err = mb_init(mb, *mlen)) != 0)
+ return (err);
+ buf = mb->mb_top->m_data;
+ len = *mlen;
+ while (len > 0) {
+ if (!moreflag) {
+ if ((err = nb_ssn_pollin(ctx, tmo)) != 0) {
+ DPRINT("pollin err %d", err);
+ return (err);
+ }
+ }
+
+ moreflag = 0;
+ cnt = t_rcv(fd, buf, len, &moreflag);
+ if (cnt < 0) {
+ err = get_xti_err(fd);
+ DPRINT("t_errno %d err %d", t_errno, err);
+ return (err);
+ }
+ buf += cnt;
+ len -= cnt;
+ }
+ mb->mb_top->m_len = *mlen;
+ mb->mb_count = *mlen;
+
+ return (0);
+}
+
+int
+get_xti_err(int fd)
+{
+ int look;
+ if (t_errno == TSYSERR)
+ return (errno);
+
+ if (t_errno == TLOOK) {
+ look = t_look(fd);
+ switch (look) {
+ case T_DISCONNECT:
+ (void) t_rcvdis(fd, NULL);
+ (void) t_snddis(fd, NULL);
+ return (ECONNRESET);
+ case T_ORDREL:
+ /* Received orderly release indication */
+ (void) t_rcvrel(fd);
+ /* Send orderly release indicator */
+ (void) t_sndrel(fd);
+ return (ECONNRESET);
+ }
+ }
+ return (EPROTO);
+}
+
+/*
+ * Wait for data we can receive.
+ * Timeout is mSec., as for poll(2)
+ */
+static int
+nb_ssn_pollin(struct smb_ctx *ctx, int tmo)
+{
+ struct pollfd pfd[1];
+ int cnt, err;
+
+ pfd[0].fd = ctx->ct_tran_fd;
+ pfd[0].events = POLLIN | POLLPRI;
+ pfd[0].revents = 0;
+ cnt = poll(pfd, 1, tmo);
+ switch (cnt) {
+ case 0:
+ err = ETIME;
+ break;
+ case -1:
+ err = errno;
+ break;
+ default:
+ err = 0;
+ break;
+ }
+ return (err);
+}
+
+/*
+ * Send a NetBIOS session request and
+ * wait for the response.
+ */
+int
+nb_ssn_request(struct smb_ctx *ctx, char *srvname)
+{
+ struct mbdata req, res;
+ struct nb_name lcl, srv;
+ int err, mtype, mlen;
+ char *ucwks;
+
+ bzero(&req, sizeof (req));
+ bzero(&res, sizeof (res));
+
+ if ((err = mb_init(&req, M_MINSIZE)) != 0)
+ goto errout;
+
+ ucwks = utf8_str_toupper(ctx->ct_locname);
+ if (ucwks == NULL) {
+ err = ENOMEM;
+ goto errout;
+ }
+
+ /* Local NB name. */
+ snprintf(lcl.nn_name, NB_NAMELEN, "%-15.15s", ucwks);
+ lcl.nn_type = NBT_WKSTA;
+ lcl.nn_scope = ctx->ct_nb->nb_scope;
+
+ /* Server NB name */
+ snprintf(srv.nn_name, NB_NAMELEN, "%-15.15s", srvname);
+ srv.nn_type = NBT_SERVER;
+ srv.nn_scope = ctx->ct_nb->nb_scope;
+
+ /*
+ * Build the request. Header is prepended later.
+ */
+ if ((err = nb_name_encode(&req, &srv)) != 0)
+ goto errout;
+ if ((err = nb_name_encode(&req, &lcl)) != 0)
+ goto errout;
+
+ /*
+ * Send it, wait for the reply.
+ */
+ err = nb_ssn_send(ctx, &req, NB_SSN_REQUEST, req.mb_count);
+ if (err) {
+ DPRINT("send, err %d", err);
+ goto errout;
+ }
+ err = nb_ssn_recv(ctx, &res, &mtype, &mlen);
+ if (err) {
+ DPRINT("recv, err %d", err);
+ goto errout;
+ }
+
+ if (mtype != NB_SSN_POSRESP) {
+ DPRINT("recv, mtype 0x%x", mtype);
+ err = ECONNREFUSED;
+ goto errout;
+ }
+
+ return (0);
+
+errout:
+ mb_done(&res);
+ mb_done(&req);
+ return (err);
+}
diff --git a/usr/src/lib/libsmbfs/smb/nbns_rq.c b/usr/src/lib/libsmbfs/smb/nbns_rq.c
index 773466b7fa..d0e85209e7 100644
--- a/usr/src/lib/libsmbfs/smb/nbns_rq.c
+++ b/usr/src/lib/libsmbfs/smb/nbns_rq.c
@@ -55,6 +55,7 @@
#include <netsmb/nb_lib.h>
#include <netsmb/mchain.h>
+#include "charsets.h"
#include "private.h"
/*
@@ -96,7 +97,80 @@ static int nbns_rq_getrr(struct nbns_rq *rqp, struct nbns_rr *rrp);
static int nbns_rq_prepare(struct nbns_rq *rqp);
static int nbns_rq(struct nbns_rq *rqp);
-static struct nb_ifdesc *nb_iflist = NULL;
+/*
+ * Call NetBIOS name lookup and return a result in the
+ * same form as getaddrinfo(3) returns. Return code is
+ * zero or one of the EAI_xxx codes like getaddrinfo.
+ */
+int
+nbns_getaddrinfo(const char *name, struct nb_ctx *nbc, struct addrinfo **res)
+{
+ struct addrinfo *nai = NULL;
+ struct sockaddr *sap = NULL;
+ char *ucname = NULL;
+ int err;
+
+ /*
+ * Try NetBIOS name lookup.
+ */
+ if (strlen(name) >= NB_NAMELEN) {
+ err = EAI_OVERFLOW;
+ goto out;
+ }
+ ucname = utf8_str_toupper(name);
+ if (ucname == NULL)
+ goto nomem;
+
+ /* Note: this returns an NBERROR value. */
+ err = nbns_resolvename(ucname, nbc, &sap);
+ if (err) {
+ if (smb_verbose)
+ smb_error(dgettext(TEXT_DOMAIN,
+ "nbns_resolvename: %s"),
+ err, name);
+ err = EAI_NODATA;
+ goto out;
+ }
+ /* Note: sap allocated */
+
+ /*
+ * Build the addrinfo struct to return.
+ */
+ nai = malloc(sizeof (*nai));
+ if (nai == NULL)
+ goto nomem;
+ bzero(nai, sizeof (*nai));
+
+ nai->ai_flags = AI_CANONNAME;
+ nai->ai_family = sap->sa_family;
+ nai->ai_socktype = SOCK_STREAM;
+ nai->ai_canonname = ucname;
+ ucname = NULL;
+
+ /*
+ * The type of this is really sockaddr_in,
+ * but is returned in the generic form.
+ * See nbns_resolvename.
+ */
+ nai->ai_addrlen = sizeof (struct sockaddr_in);
+ nai->ai_addr = sap;
+
+ *res = nai;
+ return (0);
+
+nomem:
+ err = EAI_MEMORY;
+out:
+ if (nai != NULL)
+ free(nai);
+ if (sap)
+ free(sap);
+ if (ucname)
+ free(ucname);
+ *res = NULL;
+
+ return (err);
+}
int
nbns_resolvename(const char *name, struct nb_ctx *ctx, struct sockaddr **adpp)
@@ -107,7 +181,7 @@ nbns_resolvename(const char *name, struct nb_ctx *ctx, struct sockaddr **adpp)
struct sockaddr_in *dest;
int error, rdrcount, len;
- if (strlen(name) > NB_NAMELEN)
+ if (strlen(name) >= NB_NAMELEN)
return (NBERROR(NBERR_NAMETOOLONG));
error = nbns_rq_create(NBNS_OPCODE_QUERY, ctx, &rqp);
if (error)
@@ -169,12 +243,11 @@ nbns_resolvename(const char *name, struct nb_ctx *ctx, struct sockaddr **adpp)
return (ENOMEM);
bzero(dest, len);
/*
- * Solaris sockaddr_in doesn't have this field.
+ * Solaris sockaddr_in doesn't a sin_len field.
* dest->sin_len = len;
*/
- dest->sin_family = AF_INET;
+ dest->sin_family = AF_NETBIOS; /* nb_lib.h */
bcopy(rr.rr_data + 2, &dest->sin_addr.s_addr, 4);
- dest->sin_port = htons(SMB_TCP_PORT);
*adpp = (struct sockaddr *)dest;
ctx->nb_lastns = rqp->nr_sender;
break;
@@ -183,20 +256,12 @@ nbns_resolvename(const char *name, struct nb_ctx *ctx, struct sockaddr **adpp)
return (error);
}
-static char *
-smb_optstrncpy(char *d, char *s, unsigned maxlen)
-{
- if (d && s) {
- strncpy(d, s, maxlen);
- d[maxlen] = (char)0;
- }
- return (d);
-}
-
-
+/*
+ * NB: system, workgroup are both NB_NAMELEN
+ */
int
-nbns_getnodestatus(struct sockaddr *targethost,
- struct nb_ctx *ctx, char *system, char *workgroup)
+nbns_getnodestatus(struct nb_ctx *ctx,
+ struct in_addr *targethost, char *system, char *workgroup)
{
struct nbns_rq *rqp;
struct nbns_rr rr;
@@ -204,12 +269,9 @@ nbns_getnodestatus(struct sockaddr *targethost,
struct nbns_nr *nrp;
char nrtype;
char *cp, *retname = NULL;
- struct sockaddr_in *dest;
unsigned char nrcount;
- int error, rdrcount, i, foundserver = 0, foundgroup = 0;
+ int error, i, foundserver = 0, foundgroup = 0;
- if (targethost->sa_family != AF_INET)
- return (EINVAL);
error = nbns_rq_create(NBNS_OPCODE_QUERY, ctx, &rqp);
if (error)
return (error);
@@ -224,10 +286,7 @@ nbns_getnodestatus(struct sockaddr *targethost,
rqp->nr_qdcount = 1;
rqp->nr_maxretry = 2;
- /* LINTED */
- dest = (struct sockaddr_in *)targethost;
- rqp->nr_dest = dest->sin_addr;
-
+ rqp->nr_dest = *targethost;
error = nbns_rq_prepare(rqp);
if (error) {
nbns_rq_done(rqp);
@@ -266,12 +325,14 @@ nbns_getnodestatus(struct sockaddr *targethost,
*cp = (char)0;
}
nrp->ns_flags = ntohs(nrp->ns_flags);
+ DPRINT(" %s[%02x] Flags 0x%x",
+ nrp->ns_name, nrtype, nrp->ns_flags);
if (nrp->ns_flags & NBNS_GROUPFLG) {
if (!foundgroup ||
(foundgroup != NBT_WKSTA+1 &&
nrtype == NBT_WKSTA)) {
- smb_optstrncpy(workgroup, nrp->ns_name,
- SMB_MAXUSERNAMELEN);
+ strlcpy(workgroup, nrp->ns_name,
+ NB_NAMELEN);
foundgroup = nrtype+1;
}
} else {
@@ -281,14 +342,17 @@ nbns_getnodestatus(struct sockaddr *targethost,
*/
retname = nrp->ns_name;
}
- if (nrtype == NBT_SERVER) {
- smb_optstrncpy(system, nrp->ns_name,
- SMB_MAXSRVNAMELEN);
+ /*
+ * Keep the first NBT_SERVER name.
+ */
+ if (nrtype == NBT_SERVER && foundserver == 0) {
+ strlcpy(system, nrp->ns_name,
+ NB_NAMELEN);
foundserver = 1;
}
}
if (!foundserver)
- smb_optstrncpy(system, retname, SMB_MAXSRVNAMELEN);
+ strlcpy(system, retname, NB_NAMELEN);
ctx->nb_lastns = rqp->nr_sender;
out:
@@ -370,8 +434,7 @@ nbns_rq_prepare(struct nbns_rq *rqp)
struct nb_ctx *ctx = rqp->nr_nbd;
struct mbdata *mbp = &rqp->nr_rq;
uint16_t ofr; /* opcode, flags, rcode */
- uchar_t *cp;
- int len, error;
+ int error;
/*
* Replacing with one argument.
@@ -396,11 +459,7 @@ nbns_rq_prepare(struct nbns_rq *rqp)
if (rqp->nr_qdcount) {
if (rqp->nr_qdcount > 1)
return (EINVAL);
- len = nb_name_len(rqp->nr_qdname);
- error = mb_fit(mbp, len, (char **)&cp);
- if (error)
- return (error);
- nb_name_encode(rqp->nr_qdname, cp);
+ nb_name_encode(mbp, rqp->nr_qdname);
mb_put_uint16be(mbp, rqp->nr_qdtype);
mb_put_uint16be(mbp, rqp->nr_qdclass);
}
@@ -485,7 +544,7 @@ nbns_rq_send(struct nbns_rq *rqp, in_addr_t ina)
bzero(&dest, sizeof (dest));
dest.sin_family = AF_INET;
- dest.sin_port = htons(NBNS_UDP_PORT);
+ dest.sin_port = htons(IPPORT_NETBIOS_NS);
dest.sin_addr.s_addr = ina;
if (ina == INADDR_BROADCAST) {
@@ -520,7 +579,6 @@ nbns_rq(struct nbns_rq *rqp)
struct nb_ctx *ctx = rqp->nr_nbd;
struct mbdata *mbp = &rqp->nr_rq;
uint16_t ofr, rpid;
- uint8_t nmflags;
int error, tries, maxretry;
error = nbns_rq_opensocket(rqp);
@@ -599,6 +657,8 @@ do_recv:
return (NBERROR(NBERR_INVALIDRESPONSE));
break;
}
+ if (tries == maxretry)
+ return (NBERROR(NBERR_HOSTNOTFOUND));
mb_get_uint16be(mbp, &ofr);
rqp->nr_rpnmflags = (ofr >> 4) & 0x7F;
diff --git a/usr/src/lib/libsmbfs/smb/negprot.c b/usr/src/lib/libsmbfs/smb/negprot.c
new file mode 100644
index 0000000000..6c1649f1bb
--- /dev/null
+++ b/usr/src/lib/libsmbfs/smb/negprot.c
@@ -0,0 +1,428 @@
+/*
+ * Copyright (c) 2000-2001 Boris Popov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Boris Popov.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * SMB Negotiate Protocol, and related.
+ * Copied from the driver: smb_smb.c
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <strings.h>
+#include <netdb.h>
+#include <libintl.h>
+#include <xti.h>
+#include <assert.h>
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/byteorder.h>
+#include <sys/socket.h>
+#include <sys/fcntl.h>
+
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+
+#include <netsmb/smb.h>
+#include <netsmb/smb_lib.h>
+#include <netsmb/netbios.h>
+#include <netsmb/nb_lib.h>
+#include <netsmb/smb_dev.h>
+
+#include "charsets.h"
+#include "private.h"
+
+/*
+ * SMB dialects that we know about.
+ */
+struct smb_dialect {
+ int d_id;
+ const char *d_name;
+};
+static struct smb_dialect smb_dialects[] = {
+ {SMB_DIALECT_CORE, "PC NETWORK PROGRAM 1.0"},
+ {SMB_DIALECT_LANMAN1_0, "LANMAN1.0"},
+ {SMB_DIALECT_LANMAN2_0, "LM1.2X002"},
+ {SMB_DIALECT_LANMAN2_1, "LANMAN2.1"},
+ {SMB_DIALECT_NTLM0_12, "NT LM 0.12"},
+ {-1, NULL}
+};
+
+#define SMB_DIALECT_MAX \
+ (sizeof (smb_dialects) / sizeof (struct smb_dialect) - 2)
+
+/*
+ * SMB Negotiate Protocol
+ * Based on code from the driver: smb_smb.c
+ *
+ * If using Extended Security, oblob (output)
+ * will hold the initial security "hint".
+ */
+int
+smb_negprot(struct smb_ctx *ctx, struct mbdata *oblob)
+{
+ struct smb_sopt *sv = &ctx->ct_sopt;
+ struct smb_iods *is = &ctx->ct_iods;
+ struct smb_rq *rqp;
+ struct mbdata *mbp;
+ struct smb_dialect *dp;
+ int err, len;
+ uint8_t wc, stime[8], eklen;
+ uint16_t dindex, bc;
+ int will_sign = 0;
+
+ /*
+ * Initialize: vc_hflags and vc_hflags2.
+ * Note: ctx->ct_hflags* are copied into the
+ * (per request) rqp->rq_hflags* by smb_rq_init,
+ * so changing them after that call will not
+ * affect THIS request.
+ */
+ ctx->ct_hflags = SMB_FLAGS_CASELESS;
+ ctx->ct_hflags2 = (SMB_FLAGS2_ERR_STATUS |
+ SMB_FLAGS2_KNOWS_LONG_NAMES);
+
+ /*
+ * Sould we offer extended security?
+ * We'll turn this back off below if
+ * the server doesn't support it.
+ */
+ if (ctx->ct_vopt & SMBVOPT_EXT_SEC)
+ ctx->ct_hflags2 |= SMB_FLAGS2_EXT_SEC;
+
+ /*
+ * The initial UID needs to be zero,
+ * or Windows XP says "bad user".
+ * The initial TID is all ones, but
+ * we don't use it or store it here
+ * because the driver handles that.
+ */
+ is->is_smbuid = 0;
+
+ /*
+ * In case we're reconnecting,
+ * free previous stuff.
+ */
+ ctx->ct_mac_seqno = 0;
+ if (ctx->ct_mackey != NULL) {
+ free(ctx->ct_mackey);
+ ctx->ct_mackey = NULL;
+ ctx->ct_mackeylen = 0;
+ }
+
+ sv = &ctx->ct_sopt;
+ bzero(sv, sizeof (struct smb_sopt));
+
+ err = smb_rq_init(ctx, SMB_COM_NEGOTIATE, &rqp);
+ if (err)
+ return (err);
+
+ /*
+ * Build the SMB request.
+ */
+ mbp = &rqp->rq_rq;
+ mb_put_uint8(mbp, 0); /* word count */
+ smb_rq_bstart(rqp);
+ for (dp = smb_dialects; dp->d_id != -1; dp++) {
+ mb_put_uint8(mbp, SMB_DT_DIALECT);
+ mb_put_astring(mbp, dp->d_name);
+ }
+ smb_rq_bend(rqp);
+
+ /*
+ * This does the OTW call
+ */
+ err = smb_rq_internal(ctx, rqp);
+ if (err) {
+ DPRINT("call failed, err %d", err);
+ goto errout;
+ }
+ if (rqp->rq_status != 0) {
+ DPRINT("nt status 0x%x", rqp->rq_status);
+ err = EBADRPC;
+ goto errout;
+ }
+
+ /*
+ * Decode the response
+ *
+ * Comments to right show names as described in
+ * The Microsoft SMB Protocol spec. [MS-SMB]
+ * section 2.2.3
+ */
+ mbp = &rqp->rq_rp;
+ (void) mb_get_uint8(mbp, &wc);
+ err = mb_get_uint16le(mbp, &dindex);
+ if (err || dindex > SMB_DIALECT_MAX) {
+ DPRINT("err %d dindex %d", err, (int)dindex);
+ goto errout;
+ }
+ dp = smb_dialects + dindex;
+ sv->sv_proto = dp->d_id;
+ DPRINT("Dialect %s", dp->d_name);
+ if (dp->d_id < SMB_DIALECT_NTLM0_12) {
+ /* XXX: User-visible warning too? */
+ DPRINT("old dialect %s", dp->d_name);
+ goto errout;
+ }
+ if (wc != 17) {
+ DPRINT("bad wc %d", (int)wc);
+ goto errout;
+ }
+ mb_get_uint8(mbp, &sv->sv_sm); /* SecurityMode */
+ mb_get_uint16le(mbp, &sv->sv_maxmux); /* MaxMpxCount */
+ mb_get_uint16le(mbp, &sv->sv_maxvcs); /* MaxCountVCs */
+ mb_get_uint32le(mbp, &sv->sv_maxtx); /* MaxBufferSize */
+ mb_get_uint32le(mbp, &sv->sv_maxraw); /* MaxRawSize */
+ mb_get_uint32le(mbp, &sv->sv_skey); /* SessionKey */
+ mb_get_uint32le(mbp, &sv->sv_caps); /* Capabilities */
+ mb_get_mem(mbp, (char *)stime, 8); /* SystemTime(s) */
+ mb_get_uint16le(mbp, (uint16_t *)&sv->sv_tz);
+ mb_get_uint8(mbp, &eklen); /* EncryptionKeyLength */
+ err = mb_get_uint16le(mbp, &bc); /* ByteCount */
+ if (err)
+ goto errout;
+
+ /* BEGIN CSTYLED */
+ /*
+ * Will we do SMB signing? Or block the connection?
+ * The table below describes this logic. References:
+ * [Windows Server Protocols: MS-SMB, sec. 3.2.4.2.3]
+ * http://msdn.microsoft.com/en-us/library/cc212511.aspx
+ * http://msdn.microsoft.com/en-us/library/cc212929.aspx
+ *
+ * Srv/Cli | Required | Enabled | If Required | Disabled
+ * ------------+----------+------------+-------------+-----------
+ * Required | Signed | Signed | Signed | Blocked [1]
+ * ------------+----------+------------+-------------+-----------
+ * Enabled | Signed | Signed | Not Signed | Not Signed
+ * ------------+----------+------------+-------------+-----------
+ * If Required | Signed | Not Signed | Not Signed | Not Signed
+ * ------------+----------+------------+-------------+-----------
+ * Disabled | Blocked | Not Signed | Not Signed | Not Signed
+ *
+ * [1] Like Windows 2003 and later, we don't really implement
+ * the "Disabled" setting. Instead we implement "If Required",
+ * so we always sign if the server requires signing.
+ */
+ /* END CSTYLED */
+
+ if (sv->sv_sm & SMB_SM_SIGS_REQUIRE) {
+ /*
+ * Server requires signing. We will sign,
+ * even if local setting is "disabled".
+ */
+ will_sign = 1;
+ } else if (sv->sv_sm & SMB_SM_SIGS) {
+ /*
+ * Server enables signing (client's option).
+ * If enabled locally, do signing.
+ */
+ if (ctx->ct_vopt & SMBVOPT_SIGNING_ENABLED)
+ will_sign = 1;
+ /* else not signing. */
+ } else {
+ /*
+ * Server does not support signing.
+ * If we "require" it, bail now.
+ */
+ if (ctx->ct_vopt & SMBVOPT_SIGNING_REQUIRED) {
+ DPRINT("Client requires signing "
+ "but server has it disabled.");
+ err = EBADRPC;
+ goto errout;
+ }
+ }
+
+ if (will_sign) {
+ ctx->ct_vcflags |= SMBV_WILL_SIGN;
+ }
+ DPRINT("Security signatures: %d", will_sign);
+
+ if (sv->sv_caps & SMB_CAP_UNICODE) {
+ ctx->ct_vcflags |= SMBV_UNICODE;
+ ctx->ct_hflags2 |= SMB_FLAGS2_UNICODE;
+
+ }
+ if ((sv->sv_caps & SMB_CAP_STATUS32) == 0) {
+ /*
+ * They don't do NT error codes.
+ *
+ * If we send requests with
+ * SMB_FLAGS2_ERR_STATUS set in
+ * Flags2, Windows 98, at least,
+ * appears to send replies with that
+ * bit set even though it sends back
+ * DOS error codes. (They probably
+ * just use the request header as
+ * a template for the reply header,
+ * and don't bother clearing that bit.)
+ *
+ * Therefore, we clear that bit in
+ * our vc_hflags2 field.
+ */
+ ctx->ct_hflags2 &= ~SMB_FLAGS2_ERR_STATUS;
+ }
+ if (dp->d_id == SMB_DIALECT_NTLM0_12 &&
+ sv->sv_maxtx < 4096 &&
+ (sv->sv_caps & SMB_CAP_NT_SMBS) == 0) {
+ ctx->ct_vcflags |= SMBV_WIN95;
+ DPRINT("Win95 detected");
+ }
+
+ /*
+ * The rest of the message varies depending on
+ * whether we've negotiated "extended security".
+ *
+ * With extended security, we have:
+ * Server_GUID (length 16)
+ * Security_BLOB
+ * Otherwise we have:
+ * EncryptionKey (length is eklen)
+ * PrimaryDomain
+ */
+ if (sv->sv_caps & SMB_CAP_EXT_SECURITY) {
+ struct mbuf *m;
+ DPRINT("Ext.Security: yes");
+
+ /*
+ * Skip the server GUID.
+ */
+ err = mb_get_mem(mbp, NULL, SMB_GUIDLEN);
+ if (err)
+ goto errout;
+ /*
+ * Remainder is the security blob.
+ * Note: eklen "must be ignored" [MS-SMB]
+ */
+ len = (int)bc - SMB_GUIDLEN;
+ if (len < 0)
+ goto errout;
+
+ /*
+ * Get the (optional) SPNEGO "hint".
+ */
+ err = mb_get_mbuf(mbp, len, &m);
+ if (err)
+ goto errout;
+ mb_initm(oblob, m);
+ oblob->mb_count = len;
+ } else {
+ DPRINT("Ext.Security: no");
+ ctx->ct_hflags2 &= ~SMB_FLAGS2_EXT_SEC;
+
+ /*
+ * Save the "Encryption Key" (the challenge).
+ *
+ * Sanity check: make sure the sec. blob length
+ * isn't bigger than the byte count.
+ */
+ if (bc < eklen || eklen < NTLM_CHAL_SZ) {
+ err = EBADRPC;
+ goto errout;
+ }
+ err = mb_get_mem(mbp, (char *)ctx->ct_ntlm_chal, NTLM_CHAL_SZ);
+ /*
+ * Server domain follows (ignored)
+ * Note: NOT aligned(2) - unusual!
+ */
+ }
+
+ smb_rq_done(rqp);
+
+ /*
+ * A few sanity checks on what we received,
+ * becuse we will send these in ssnsetup.
+ *
+ * Maximum outstanding requests (we care),
+ * and Max. VCs (we only use one). Also,
+ * MaxBufferSize lower limit per spec.
+ */
+ if (sv->sv_maxmux < 1)
+ sv->sv_maxmux = 1;
+ if (sv->sv_maxvcs < 1)
+ sv->sv_maxvcs = 1;
+ if (sv->sv_maxtx < 1024)
+ sv->sv_maxtx = 1024;
+
+ /*
+ * Maximum transfer size.
+ * Sanity checks:
+ *
+ * Let's be conservative about an upper limit here.
+ * Win2k uses 16644 (and others) so 32k should be a
+ * reasonable sanity limit for this value.
+ *
+ * Note that this limit does NOT affect READX/WRITEX
+ * with CAP_LARGE_..., which we nearly always use.
+ */
+ is->is_txmax = sv->sv_maxtx;
+ if (is->is_txmax > 0x8000)
+ is->is_txmax = 0x8000;
+
+ /*
+ * Max read/write sizes, WITHOUT overhead.
+ * This is just the payload size, so we must
+ * leave room for the SMB headers, etc.
+ * This is just the ct_txmax value, but
+ * reduced and rounded down. Tricky bit:
+ *
+ * Servers typically give us a value that's
+ * some nice "round" number, i.e 0x4000 plus
+ * some overhead, i.e. Win2k: 16644==0x4104
+ * Subtract for the SMB header (32) and the
+ * SMB command word and byte vectors (34?),
+ * then round down to a 512 byte multiple.
+ */
+ len = is->is_txmax - 68;
+ len &= 0xFE00;
+ /* XXX: Not sure yet which of these to keep. */
+ is->is_rwmax = len;
+ is->is_rxmax = len;
+ is->is_wxmax = len;
+
+ return (0);
+
+errout:
+ smb_rq_done(rqp);
+ if (err == 0)
+ err = EBADRPC;
+ return (err);
+}
diff --git a/usr/src/lib/libsmbfs/smb/netshareenum.c b/usr/src/lib/libsmbfs/smb/netshareenum.c
index 2d7f9c2c3a..2ee1fd7792 100644
--- a/usr/src/lib/libsmbfs/smb/netshareenum.c
+++ b/usr/src/lib/libsmbfs/smb/netshareenum.c
@@ -37,7 +37,7 @@
/* END CSTYLED */
/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -47,6 +47,7 @@
#include <errno.h>
#include <netsmb/mchain.h>
+#include <netsmb/smb.h>
#include <netsmb/smb_lib.h>
#include <netsmb/smb_rap.h>
#include <netsmb/smb_netshareenum.h>
@@ -275,8 +276,6 @@ smb_rap_NetShareEnum(struct smb_ctx *ctx, int sLevel, void *pbBuffer,
struct smb_rap *rap;
long lval = -1;
int error;
- char *pass;
- int i;
error = smb_rap_create(0, "WrLeh", "B13BWz", &rap);
if (error)
@@ -375,5 +374,6 @@ smb_netshareenum(struct smb_ctx *ctx, int *entriesp, int *totalp,
* XXX - do so only if it failed because we couldn't open
* the pipe?
*/
- return (rap_netshareenum(ctx, entriesp, totalp, entry_listp));
+ error = rap_netshareenum(ctx, entriesp, totalp, entry_listp);
+ return (error);
}
diff --git a/usr/src/lib/libsmbfs/smb/newvc.c b/usr/src/lib/libsmbfs/smb/newvc.c
new file mode 100644
index 0000000000..0153ffd437
--- /dev/null
+++ b/usr/src/lib/libsmbfs/smb/newvc.c
@@ -0,0 +1,118 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Create a new VC given a list of addresses.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <libintl.h>
+#include <xti.h>
+#include <assert.h>
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/byteorder.h>
+#include <sys/socket.h>
+#include <sys/fcntl.h>
+
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+
+#include <netsmb/smb.h>
+#include <netsmb/smb_lib.h>
+#include <netsmb/netbios.h>
+#include <netsmb/nb_lib.h>
+#include <netsmb/smb_dev.h>
+
+#include "charsets.h"
+#include "private.h"
+
+/*
+ * Ask the IOD to create a VC with this IP address.
+ */
+static int
+newvc(struct smb_ctx *ctx, struct addrinfo *ai)
+{
+ smbioc_ossn_t *ssn = &ctx->ct_ssn;
+
+ /*
+ * Copy the passed address into ssn_srvaddr,
+ * but first sanity-check lengths. Also,
+ * zero it first to avoid trailing junk.
+ */
+ if (ai->ai_addrlen > sizeof (ssn->ssn_srvaddr))
+ return (EINVAL);
+ bzero(&ssn->ssn_srvaddr, sizeof (ssn->ssn_srvaddr));
+ bcopy(ai->ai_addr, &ssn->ssn_srvaddr, ai->ai_addrlen);
+
+ return (smb_iod_cl_newvc(ctx));
+}
+
+/*
+ * Setup a new VC via the IOD.
+ * Similar to findvc.c
+ */
+int
+smb_ctx_newvc(struct smb_ctx *ctx)
+{
+ struct addrinfo *ai;
+ int err = ENOENT;
+
+ /* Should already have the address list. */
+ if ((ctx->ct_flags & SMBCF_RESOLVED) == 0)
+ return (EINVAL);
+
+ for (ai = ctx->ct_addrinfo; ai; ai = ai->ai_next) {
+
+ switch (ai->ai_family) {
+
+ case AF_INET:
+ case AF_INET6:
+ case AF_NETBIOS:
+ err = newvc(ctx, ai);
+ break;
+
+ default:
+ DPRINT("skipped family %d", ai->ai_family);
+ break;
+ }
+
+ if (err == 0) {
+ ctx->ct_flags |= SMBCF_SSNACTIVE;
+ return (0);
+ }
+ }
+
+ return (err);
+}
diff --git a/usr/src/lib/libsmbfs/smb/nls.c b/usr/src/lib/libsmbfs/smb/nls.c
index 03fe1bec13..e5b6719716 100644
--- a/usr/src/lib/libsmbfs/smb/nls.c
+++ b/usr/src/lib/libsmbfs/smb/nls.c
@@ -32,8 +32,6 @@
* $Id: nls.c,v 1.10 2004/12/13 00:25:22 lindak Exp $
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <sys/types.h>
#include <ctype.h>
#include <errno.h>
@@ -52,8 +50,8 @@ typedef void *iconv_t;
static size_t(*my_iconv)(iconv_t, const char **, size_t *, char **, size_t *);
-u_char nls_lower[256];
-u_char nls_upper[256];
+uchar_t nls_lower[256];
+uchar_t nls_upper[256];
static iconv_t nls_toext, nls_toloc;
static int iconv_loaded;
@@ -71,13 +69,14 @@ nls_setlocale(const char *name)
nls_lower[i] = tolower(i);
nls_upper[i] = toupper(i);
}
- return 0;
+ return (0);
}
+/*ARGSUSED*/
int
nls_setrecode(const char *local, const char *external)
{
- return ENOENT;
+ return (ENOENT);
}
char *
@@ -87,15 +86,15 @@ nls_str_toloc(char *dst, const char *src)
size_t inlen, outlen;
if (!iconv_loaded)
- return strcpy(dst, src);
+ return (strcpy(dst, src));
if (nls_toloc == (iconv_t)0)
- return strcpy(dst, src);
+ return (strcpy(dst, src));
inlen = outlen = strlen(src);
my_iconv(nls_toloc, NULL, NULL, &p, &outlen);
my_iconv(nls_toloc, &src, &inlen, &p, &outlen);
*p = 0;
- return dst;
+ return (dst);
}
char *
@@ -105,15 +104,15 @@ nls_str_toext(char *dst, const char *src)
size_t inlen, outlen;
if (!iconv_loaded)
- return strcpy(dst, src);
+ return (strcpy(dst, src));
if (nls_toext == (iconv_t)0)
- return strcpy(dst, src);
+ return (strcpy(dst, src));
inlen = outlen = strlen(src);
my_iconv(nls_toext, NULL, NULL, &p, &outlen);
my_iconv(nls_toext, &src, &inlen, &p, &outlen);
*p = 0;
- return dst;
+ return (dst);
}
void *
@@ -124,17 +123,17 @@ nls_mem_toloc(void *dst, const void *src, int size)
size_t inlen, outlen;
if (!iconv_loaded)
- return memcpy(dst, src, size);
+ return (memcpy(dst, src, size));
if (size == 0)
- return NULL;
+ return (NULL);
if (nls_toloc == (iconv_t)0)
- return memcpy(dst, src, size);
+ return (memcpy(dst, src, size));
inlen = outlen = size;
my_iconv(nls_toloc, NULL, NULL, &p, &outlen);
my_iconv(nls_toloc, &s, &inlen, &p, &outlen);
- return dst;
+ return (dst);
}
void *
@@ -145,15 +144,15 @@ nls_mem_toext(void *dst, const void *src, int size)
size_t inlen, outlen;
if (size == 0)
- return NULL;
+ return (NULL);
if (!iconv_loaded || nls_toext == (iconv_t)0)
- return memcpy(dst, src, size);
+ return (memcpy(dst, src, size));
inlen = outlen = size;
my_iconv(nls_toext, NULL, NULL, &p, &outlen);
my_iconv(nls_toext, &s, &inlen, &p, &outlen);
- return dst;
+ return (dst);
}
char *
@@ -164,7 +163,7 @@ nls_str_upper(char *dst, const char *src)
while (*src)
*dst++ = toupper(*src++);
*dst = 0;
- return p;
+ return (p);
}
char *
@@ -175,5 +174,5 @@ nls_str_lower(char *dst, const char *src)
while (*src)
*dst++ = tolower(*src++);
*dst = 0;
- return p;
+ return (p);
}
diff --git a/usr/src/lib/libsmbfs/smb/ntlm.c b/usr/src/lib/libsmbfs/smb/ntlm.c
new file mode 100644
index 0000000000..8119e62b65
--- /dev/null
+++ b/usr/src/lib/libsmbfs/smb/ntlm.c
@@ -0,0 +1,584 @@
+/*
+ * Copyright (c) 2000-2001, Boris Popov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Boris Popov.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: smb_crypt.c,v 1.13 2005/01/26 23:50:50 lindak Exp $
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * NTLM support functions
+ *
+ * Some code from the driver: smb_smb.c, smb_crypt.c
+ */
+
+#include <sys/errno.h>
+#include <sys/types.h>
+#include <sys/md4.h>
+#include <sys/md5.h>
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <strings.h>
+
+#include <netsmb/smb_lib.h>
+
+#include "private.h"
+#include "charsets.h"
+#include "smb_crypt.h"
+#include "ntlm.h"
+
+
+/*
+ * ntlm_compute_lm_hash
+ *
+ * Compute an LM hash given a password
+ *
+ * Output:
+ * hash: 16-byte "LanMan" (LM) hash.
+ * Inputs:
+ * ucpw: User's password, upper-case UTF-8 string.
+ *
+ * Source: Implementing CIFS (Chris Hertel)
+ *
+ * P14 = UCPW padded to 14-bytes, or truncated (as needed)
+ * result = Encrypt(Key=P14, Data=MagicString)
+ */
+int
+ntlm_compute_lm_hash(uchar_t *hash, const char *pass)
+{
+ static const uchar_t M8[8] = "KGS!@#$%";
+ uchar_t P14[14 + 1];
+ int err;
+ char *ucpw;
+
+ /* First, convert the p/w to upper case. */
+ ucpw = utf8_str_toupper(pass);
+ if (ucpw == NULL)
+ return (ENOMEM);
+
+ /* Pad or truncate the upper-case P/W as needed. */
+ bzero(P14, sizeof (P14));
+ (void) strncpy((char *)P14, ucpw, 14);
+
+ /* Compute the hash. */
+ err = smb_encrypt_DES(hash, NTLM_HASH_SZ,
+ P14, 14, M8, 8);
+
+ free(ucpw);
+ return (err);
+}
+
+/*
+ * ntlm_compute_nt_hash
+ *
+ * Compute an NT hash given a password in UTF-8.
+ *
+ * Output:
+ * hash: 16-byte "NT" hash.
+ * Inputs:
+ * upw: User's password, mixed-case UCS-2LE.
+ * pwlen: Size (in bytes) of upw
+ */
+int
+ntlm_compute_nt_hash(uchar_t *hash, const char *pass)
+{
+ MD4_CTX ctx;
+ uint16_t *unipw = NULL;
+ int pwsz;
+
+ /* First, convert the password to unicode. */
+ unipw = convert_utf8_to_leunicode(pass);
+ if (unipw == NULL)
+ return (ENOMEM);
+ pwsz = unicode_strlen(unipw) << 1;
+
+ /* Compute the hash. */
+ MD4Init(&ctx);
+ MD4Update(&ctx, unipw, pwsz);
+ MD4Final(hash, &ctx);
+
+ free(unipw);
+ return (0);
+}
+
+/*
+ * ntlm_v1_response
+ *
+ * Create an LM response from the given LM hash and challenge,
+ * or an NTLM repsonse from a given NTLM hash and challenge.
+ * Both response types are 24 bytes (NTLM_V1_RESP_SZ)
+ */
+static int
+ntlm_v1_response(uchar_t *resp,
+ const uchar_t *hash,
+ const uchar_t *chal, int clen)
+{
+ uchar_t S21[21];
+ int err;
+
+ /*
+ * 14-byte LM Hash should be padded with 5 nul bytes to create
+ * a 21-byte string to be used in producing LM response
+ */
+ bzero(&S21, sizeof (S21));
+ bcopy(hash, S21, NTLM_HASH_SZ);
+
+ /* padded LM Hash -> LM Response */
+ err = smb_encrypt_DES(resp, NTLM_V1_RESP_SZ,
+ S21, 21, chal, clen);
+ return (err);
+}
+
+/*
+ * Calculate an NTLMv1 session key (16 bytes).
+ */
+static void
+ntlm_v1_session_key(uchar_t *ssn_key, const uchar_t *nt_hash)
+{
+ MD4_CTX md4;
+
+ MD4Init(&md4);
+ MD4Update(&md4, nt_hash, NTLM_HASH_SZ);
+ MD4Final(ssn_key, &md4);
+}
+
+/*
+ * Compute both the LM(v1) response and the NTLM(v1) response,
+ * and put them in the mbdata chains passed. This allocates
+ * mbuf chains in the output args, which the caller frees.
+ */
+int
+ntlm_put_v1_responses(struct smb_ctx *ctx,
+ struct mbdata *lm_mbp, struct mbdata *nt_mbp)
+{
+ uchar_t *lmresp, *ntresp;
+ int err;
+
+ /* Get mbuf chain for the LM response. */
+ if ((err = mb_init(lm_mbp, NTLM_V1_RESP_SZ)) != 0)
+ return (err);
+
+ /* Get mbuf chain for the NT response. */
+ if ((err = mb_init(nt_mbp, NTLM_V1_RESP_SZ)) != 0)
+ return (err);
+
+ /*
+ * Compute the LM response, derived
+ * from the challenge and the ASCII
+ * password (if authflags allow).
+ */
+ mb_fit(lm_mbp, NTLM_V1_RESP_SZ, (char **)&lmresp);
+ bzero(lmresp, NTLM_V1_RESP_SZ);
+ if (ctx->ct_authflags & SMB_AT_LM1) {
+ /* They asked to send the LM hash too. */
+ err = ntlm_v1_response(lmresp, ctx->ct_lmhash,
+ ctx->ct_ntlm_chal, NTLM_CHAL_SZ);
+ if (err)
+ return (err);
+ }
+
+ /*
+ * Compute the NTLM response, derived from
+ * the challenge and the NT hash.
+ */
+ mb_fit(nt_mbp, NTLM_V1_RESP_SZ, (char **)&ntresp);
+ bzero(ntresp, NTLM_V1_RESP_SZ);
+ err = ntlm_v1_response(ntresp, ctx->ct_nthash,
+ ctx->ct_ntlm_chal, NTLM_CHAL_SZ);
+
+ /*
+ * Compute the session key
+ */
+ ntlm_v1_session_key(ctx->ct_ssn_key, ctx->ct_nthash);
+
+ return (err);
+}
+
+/*
+ * A variation on HMAC-MD5 known as HMACT64 is used by Windows systems.
+ * The HMACT64() function is the same as the HMAC-MD5() except that
+ * it truncates the input key to 64 bytes rather than hashing it down
+ * to 16 bytes using the MD5() function.
+ *
+ * Output: digest (16-bytes)
+ */
+static void
+HMACT64(uchar_t *digest,
+ const uchar_t *key, size_t key_len,
+ const uchar_t *data, size_t data_len)
+{
+ MD5_CTX context;
+ uchar_t k_ipad[64]; /* inner padding - key XORd with ipad */
+ uchar_t k_opad[64]; /* outer padding - key XORd with opad */
+ int i;
+
+ /* if key is longer than 64 bytes use only the first 64 bytes */
+ if (key_len > 64)
+ key_len = 64;
+
+ /*
+ * The HMAC-MD5 (and HMACT64) transform looks like:
+ *
+ * MD5(K XOR opad, MD5(K XOR ipad, data))
+ *
+ * where K is an n byte key
+ * ipad is the byte 0x36 repeated 64 times
+ * opad is the byte 0x5c repeated 64 times
+ * and data is the data being protected.
+ */
+
+ /* start out by storing key in pads */
+ bzero(k_ipad, sizeof (k_ipad));
+ bzero(k_opad, sizeof (k_opad));
+ bcopy(key, k_ipad, key_len);
+ bcopy(key, k_opad, key_len);
+
+ /* XOR key with ipad and opad values */
+ for (i = 0; i < 64; i++) {
+ k_ipad[i] ^= 0x36;
+ k_opad[i] ^= 0x5c;
+ }
+
+ /*
+ * perform inner MD5
+ */
+ MD5Init(&context); /* init context for 1st pass */
+ MD5Update(&context, k_ipad, 64); /* start with inner pad */
+ MD5Update(&context, data, data_len); /* then data of datagram */
+ MD5Final(digest, &context); /* finish up 1st pass */
+
+ /*
+ * perform outer MD5
+ */
+ MD5Init(&context); /* init context for 2nd pass */
+ MD5Update(&context, k_opad, 64); /* start with outer pad */
+ MD5Update(&context, digest, 16); /* then results of 1st hash */
+ MD5Final(digest, &context); /* finish up 2nd pass */
+}
+
+
+/*
+ * Compute an NTLMv2 hash given the NTLMv1 hash, the user name,
+ * and the destination (machine or domain name).
+ *
+ * Output:
+ * v2hash: 16-byte NTLMv2 hash.
+ * Inputs:
+ * v1hash: 16-byte NTLMv1 hash.
+ * user: User name, UPPER-case UTF-8 string.
+ * destination: Domain or server, MIXED-case UTF-8 string.
+ */
+static int
+ntlm_v2_hash(uchar_t *v2hash, const uchar_t *v1hash,
+ const char *user, const char *destination)
+{
+ int ulen, dlen;
+ size_t ucs2len;
+ uint16_t *ucs2data = NULL;
+ char *utf8data = NULL;
+ int err = ENOMEM;
+
+ /*
+ * v2hash = HMACT64(v1hash, 16, concat(upcase(user), dest))
+ * where "dest" is the domain or server name ("target name")
+ * Note: user name is converted to upper-case by the caller.
+ */
+
+ /* utf8data = concat(user, dest) */
+ ulen = strlen(user);
+ dlen = strlen(destination);
+ utf8data = malloc(ulen + dlen + 1);
+ if (utf8data == NULL)
+ goto out;
+ bcopy(user, utf8data, ulen);
+ bcopy(destination, utf8data + ulen, dlen + 1);
+
+ /* Convert to UCS-2LE */
+ ucs2data = convert_utf8_to_leunicode(utf8data);
+ if (ucs2data == NULL)
+ goto out;
+ ucs2len = 2 * unicode_strlen(ucs2data);
+
+ HMACT64(v2hash, v1hash, NTLM_HASH_SZ,
+ (uchar_t *)ucs2data, ucs2len);
+ err = 0;
+out:
+ if (ucs2data)
+ free(ucs2data);
+ if (utf8data)
+ free(utf8data);
+ return (err);
+}
+
+/*
+ * Compute a partial LMv2 or NTLMv2 response (first 16-bytes).
+ * The full response is composed by the caller by
+ * appending the client_data to the returned hash.
+ *
+ * Output:
+ * rhash: _partial_ LMv2/NTLMv2 response (first 16-bytes)
+ * Inputs:
+ * v2hash: 16-byte NTLMv2 hash.
+ * C8: Challenge from server (8 bytes)
+ * client_data: client nonce (for LMv2) or the
+ * "blob" from ntlm_build_target_info (NTLMv2)
+ */
+static int
+ntlm_v2_resp_hash(uchar_t *rhash,
+ const uchar_t *v2hash, const uchar_t *C8,
+ const uchar_t *client_data, size_t cdlen)
+{
+ size_t dlen;
+ uchar_t *data = NULL;
+
+ /* data = concat(C8, client_data) */
+ dlen = 8 + cdlen;
+ data = malloc(dlen);
+ if (data == NULL)
+ return (ENOMEM);
+ bcopy(C8, data, 8);
+ bcopy(client_data, data + 8, cdlen);
+
+ HMACT64(rhash, v2hash, NTLM_HASH_SZ, data, dlen);
+
+ free(data);
+ return (0);
+}
+
+/*
+ * Calculate an NTLMv2 session key (16 bytes).
+ */
+static void
+ntlm_v2_session_key(uchar_t *ssn_key,
+ const uchar_t *v2hash,
+ const uchar_t *ntresp)
+{
+
+ /* session key uses only 1st 16 bytes of ntresp */
+ HMACT64(ssn_key, v2hash, NTLM_HASH_SZ, ntresp, NTLM_HASH_SZ);
+}
+
+
+/*
+ * Compute both the LMv2 response and the NTLMv2 response,
+ * and put them in the mbdata chains passed. This allocates
+ * mbuf chains in the output args, which the caller frees.
+ * Also computes the session key.
+ */
+int
+ntlm_put_v2_responses(struct smb_ctx *ctx, struct mbdata *ti_mbp,
+ struct mbdata *lm_mbp, struct mbdata *nt_mbp)
+{
+ uchar_t *lmresp, *ntresp;
+ int err;
+ char *ucdom = NULL; /* user's domain */
+ char *ucuser = NULL; /* account name */
+ uchar_t v2hash[NTLM_HASH_SZ];
+ struct mbuf *tim = ti_mbp->mb_top;
+
+ if ((err = mb_init(lm_mbp, M_MINSIZE)) != 0)
+ return (err);
+ if ((err = mb_init(nt_mbp, M_MINSIZE)) != 0)
+ return (err);
+
+ /*
+ * Convert the user name to upper-case, as
+ * that's what's used when computing LMv2
+ * and NTLMv2 responses. Also the domain.
+ */
+ ucdom = utf8_str_toupper(ctx->ct_domain);
+ ucuser = utf8_str_toupper(ctx->ct_user);
+ if (ucdom == NULL || ucuser == NULL) {
+ err = ENOMEM;
+ goto out;
+ }
+
+ /*
+ * Compute the NTLMv2 hash (see above)
+ * Needs upper-case user, domain.
+ */
+ err = ntlm_v2_hash(v2hash, ctx->ct_nthash, ucuser, ucdom);
+ if (err)
+ goto out;
+
+ /*
+ * Compute the LMv2 response, derived from
+ * the v2hash, the server challenge, and
+ * the client nonce (random bits).
+ *
+ * We compose it from two parts:
+ * 1: 16-byte response hash
+ * 2: Client nonce
+ */
+ lmresp = (uchar_t *)lm_mbp->mb_pos;
+ mb_put_mem(lm_mbp, NULL, NTLM_HASH_SZ);
+ err = ntlm_v2_resp_hash(lmresp,
+ v2hash, ctx->ct_ntlm_chal,
+ ctx->ct_clnonce, NTLM_CHAL_SZ);
+ if (err)
+ goto out;
+ mb_put_mem(lm_mbp, ctx->ct_clnonce, NTLM_CHAL_SZ);
+
+ /*
+ * Compute the NTLMv2 response, derived
+ * from the server challenge and the
+ * "target info." blob passed in.
+ *
+ * Again composed from two parts:
+ * 1: 16-byte response hash
+ * 2: "target info." blob
+ */
+ ntresp = (uchar_t *)nt_mbp->mb_pos;
+ mb_put_mem(nt_mbp, NULL, NTLM_HASH_SZ);
+ err = ntlm_v2_resp_hash(ntresp,
+ v2hash, ctx->ct_ntlm_chal,
+ (uchar_t *)tim->m_data, tim->m_len);
+ if (err)
+ goto out;
+ mb_put_mem(nt_mbp, tim->m_data, tim->m_len);
+
+ /*
+ * Compute the session key
+ */
+ ntlm_v2_session_key(ctx->ct_ssn_key, v2hash, ntresp);
+
+out:
+ if (err) {
+ mb_done(lm_mbp);
+ mb_done(nt_mbp);
+ }
+ free(ucdom);
+ free(ucuser);
+
+ return (err);
+}
+
+/*
+ * Helper for ntlm_build_target_info below.
+ * Put a name in the NTLMv2 "target info." blob.
+ */
+static void
+smb_put_blob_name(struct mbdata *mbp, char *name, int type)
+{
+ uint16_t *ucs = NULL;
+ int nlen;
+
+ if (name)
+ ucs = convert_utf8_to_leunicode(name);
+ if (ucs)
+ nlen = unicode_strlen(ucs);
+ else
+ nlen = 0;
+
+ nlen <<= 1; /* length in bytes, without null. */
+
+ mb_put_uint16le(mbp, type);
+ mb_put_uint16le(mbp, nlen);
+ mb_put_mem(mbp, (char *)ucs, nlen);
+
+ if (ucs)
+ free(ucs);
+}
+
+/*
+ * Build an NTLMv2 "target info." blob. When called from NTLMSSP,
+ * the list of names comes from the Type 2 message. Otherwise,
+ * we create the name list here.
+ */
+int
+ntlm_build_target_info(struct smb_ctx *ctx, struct mbuf *names,
+ struct mbdata *mbp)
+{
+ struct timeval now;
+ uint64_t nt_time;
+
+ char *ucdom = NULL; /* user's domain */
+ int err;
+
+ /* Get mbuf chain for the "target info". */
+ if ((err = mb_init(mbp, M_MINSIZE)) != 0)
+ return (err);
+
+ /*
+ * Construct the client nonce by getting
+ * some random data from /dev/urandom
+ */
+ err = smb_get_urandom(ctx->ct_clnonce, NTLM_CHAL_SZ);
+ if (err)
+ goto out;
+
+ /*
+ * Get the "NT time" for the target info header.
+ */
+ (void) gettimeofday(&now, 0);
+ smb_time_local2NT(&now, 0, &nt_time);
+
+ /*
+ * Build the "target info." block.
+ *
+ * Based on information at:
+ * http://davenport.sourceforge.net/ntlm.html#theNtlmv2Response
+ *
+ * First the fixed-size part.
+ */
+ mb_put_uint32le(mbp, 0x101); /* Blob signature */
+ mb_put_uint32le(mbp, 0); /* reserved */
+ mb_put_uint64le(mbp, nt_time); /* NT time stamp */
+ mb_put_mem(mbp, ctx->ct_clnonce, NTLM_CHAL_SZ);
+ mb_put_uint32le(mbp, 0); /* unknown */
+
+ /*
+ * Now put the list of names, either from the
+ * NTLMSSP Type 2 message or composed here.
+ */
+ if (names) {
+ err = mb_put_mem(mbp, names->m_data, names->m_len);
+ } else {
+ /* Get upper-case names. */
+ ucdom = utf8_str_toupper(ctx->ct_domain);
+ if (ucdom == NULL) {
+ err = ENOMEM;
+ goto out;
+ }
+ smb_put_blob_name(mbp, ucdom, NAMETYPE_DOMAIN_NB);
+ smb_put_blob_name(mbp, NULL, NAMETYPE_EOL);
+ /* OK, that's the whole "target info." blob! */
+ }
+ err = 0;
+
+out:
+ free(ucdom);
+ return (err);
+}
diff --git a/usr/src/lib/libsmbfs/smb/ntlm.h b/usr/src/lib/libsmbfs/smb/ntlm.h
new file mode 100644
index 0000000000..e8eae559e9
--- /dev/null
+++ b/usr/src/lib/libsmbfs/smb/ntlm.h
@@ -0,0 +1,65 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _NTLM_H
+#define _NTLM_H
+
+/*
+ * NTLM support functions
+ * See ntlm.c
+ */
+
+/*
+ * Size of all LM/NTLM hashes, challenge
+ * NTLM_HASH_SZ: 16 bytes (see smb_lib.h)
+ * NTLM_CHAL_SZ: 8 bytes (see smb_lib.h)
+ */
+#define NTLM_V1_RESP_SZ 24 /* response size */
+
+#define NAMETYPE_EOL 0x0000 /* end of list of names */
+#define NAMETYPE_MACHINE_NB 0x0001 /* NetBIOS machine name */
+#define NAMETYPE_DOMAIN_NB 0x0002 /* NetBIOS domain name */
+#define NAMETYPE_MACHINE_DNS 0x0003 /* DNS machine name */
+#define NAMETYPE_DOMAIN_DNS 0x0004 /* DNS (AD) domain name */
+
+int
+ntlm_compute_lm_hash(uchar_t *hash, const char *pw);
+
+int
+ntlm_compute_nt_hash(uchar_t *hash, const char *pw);
+
+int
+ntlm_build_target_info(struct smb_ctx *, struct mbuf *, struct mbdata *);
+
+int
+ntlm_put_v1_responses(struct smb_ctx *ctx,
+ struct mbdata *lm_mbp, struct mbdata *nt_mbp);
+
+int
+ntlm_put_v2_responses(struct smb_ctx *ctx, struct mbdata *ti_mbp,
+ struct mbdata *lm_mbp, struct mbdata *nt_mbp);
+
+#endif /* _NTLM_H */
diff --git a/usr/src/lib/libsmbfs/smb/ntlmssp.c b/usr/src/lib/libsmbfs/smb/ntlmssp.c
new file mode 100644
index 0000000000..3428fbca36
--- /dev/null
+++ b/usr/src/lib/libsmbfs/smb/ntlmssp.c
@@ -0,0 +1,634 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * NT Lan Manager Security Support Provider (NTLMSSP)
+ *
+ * Based on information from the "Davenport NTLM" page:
+ * http://davenport.sourceforge.net/ntlm.html
+ */
+
+
+#include <errno.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <strings.h>
+#include <netdb.h>
+#include <libintl.h>
+#include <xti.h>
+#include <assert.h>
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/byteorder.h>
+#include <sys/socket.h>
+#include <sys/fcntl.h>
+
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+
+#include <netsmb/smb.h>
+#include <netsmb/smb_lib.h>
+#include <netsmb/mchain.h>
+
+#include "private.h"
+#include "charsets.h"
+#include "spnego.h"
+#include "derparse.h"
+#include "ssp.h"
+#include "ntlm.h"
+#include "ntlmssp.h"
+
+typedef struct ntlmssp_state {
+ uint32_t ss_flags;
+ char *ss_target_name;
+ struct mbuf *ss_target_info;
+} ntlmssp_state_t;
+
+/*
+ * So called "security buffer".
+ * A lot like an RPC string.
+ */
+struct sec_buf {
+ uint16_t sb_length;
+ uint16_t sb_maxlen;
+ uint32_t sb_offset;
+};
+#define ID_SZ 8
+static const char ntlmssp_id[ID_SZ] = "NTLMSSP";
+
+/*
+ * Get a "security buffer" (header part)
+ */
+static int
+mb_get_sb_hdr(struct mbdata *mbp, struct sec_buf *sb)
+{
+ int err;
+
+ (void) mb_get_uint16le(mbp, &sb->sb_length);
+ (void) mb_get_uint16le(mbp, &sb->sb_maxlen);
+ err = mb_get_uint32le(mbp, &sb->sb_offset);
+
+ return (err);
+}
+
+/*
+ * Get a "security buffer" (data part), where
+ * the data is delivered as an mbuf.
+ */
+static int
+mb_get_sb_data(struct mbdata *mbp, struct sec_buf *sb, struct mbuf **mp)
+{
+ struct mbdata tmp_mb;
+ int err;
+
+ /*
+ * Setup tmp_mb to point to the start of the header.
+ * This is a dup ref - do NOT free it.
+ */
+ mb_initm(&tmp_mb, mbp->mb_top);
+
+ /* Skip data up to the offset. */
+ err = mb_get_mem(&tmp_mb, NULL, sb->sb_offset);
+ if (err)
+ return (err);
+
+ /* Get the data (as an mbuf). */
+ err = mb_get_mbuf(&tmp_mb, sb->sb_maxlen, mp);
+
+ return (err);
+}
+
+/*
+ * Put a "security buffer" (header part)
+ */
+static int
+mb_put_sb_hdr(struct mbdata *mbp, struct sec_buf *sb)
+{
+ int err;
+
+ (void) mb_put_uint16le(mbp, sb->sb_length);
+ (void) mb_put_uint16le(mbp, sb->sb_maxlen);
+ err = mb_put_uint32le(mbp, sb->sb_offset);
+
+ return (err);
+}
+
+/*
+ * Put a "security buffer" (data part), where
+ * the data is an mbuf. Note: consumes m.
+ */
+static int
+mb_put_sb_data(struct mbdata *mbp, struct sec_buf *sb, struct mbuf *m)
+{
+ int cnt0, err;
+
+ sb->sb_offset = cnt0 = mbp->mb_count;
+ err = mb_put_mbuf(mbp, m);
+ sb->sb_maxlen = sb->sb_length = mbp->mb_count - cnt0;
+
+ return (err);
+}
+
+/*
+ * Put a "security buffer" (data part), where
+ * the data is a string (OEM or unicode).
+ *
+ * The string is NOT null terminated.
+ */
+static int
+mb_put_sb_string(struct mbdata *mbp, struct sec_buf *sb,
+ const char *s, int unicode)
+{
+ int err, trim;
+ struct mbdata tmp_mb;
+
+ /*
+ * Put the string into a temp. mbuf,
+ * then chop off the null terminator
+ * before appending to caller's mbp.
+ */
+ err = mb_init(&tmp_mb, M_MINSIZE);
+ if (err)
+ return (err);
+ err = mb_put_dstring(&tmp_mb, s, unicode);
+ if (err)
+ return (err);
+
+ trim = (unicode) ? 2 : 1;
+ if (tmp_mb.mb_cur->m_len < trim)
+ return (EFAULT);
+ tmp_mb.mb_cur->m_len -= trim;
+
+ err = mb_put_sb_data(mbp, sb, tmp_mb.mb_top);
+ /*
+ * Note: tmp_mb.mb_top is consumed,
+ * so do NOT free it (no mb_done)
+ */
+ return (err);
+}
+
+/*
+ * Build a Type 1 message
+ *
+ * This message has a header section containing offsets to
+ * data later in the message. We use the common trick of
+ * building it in two parts and then concatenatening.
+ */
+int
+ntlmssp_put_type1(struct ssp_ctx *sp, struct mbdata *out_mb)
+{
+ struct type1hdr {
+ char h_id[ID_SZ];
+ uint32_t h_type;
+ uint32_t h_flags;
+ struct sec_buf h_cldom;
+ struct sec_buf h_wksta;
+ } hdr;
+ struct mbdata mb2; /* 2nd part */
+ int err;
+ struct smb_ctx *ctx = sp->smb_ctx;
+ ntlmssp_state_t *ssp_st = sp->sp_private;
+ char *ucdom = NULL;
+ char *ucwks = NULL;
+
+ if ((err = mb_init(&mb2, M_MINSIZE)) != 0)
+ return (err);
+ mb2.mb_count = sizeof (hdr);
+
+ /*
+ * Initialize the negotiation flags, and
+ * save what we sent. For reference:
+ * [MS-NLMP] spec. (also ntlmssp.h)
+ */
+ ssp_st->ss_flags =
+ NTLMSSP_REQUEST_TARGET |
+ NTLMSSP_NEGOTIATE_NTLM |
+ NTLMSSP_NEGOTIATE_TARGET_INFO |
+ NTLMSSP_NEGOTIATE_128 |
+ NTLMSSP_NEGOTIATE_56;
+
+ if (ctx->ct_hflags2 & SMB_FLAGS2_UNICODE)
+ ssp_st->ss_flags |= NTLMSSP_NEGOTIATE_UNICODE;
+ else
+ ssp_st->ss_flags |= NTLMSSP_NEGOTIATE_OEM;
+
+ if (ctx->ct_vcflags & SMBV_WILL_SIGN) {
+ ssp_st->ss_flags |= NTLMSSP_NEGOTIATE_ALWAYS_SIGN;
+ ctx->ct_hflags2 |= SMB_FLAGS2_SECURITY_SIGNATURE;
+ }
+
+ bcopy(ntlmssp_id, &hdr.h_id, ID_SZ);
+ hdr.h_type = 1; /* Type 1 */
+ hdr.h_flags = ssp_st->ss_flags;
+
+ /*
+ * Put the client domain, client name strings.
+ * These are always in OEM format, upper-case.
+ */
+ ucdom = utf8_str_toupper(ctx->ct_domain);
+ ucwks = utf8_str_toupper(ctx->ct_locname);
+ if (ucdom == NULL || ucwks == NULL) {
+ err = ENOMEM;
+ goto out;
+ }
+ err = mb_put_sb_string(&mb2, &hdr.h_cldom, ucdom, 0);
+ if (err)
+ goto out;
+ err = mb_put_sb_string(&mb2, &hdr.h_wksta, ucwks, 0);
+ if (err)
+ goto out;
+
+ /*
+ * Marshal the header (in LE order)
+ * then concatenate the 2nd part.
+ */
+ (void) mb_put_mem(out_mb, &hdr.h_id, ID_SZ);
+ (void) mb_put_uint32le(out_mb, hdr.h_type);
+ (void) mb_put_uint32le(out_mb, hdr.h_flags);
+ (void) mb_put_sb_hdr(out_mb, &hdr.h_cldom);
+ (void) mb_put_sb_hdr(out_mb, &hdr.h_wksta);
+
+ err = mb_put_mbuf(out_mb, mb2.mb_top);
+
+out:
+ free(ucdom);
+ free(ucwks);
+
+ return (err);
+}
+
+/*
+ * Parse a Type 2 message
+ */
+int
+ntlmssp_get_type2(struct ssp_ctx *sp, struct mbdata *in_mb)
+{
+ struct type2hdr {
+ char h_id[ID_SZ];
+ uint32_t h_type;
+ struct sec_buf h_target_name;
+ uint32_t h_flags;
+ uint8_t h_challenge[8];
+ uint32_t h_context[2]; /* optional */
+ struct sec_buf h_target_info; /* optional */
+ } hdr;
+ struct mbdata top_mb, tmp_mb;
+ struct mbuf *m;
+ int err, uc;
+ int min_hdr_sz = offsetof(struct type2hdr, h_context);
+ struct smb_ctx *ctx = sp->smb_ctx;
+ ntlmssp_state_t *ssp_st = sp->sp_private;
+ char *buf = NULL;
+
+ if (m_totlen(in_mb->mb_top) < min_hdr_sz) {
+ err = EBADRPC;
+ goto out;
+ }
+
+ /*
+ * Save the mbdata pointers before we consume anything.
+ * Careful to NOT free this (would be dup. free)
+ * We use this below to find data based on offsets
+ * from the start of the header.
+ */
+ top_mb = *in_mb;
+
+ /* Parse the fixed size header stuff. */
+ bzero(&hdr, sizeof (hdr));
+ (void) mb_get_mem(in_mb, &hdr.h_id, ID_SZ);
+ (void) mb_get_uint32le(in_mb, &hdr.h_type);
+ if (hdr.h_type != 2) {
+ err = EPROTO;
+ goto out;
+ }
+ (void) mb_get_sb_hdr(in_mb, &hdr.h_target_name);
+ (void) mb_get_uint32le(in_mb, &hdr.h_flags);
+ (void) mb_get_mem(in_mb, &hdr.h_challenge, NTLM_CHAL_SZ);
+
+ /*
+ * Save flags, challenge for later.
+ */
+ ssp_st->ss_flags = hdr.h_flags;
+ uc = hdr.h_flags & NTLMSSP_NEGOTIATE_UNICODE;
+ bcopy(&hdr.h_challenge, ctx->ct_ntlm_chal, NTLM_CHAL_SZ);
+
+ /*
+ * Now find out if the optional parts are there.
+ */
+ if ((m_totlen(top_mb.mb_top) > sizeof (hdr)) &&
+ (hdr.h_target_name.sb_offset >= sizeof (hdr))) {
+ (void) mb_get_uint32le(in_mb, &hdr.h_context[0]);
+ (void) mb_get_uint32le(in_mb, &hdr.h_context[1]);
+ (void) mb_get_sb_hdr(in_mb, &hdr.h_target_info);
+ }
+
+ /*
+ * Get the target name string. First get a copy of
+ * the data from the offset/length indicated in the
+ * security buffer header; then parse the string.
+ */
+ err = mb_get_sb_data(&top_mb, &hdr.h_target_name, &m);
+ if (err)
+ goto out;
+ mb_initm(&tmp_mb, m);
+ err = mb_get_string(&tmp_mb, &ssp_st->ss_target_name, uc);
+ mb_done(&tmp_mb);
+
+ /*
+ * Get the target info blob, if present.
+ */
+ if (hdr.h_target_info.sb_offset >= sizeof (hdr)) {
+ err = mb_get_sb_data(&top_mb, &hdr.h_target_info,
+ &ssp_st->ss_target_info);
+ }
+
+out:
+ if (buf != NULL)
+ free(buf);
+
+ return (err);
+}
+
+/*
+ * Build a Type 3 message
+ *
+ * This message has a header section containing offsets to
+ * data later in the message. We use the common trick of
+ * building it in two parts and then concatenatening.
+ */
+int
+ntlmssp_put_type3(struct ssp_ctx *sp, struct mbdata *out_mb)
+{
+ struct type3hdr {
+ char h_id[ID_SZ];
+ uint32_t h_type;
+ struct sec_buf h_lm_resp;
+ struct sec_buf h_nt_resp;
+ struct sec_buf h_domain;
+ struct sec_buf h_user;
+ struct sec_buf h_wksta;
+ } hdr;
+ struct mbdata lm_mbc, nt_mbc, ti_mbc;
+ struct mbdata mb2; /* 2nd part */
+ int err, uc;
+ char *ucdom = NULL; /* user's domain */
+ char *ucuser = NULL; /* user name */
+ char *ucwksta = NULL; /* workstation */
+ struct smb_ctx *ctx = sp->smb_ctx;
+ ntlmssp_state_t *ssp_st = sp->sp_private;
+
+ bzero(&lm_mbc, sizeof (lm_mbc));
+ bzero(&nt_mbc, sizeof (nt_mbc));
+ bzero(&ti_mbc, sizeof (ti_mbc));
+ bzero(&mb2, sizeof (mb2));
+
+ /*
+ * Convert the user name to upper-case, as that's what's
+ * used when computing LMv2 and NTLMv2 responses. Also
+ * domain, workstation
+ */
+ ucdom = utf8_str_toupper(ctx->ct_domain);
+ ucuser = utf8_str_toupper(ctx->ct_user);
+ ucwksta = utf8_str_toupper(ctx->ct_locname);
+ if (ucdom == NULL || ucuser == NULL || ucwksta == NULL) {
+ err = ENOMEM;
+ goto out;
+ }
+
+ if ((err = mb_init(&mb2, M_MINSIZE)) != 0)
+ goto out;
+ mb2.mb_count = sizeof (hdr);
+ uc = ssp_st->ss_flags & NTLMSSP_NEGOTIATE_UNICODE;
+
+ bcopy(ntlmssp_id, &hdr.h_id, ID_SZ);
+ hdr.h_type = 3; /* Type 3 */
+
+ /*
+ * Put the LMv2,NTLMv2 responses, or
+ * possibly LM, NTLM (v1) responses.
+ */
+ if (ctx->ct_authflags & SMB_AT_NTLM2) {
+ /* Build the NTLMv2 "target info" blob. */
+ err = ntlm_build_target_info(ctx,
+ ssp_st->ss_target_info, &ti_mbc);
+ if (err)
+ goto out;
+ err = ntlm_put_v2_responses(ctx, &ti_mbc,
+ &lm_mbc, &nt_mbc);
+ } else {
+ err = ntlm_put_v1_responses(ctx,
+ &lm_mbc, &nt_mbc);
+ }
+ if (err)
+ goto out;
+
+ err = mb_put_sb_data(&mb2, &hdr.h_lm_resp, lm_mbc.mb_top);
+ lm_mbc.mb_top = NULL; /* consumed */
+ if (err)
+ goto out;
+ err = mb_put_sb_data(&mb2, &hdr.h_nt_resp, nt_mbc.mb_top);
+ nt_mbc.mb_top = NULL; /* consumed */
+ if (err)
+ goto out;
+
+ /*
+ * Put the "target" (domain), user, workstation
+ */
+ err = mb_put_sb_string(&mb2, &hdr.h_domain, ucdom, uc);
+ if (err)
+ goto out;
+ err = mb_put_sb_string(&mb2, &hdr.h_user, ucuser, uc);
+ if (err)
+ goto out;
+ err = mb_put_sb_string(&mb2, &hdr.h_wksta, ucwksta, uc);
+ if (err)
+ goto out;
+
+ /*
+ * Marshal the header (in LE order)
+ * then concatenate the 2nd part.
+ */
+ (void) mb_put_mem(out_mb, &hdr.h_id, ID_SZ);
+ (void) mb_put_uint32le(out_mb, hdr.h_type);
+
+ (void) mb_put_sb_hdr(out_mb, &hdr.h_lm_resp);
+ (void) mb_put_sb_hdr(out_mb, &hdr.h_nt_resp);
+
+ (void) mb_put_sb_hdr(out_mb, &hdr.h_domain);
+ (void) mb_put_sb_hdr(out_mb, &hdr.h_user);
+ (void) mb_put_sb_hdr(out_mb, &hdr.h_wksta);
+
+ err = mb_put_mbuf(out_mb, mb2.mb_top);
+ mb2.mb_top = NULL; /* consumed */
+
+out:
+ free(ucdom);
+ free(ucuser);
+ free(ucwksta);
+
+ mb_done(&mb2);
+ mb_done(&lm_mbc);
+ mb_done(&nt_mbc);
+
+ return (err);
+}
+
+/*
+ * ntlmssp_final
+ *
+ * Called after successful authentication.
+ * Setup the MAC key for signing.
+ */
+int
+ntlmssp_final(struct ssp_ctx *sp)
+{
+ struct smb_ctx *ctx = sp->smb_ctx;
+ int err = 0;
+
+ /*
+ * MAC_key is just the session key, but
+ * Only on the first successful auth.
+ */
+ if ((ctx->ct_hflags2 & SMB_FLAGS2_SECURITY_SIGNATURE) &&
+ (ctx->ct_mackey == NULL)) {
+ ctx->ct_mackeylen = NTLM_HASH_SZ;
+ ctx->ct_mackey = malloc(ctx->ct_mackeylen);
+ if (ctx->ct_mackey == NULL) {
+ ctx->ct_mackeylen = 0;
+ err = ENOMEM;
+ goto out;
+ }
+ memcpy(ctx->ct_mackey, ctx->ct_ssn_key, NTLM_HASH_SZ);
+ /*
+ * Apparently, the server used seq. no. zero
+ * for our previous message, so next is two.
+ */
+ ctx->ct_mac_seqno = 2;
+ }
+
+out:
+ return (err);
+}
+
+/*
+ * ntlmssp_next_token
+ *
+ * See ssp.c: ssp_ctx_next_token
+ */
+int
+ntlmssp_next_token(struct ssp_ctx *sp, struct mbdata *in_mb,
+ struct mbdata *out_mb)
+{
+ int err;
+
+ if (out_mb == NULL) {
+ /* final call on successful auth. */
+ err = ntlmssp_final(sp);
+ goto out;
+ }
+
+ /* Will build an ouptut token. */
+ err = mb_init(out_mb, M_MINSIZE);
+ if (err)
+ goto out;
+
+ /*
+ * When called with in_mb == NULL, it means
+ * this is the first call for this session,
+ * so put a Type 1 (initialize) token.
+ */
+ if (in_mb == NULL) {
+ err = ntlmssp_put_type1(sp, out_mb);
+ goto out;
+ }
+
+ /*
+ * This is not the first call, so
+ * parse the response token we received.
+ * It should be a Type 2 (challenge).
+ * Then put a Type 3 (authenticate)
+ */
+ err = ntlmssp_get_type2(sp, in_mb);
+ if (err)
+ goto out;
+
+ err = ntlmssp_put_type3(sp, out_mb);
+
+out:
+ if (err)
+ DPRINT("ret: %d", err);
+ return (err);
+}
+
+/*
+ * ntlmssp_ctx_destroy
+ *
+ * Destroy mechanism-specific data.
+ */
+void
+ntlmssp_destroy(struct ssp_ctx *sp)
+{
+ ntlmssp_state_t *ssp_st;
+
+ ssp_st = sp->sp_private;
+ if (ssp_st != NULL) {
+ sp->sp_private = NULL;
+ free(ssp_st->ss_target_name);
+ m_freem(ssp_st->ss_target_info);
+ free(ssp_st);
+ }
+}
+
+/*
+ * ntlmssp_init_clnt
+ *
+ * Initialize a new NTLMSSP client context.
+ */
+int
+ntlmssp_init_client(struct ssp_ctx *sp)
+{
+ ntlmssp_state_t *ssp_st;
+
+ if ((sp->smb_ctx->ct_authflags &
+ (SMB_AT_NTLM2 | SMB_AT_NTLM1)) == 0) {
+ DPRINT("No NTLM authflags");
+ return (ENOTSUP);
+ }
+
+ ssp_st = calloc(1, sizeof (*ssp_st));
+ if (ssp_st == NULL)
+ return (ENOMEM);
+
+ sp->sp_nexttok = ntlmssp_next_token;
+ sp->sp_destroy = ntlmssp_destroy;
+ sp->sp_private = ssp_st;
+
+ return (0);
+}
diff --git a/usr/src/lib/libsmbfs/smb/ntlmssp.h b/usr/src/lib/libsmbfs/smb/ntlmssp.h
new file mode 100644
index 0000000000..591b1ab088
--- /dev/null
+++ b/usr/src/lib/libsmbfs/smb/ntlmssp.h
@@ -0,0 +1,76 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _NTLMSSP_H
+#define _NTLMSSP_H
+
+/*
+ * NT LanMan Security Support Package (NTLMSSP)
+ * Negotiation flags, etc.
+ *
+ * Reference: [MS-NLMP] NT LAN Manager (NTLM)
+ * Authentication Protocol Specification
+ * http://msdn.microsoft.com/en-us/library/cc236621(PROT.10).aspx
+ */
+
+/*
+ * NTLMSSP Negotiate Flags
+ * [MS-NLMP] sec. 2.2.2.5
+ */
+#define NTLMSSP_NEGOTIATE_UNICODE 0x00000001
+#define NTLMSSP_NEGOTIATE_OEM 0x00000002
+#define NTLMSSP_REQUEST_TARGET 0x00000004
+/* reserved 0x00000008 */
+#define NTLMSSP_NEGOTIATE_SIGN 0x00000010
+#define NTLMSSP_NEGOTIATE_SEAL 0x00000020
+#define NTLMSSP_NEGOTIATE_DATAGRAM 0x00000040
+#define NTLMSSP_NEGOTIATE_LM_KEY 0x00000080
+/* reserved 0x00000100 */
+#define NTLMSSP_NEGOTIATE_NTLM 0x00000200
+#define NTLMSSP_NEGOTIATE_NT_ONLY 0x00000400
+/* old anonymous_session (ignored by servers) 0x00000800 */
+#define NTLMSSP_NEGOTIATE_OEM_DOMAIN_SUPPLIED 0x00001000
+#define NTLMSSP_NEGOTIATE_OEM_WORKSTATION_SUPPLIED 0x00002000
+/* reserved 0x00004000 */
+#define NTLMSSP_NEGOTIATE_ALWAYS_SIGN 0x00008000
+#define NTLMSSP_TARGET_TYPE_DOMAIN 0x00010000
+#define NTLMSSP_TARGET_TYPE_SERVER 0x00020000
+#define NTLMSSP_TARGET_TYPE_SHARE 0x00040000
+#define NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY 0x00080000
+#define NTLMSSP_NEGOTIATE_IDENTIFY 0x00100000
+/* reserved 0x00200000 */
+#define NTLMSSP_REQUEST_NON_NT_SESSION_KEY 0x00400000
+#define NTLMSSP_NEGOTIATE_TARGET_INFO 0x00800000
+/* reserved 0x01000000 */
+#define NTLMSSP_NEGOTIATE_VERSION 0x02000000
+/* reserved 0x04000000 */
+/* reserved 0x08000000 */
+/* reserved 0x10000000 */
+#define NTLMSSP_NEGOTIATE_128 0x20000000
+#define NTLMSSP_NEGOTIATE_KEY_EXCH 0x40000000
+#define NTLMSSP_NEGOTIATE_56 0x80000000
+
+#endif /* _NTLMSSP_H */
diff --git a/usr/src/lib/libsmbfs/smb/print.c b/usr/src/lib/libsmbfs/smb/print.c
index e0b18e8c40..263cafaa93 100644
--- a/usr/src/lib/libsmbfs/smb/print.c
+++ b/usr/src/lib/libsmbfs/smb/print.c
@@ -46,50 +46,77 @@
#include <grp.h>
#include <unistd.h>
+#include <netsmb/smb.h>
#include <netsmb/smb_lib.h>
-#include <cflib.h>
#include "private.h"
int
-smb_smb_open_print_file(struct smb_ctx *ctx, int setuplen, int mode,
- const char *ident, smbfh *fhp)
+smb_printer_open(struct smb_ctx *ctx, int setuplen, int mode,
+ const char *ident, int *fhp)
{
struct smb_rq *rqp;
struct mbdata *mbp;
- int error;
+ int error, flags2, uc;
+ uint16_t fh;
+ uint8_t wc;
+
+ flags2 = smb_ctx_flags2(ctx);
+ if (flags2 == -1)
+ return (EIO);
+ uc = flags2 & SMB_FLAGS2_UNICODE;
- error = smb_rq_init(ctx, SMB_COM_OPEN_PRINT_FILE, 2, &rqp);
+ error = smb_rq_init(ctx, SMB_COM_OPEN_PRINT_FILE, &rqp);
if (error)
return (error);
mbp = smb_rq_getrequest(rqp);
+ smb_rq_wstart(rqp);
mb_put_uint16le(mbp, setuplen);
mb_put_uint16le(mbp, mode);
smb_rq_wend(rqp);
+ smb_rq_bstart(rqp);
mb_put_uint8(mbp, SMB_DT_ASCII);
- smb_rq_dstring(mbp, ident);
+ mb_put_dstring(mbp, ident, uc);
+ smb_rq_bend(rqp);
error = smb_rq_simple(rqp);
- if (!error) {
- mbp = smb_rq_getreply(rqp);
- mb_get_uint16(mbp, fhp);
+ if (error)
+ goto out;
+
+ mbp = smb_rq_getreply(rqp);
+ error = mb_get_uint8(mbp, &wc);
+ if (error || wc < 1) {
+ error = EBADRPC;
+ goto out;
}
+ mb_get_uint16(mbp, &fh);
+ *fhp = fh;
+ error = 0;
+
+out:
smb_rq_done(rqp);
return (error);
}
+/*
+ * Similar to smb_fh_close
+ */
int
-smb_smb_close_print_file(struct smb_ctx *ctx, smbfh fh)
+smb_printer_close(struct smb_ctx *ctx, int fh)
{
struct smb_rq *rqp;
struct mbdata *mbp;
int error;
- error = smb_rq_init(ctx, SMB_COM_CLOSE_PRINT_FILE, 0, &rqp);
+ error = smb_rq_init(ctx, SMB_COM_CLOSE_PRINT_FILE, &rqp);
if (error)
return (error);
mbp = smb_rq_getrequest(rqp);
- mb_put_mem(mbp, (char *)&fh, 2);
+ smb_rq_wstart(rqp);
+ mb_put_uint16le(mbp, (uint16_t)fh);
smb_rq_wend(rqp);
+ mb_put_uint16le(mbp, 0); /* byte count */
+
error = smb_rq_simple(rqp);
smb_rq_done(rqp);
+
return (error);
}
diff --git a/usr/src/lib/libsmbfs/smb/private.h b/usr/src/lib/libsmbfs/smb/private.h
index 6630ed160a..b95ec1f7f4 100644
--- a/usr/src/lib/libsmbfs/smb/private.h
+++ b/usr/src/lib/libsmbfs/smb/private.h
@@ -31,7 +31,7 @@
*/
/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -44,6 +44,33 @@
*/
#include <inttypes.h>
+#include <sys/byteorder.h>
+#include <sys/ccompile.h>
+
+#include <netsmb/netbios.h>
+
+extern void dprint(const char *, const char *, ...)
+ __PRINTFLIKE(2);
+
+#if defined(DEBUG) || defined(__lint)
+#define DPRINT(...) dprint(__func__, __VA_ARGS__)
+#else
+#define DPRINT(...) ((void)0)
+#endif
+
+/*
+ * Flags bits in ct_vcflags (copied from smb_conn.h)
+ * Pass these to the driver?
+ */
+#define SMBV_RECONNECTING 0x0002 /* conn in process of reconnection */
+#define SMBV_LONGNAMES 0x0004 /* conn configured to use long names */
+#define SMBV_ENCRYPT 0x0008 /* server demands encrypted password */
+#define SMBV_WIN95 0x0010 /* used to apply bugfixes for this OS */
+#define SMBV_NT4 0x0020 /* used when NT4 issues invalid resp */
+#define SMBV_UNICODE 0x0040 /* conn configured to use Unicode */
+#define SMBV_EXT_SEC 0x0080 /* conn to use extended security */
+#define SMBV_WILL_SIGN 0x0100 /* negotiated signing */
+
/*
* BSD-style mbuf simulation
@@ -56,7 +83,6 @@ struct mbuf {
};
typedef struct mbuf mbuf_t;
-#if 0 /* in smb_lib.h */
struct mbdata {
struct mbuf *mb_top;
struct mbuf *mb_cur;
@@ -64,12 +90,16 @@ struct mbdata {
int mb_count;
};
typedef struct mbdata mbdata_t;
-#endif
+/*
+ * Note: Leaving a little space (8 bytes) between the
+ * mbuf header and the start of the data so we can
+ * prepend a NetBIOS header in that space.
+ */
#define M_ALIGNFACTOR (sizeof (long))
#define M_ALIGN(len) (((len) + M_ALIGNFACTOR - 1) & ~(M_ALIGNFACTOR - 1))
-#define M_BASESIZE (sizeof (struct mbuf))
-#define M_MINSIZE (256 - M_BASESIZE)
+#define M_BASESIZE (sizeof (struct mbuf) + 8)
+#define M_MINSIZE (1024 - M_BASESIZE)
#define M_TOP(m) ((char *)(m) + M_BASESIZE)
#define M_TRAILINGSPACE(m) ((m)->m_maxlen - (m)->m_len)
#define mtod(m, t) ((t)(m)->m_data)
@@ -78,32 +108,51 @@ typedef struct mbdata mbdata_t;
* request handling structures
*/
struct smb_rq {
- uchar_t rq_cmd;
+ struct smb_ctx *rq_ctx;
struct mbdata rq_rq;
struct mbdata rq_rp;
- struct smb_ctx *rq_ctx;
- int rq_wcount;
- int rq_bcount;
+ int rq_rpbufsz;
+ uint8_t rq_cmd;
+ uint8_t rq_hflags;
+ uint16_t rq_hflags2;
+ uint32_t rq_status;
+ uint16_t rq_uid;
+ uint16_t rq_tid;
+ uint16_t rq_mid;
+ uint32_t rq_seqno;
+ /* See rq_[bw]{start,end} functions */
+ char *rq_wcntp;
+ int rq_wcbase;
+ char *rq_bcntp;
+ int rq_bcbase;
};
typedef struct smb_rq smb_rq_t;
#define smb_rq_getrequest(rqp) (&(rqp)->rq_rq)
#define smb_rq_getreply(rqp) (&(rqp)->rq_rp)
-int smb_rq_init(struct smb_ctx *, uchar_t, size_t, struct smb_rq **);
+int smb_rq_init(struct smb_ctx *, uchar_t, struct smb_rq **);
void smb_rq_done(struct smb_rq *);
+void smb_rq_bstart(struct smb_rq *);
+void smb_rq_bend(struct smb_rq *);
+void smb_rq_wstart(struct smb_rq *);
void smb_rq_wend(struct smb_rq *);
int smb_rq_simple(struct smb_rq *);
int smb_rq_dmem(struct mbdata *, const char *, size_t);
-int smb_rq_dstring(struct mbdata *, const char *);
+int smb_rq_internal(struct smb_ctx *, struct smb_rq *);
+int smb_rq_sign(struct smb_rq *);
+int smb_rq_verify(struct smb_rq *);
/*
* Message compose/parse
*/
+void m_freem(struct mbuf *);
int m_getm(struct mbuf *, size_t, struct mbuf **);
int m_lineup(struct mbuf *, struct mbuf **);
+size_t m_totlen(struct mbuf *);
+
int mb_init(struct mbdata *, size_t);
int mb_initm(struct mbdata *, struct mbuf *);
int mb_done(struct mbdata *);
@@ -115,9 +164,11 @@ int mb_put_uint32be(struct mbdata *, uint32_t);
int mb_put_uint32le(struct mbdata *, uint32_t);
int mb_put_uint64be(struct mbdata *, uint64_t);
int mb_put_uint64le(struct mbdata *, uint64_t);
-int mb_put_mem(struct mbdata *, const char *, size_t);
-int mb_put_pstring(struct mbdata *mbp, const char *s);
+int mb_put_mem(struct mbdata *, const void *, size_t);
int mb_put_mbuf(struct mbdata *, struct mbuf *);
+int mb_put_astring(struct mbdata *mbp, const char *s);
+int mb_put_dstring(struct mbdata *mbp, const char *s, int);
+int mb_put_ustring(struct mbdata *mbp, const char *s);
int mb_get_uint8(struct mbdata *, uint8_t *);
int mb_get_uint16(struct mbdata *, uint16_t *);
@@ -129,26 +180,82 @@ int mb_get_uint32le(struct mbdata *, uint32_t *);
int mb_get_uint64(struct mbdata *, uint64_t *);
int mb_get_uint64be(struct mbdata *, uint64_t *);
int mb_get_uint64le(struct mbdata *, uint64_t *);
-int mb_get_mem(struct mbdata *, char *, size_t);
+int mb_get_mem(struct mbdata *, void *, size_t);
+int mb_get_mbuf(struct mbdata *, int, struct mbuf **);
+int mb_get_string(struct mbdata *, char **, int);
+int mb_get_astring(struct mbdata *, char **);
+int mb_get_ustring(struct mbdata *, char **);
+
/*
* Network stuff (NetBIOS and otherwise)
*/
+struct nb_name;
+struct sockaddr_nb;
+
+extern int smb_recv_timeout; /* seconds */
+
+void dump_ctx(char *, struct smb_ctx *);
+void dump_addrinfo(struct addrinfo *);
+void dump_sockaddr(struct sockaddr *);
+int nb_ssn_request(struct smb_ctx *, char *);
int nb_name_len(struct nb_name *);
-/* new flag UCflag. 1=uppercase,0=don't */
-int nb_name_encode(struct nb_name *, uchar_t *);
+int nb_name_encode(struct mbdata *, struct nb_name *);
int nb_encname_len(const uchar_t *);
-int nb_snballoc(int namelen, struct sockaddr_nb **);
+int nb_snballoc(struct sockaddr_nb **);
void nb_snbfree(struct sockaddr *);
int nb_sockaddr(struct sockaddr *, struct nb_name *, struct sockaddr_nb **);
+int nbns_getaddrinfo(const char *name, struct nb_ctx *nbc,
+ struct addrinfo **res);
int nbns_resolvename(const char *, struct nb_ctx *, struct sockaddr **);
-int nbns_getnodestatus(struct sockaddr *targethost,
- struct nb_ctx *ctx, char *system, char *workgroup);
-int nb_getlocalname(char *name, size_t maxlen);
+int get_xti_err(int);
+
+
+/*
+ * Private SMB stuff
+ */
+
+struct smb_bitname {
+ uint_t bn_bit;
+ char *bn_name;
+};
+typedef struct smb_bitname smb_bitname_t;
+char *smb_printb(char *, int, const struct smb_bitname *);
+
+int smb_ctx_getaddr(struct smb_ctx *ctx);
+int smb_ctx_gethandle(struct smb_ctx *ctx);
+
+int smb_ssn_send(struct smb_ctx *, struct mbdata *);
+int smb_ssn_recv(struct smb_ctx *, struct mbdata *);
+
+int smb_negprot(struct smb_ctx *, struct mbdata *);
+
+int smb_ssnsetup_null(struct smb_ctx *);
+int smb_ssnsetup_ntlm1(struct smb_ctx *);
+int smb_ssnsetup_ntlm2(struct smb_ctx *);
+int smb_ssnsetup_spnego(struct smb_ctx *, struct mbdata *);
+
+void smb_time_local2server(struct timeval *, int, long *);
+void smb_time_server2local(ulong_t, int, struct timeval *);
+void smb_time_NT2local(uint64_t, int, struct timeval *);
+void smb_time_local2NT(struct timeval *, int, uint64_t *);
+
+int smb_getlocalname(char **);
+int smb_get_authentication(struct smb_ctx *);
+int smb_get_keychain(struct smb_ctx *ctx);
+void smb_hexdump(const void *buf, int len);
+
+/* See ssp.c */
+int ssp_ctx_create_client(struct smb_ctx *, struct mbdata *);
+int ssp_ctx_next_token(struct smb_ctx *, struct mbdata *, struct mbdata *);
+void ssp_ctx_destroy(struct smb_ctx *);
+#ifdef KICONV_SUPPORT
+/* See nls.c (get rid of this?) */
extern uchar_t nls_lower[256], nls_upper[256];
+#endif /* KICONV_SUPPORT */
#endif /* _PRIVATE_H */
diff --git a/usr/src/lib/libsmbfs/smb/rap.c b/usr/src/lib/libsmbfs/smb/rap.c
index 3a9e785191..8260e2639c 100644
--- a/usr/src/lib/libsmbfs/smb/rap.c
+++ b/usr/src/lib/libsmbfs/smb/rap.c
@@ -204,7 +204,6 @@ smb_rap_create(int fn, const char *param, const char *data,
struct smb_rap *rap;
char *p;
int plen = 0, len = 0;
- int i;
rap = malloc(sizeof (*rap));
if (rap == NULL)
@@ -326,10 +325,10 @@ int
smb_rap_request(struct smb_rap *rap, struct smb_ctx *ctx)
{
uint16_t *rp, conv, *tmp;
- uint32_t *p32, ps1;
+ uint32_t *p32;
char *dp, *p = rap->r_nparam;
char ptype;
- int error, rdatacnt, rparamcnt, entries, done, dlen, buffer_oflow, i;
+ int error, rdatacnt, rparamcnt, entries, done, dlen, buffer_oflow;
rdatacnt = rap->r_rcvbuflen;
rparamcnt = rap->r_plen;
diff --git a/usr/src/lib/libsmbfs/smb/rcfile.c b/usr/src/lib/libsmbfs/smb/rcfile.c
index 3f5a87435d..22ca0fc420 100644
--- a/usr/src/lib/libsmbfs/smb/rcfile.c
+++ b/usr/src/lib/libsmbfs/smb/rcfile.c
@@ -36,45 +36,61 @@
#include <sys/types.h>
#include <sys/queue.h>
#include <sys/stat.h>
+
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <stdlib.h>
-#include <libintl.h>
-#include <pwd.h>
+#include <synch.h>
#include <unistd.h>
-#include <sys/debug.h>
+#include <pwd.h>
+#include <libintl.h>
#include <cflib.h>
#include "rcfile_priv.h"
-extern int smb_debug;
-SLIST_HEAD(rcfile_head, rcfile);
-static struct rcfile_head pf_head = {NULL};
+#include <assert.h>
+
+#if 0 /* before SMF */
+#define SMB_CFG_FILE "/etc/nsmb.conf"
+#define OLD_SMB_CFG_FILE "/usr/local/etc/nsmb.conf"
+#endif
+#define SMBFS_SHARECTL_CMD "/usr/sbin/sharectl get smbfs"
+
+extern int smb_debug;
static struct rcfile *rc_cachelookup(const char *filename);
-struct rcsection *rc_findsect(struct rcfile *rcp, const char *sectname);
+static struct rcsection *rc_findsect(struct rcfile *rcp, const char *sectname);
static struct rcsection *rc_addsect(struct rcfile *rcp, const char *sectname);
-static int rc_freesect(struct rcfile *rcp, struct rcsection *rsp);
-struct rckey *rc_sect_findkey(struct rcsection *rsp, const char *keyname);
+static int rc_freesect(struct rcfile *rcp, struct rcsection *rsp);
+static struct rckey *rc_sect_findkey(struct rcsection *rsp, const char *key);
static struct rckey *rc_sect_addkey(struct rcsection *rsp, const char *name,
const char *value);
static void rc_key_free(struct rckey *p);
static void rc_parse(struct rcfile *rcp);
+/* lock for the variables below */
+mutex_t rcfile_mutex = DEFAULTMUTEX;
+
+SLIST_HEAD(rcfile_head, rcfile);
+static struct rcfile_head pf_head = {NULL};
+struct rcfile *smb_rc;
+int home_nsmbrc;
int insecure_nsmbrc;
/*
* open rcfile and load its content, if already open - return previous handle
*/
-int
+static int
rc_open(const char *filename, const char *mode, struct rcfile **rcfile)
{
+ struct stat statbuf;
struct rcfile *rcp;
FILE *f;
- struct stat statbuf;
+
+ assert(MUTEX_HELD(&rcfile_mutex));
rcp = rc_cachelookup(filename);
if (rcp) {
@@ -102,12 +118,15 @@ rc_open(const char *filename, const char *mode, struct rcfile **rcfile)
return (0);
}
-int
+static int
rc_merge(const char *filename, struct rcfile **rcfile)
{
+ struct stat statbuf;
struct rcfile *rcp = *rcfile;
FILE *f, *t;
+ assert(MUTEX_HELD(&rcfile_mutex));
+
insecure_nsmbrc = 0;
if (rcp == NULL) {
return (rc_open(filename, "r", rcfile));
@@ -115,6 +134,10 @@ rc_merge(const char *filename, struct rcfile **rcfile)
f = fopen(filename, "r");
if (f == NULL)
return (errno);
+ insecure_nsmbrc = 0;
+ if (fstat(fileno(f), &statbuf) >= 0 &&
+ (statbuf.st_mode & 077) != 0)
+ insecure_nsmbrc = 1;
t = rcp->rf_f;
rcp->rf_f = f;
rc_parse(rcp);
@@ -123,43 +146,45 @@ rc_merge(const char *filename, struct rcfile **rcfile)
return (0);
}
-int
-rc_merge_pipe(const char *command, struct rcfile **rcfile)
+/*
+ * Like rc_open, but does popen of command:
+ * sharectl get smbfs
+ */
+static int
+rc_popen_cmd(const char *command, struct rcfile **rcfile)
{
- struct rcfile *rcp = *rcfile;
- FILE *f, *t;
+ struct rcfile *rcp;
+ FILE *f;
+
+ assert(MUTEX_HELD(&rcfile_mutex));
- insecure_nsmbrc = 0;
f = popen(command, "r");
if (f == NULL)
return (errno);
+ insecure_nsmbrc = 0;
+
+ rcp = malloc(sizeof (struct rcfile));
if (rcp == NULL) {
- rcp = malloc(sizeof (struct rcfile));
- if (rcp == NULL) {
- fclose(f);
- return (ENOMEM);
- }
- *rcfile = rcp;
- bzero(rcp, sizeof (struct rcfile));
- rcp->rf_name = strdup(command);
- rcp->rf_f = f;
- SLIST_INSERT_HEAD(&pf_head, rcp, rf_next);
- rc_parse(rcp);
- } else {
- t = rcp->rf_f;
- rcp->rf_f = f;
- rc_parse(rcp);
- rcp->rf_f = t;
+ fclose(f);
+ return (ENOMEM);
}
- fclose(f);
+ bzero(rcp, sizeof (struct rcfile));
+ rcp->rf_name = strdup(command);
+ rcp->rf_f = f;
+ SLIST_INSERT_HEAD(&pf_head, rcp, rf_next);
+ rc_parse(rcp);
+ *rcfile = rcp;
+ /* fclose(f) in rc_close */
return (0);
}
-int
+static int
rc_close(struct rcfile *rcp)
{
struct rcsection *p, *n;
+ mutex_lock(&rcfile_mutex);
+
fclose(rcp->rf_f);
for (p = SLIST_FIRST(&rcp->rf_sect); p; ) {
n = p;
@@ -169,6 +194,8 @@ rc_close(struct rcfile *rcp)
free(rcp->rf_name);
SLIST_REMOVE(&pf_head, rcp, rcfile, rf_next);
free(rcp);
+
+ mutex_unlock(&rcfile_mutex);
return (0);
}
@@ -177,17 +204,21 @@ rc_cachelookup(const char *filename)
{
struct rcfile *p;
+ assert(MUTEX_HELD(&rcfile_mutex));
+
SLIST_FOREACH(p, &pf_head, rf_next)
if (strcmp(filename, p->rf_name) == 0)
return (p);
return (0);
}
-/* static */ struct rcsection *
+static struct rcsection *
rc_findsect(struct rcfile *rcp, const char *sectname)
{
struct rcsection *p;
+ assert(MUTEX_HELD(&rcfile_mutex));
+
SLIST_FOREACH(p, &rcp->rf_sect, rs_next)
if (strcasecmp(p->rs_name, sectname) == 0)
return (p);
@@ -199,6 +230,8 @@ rc_addsect(struct rcfile *rcp, const char *sectname)
{
struct rcsection *p;
+ assert(MUTEX_HELD(&rcfile_mutex));
+
p = rc_findsect(rcp, sectname);
if (p)
return (p);
@@ -216,6 +249,8 @@ rc_freesect(struct rcfile *rcp, struct rcsection *rsp)
{
struct rckey *p, *n;
+ assert(MUTEX_HELD(&rcfile_mutex));
+
SLIST_REMOVE(&rcp->rf_sect, rsp, rcsection, rs_next);
for (p = SLIST_FIRST(&rsp->rs_keys); p; ) {
n = p;
@@ -227,11 +262,13 @@ rc_freesect(struct rcfile *rcp, struct rcsection *rsp)
return (0);
}
-/* static */ struct rckey *
+static struct rckey *
rc_sect_findkey(struct rcsection *rsp, const char *keyname)
{
struct rckey *p;
+ assert(MUTEX_HELD(&rcfile_mutex));
+
SLIST_FOREACH(p, &rsp->rs_keys, rk_next)
if (strcmp(p->rk_name, keyname) == 0)
return (p);
@@ -243,6 +280,8 @@ rc_sect_addkey(struct rcsection *rsp, const char *name, const char *value)
{
struct rckey *p;
+ assert(MUTEX_HELD(&rcfile_mutex));
+
p = rc_sect_findkey(rsp, name);
if (!p) {
p = malloc(sizeof (*p));
@@ -273,16 +312,13 @@ rc_key_free(struct rckey *p)
free(p);
}
-enum { stNewLine, stHeader, stSkipToEOL, stGetKey, stGetValue};
-
-int home_nsmbrc = 0;
-static char *minauth[] = {
- "kerberos",
- "ntlmv2",
- "ntlm",
- "lm",
+static char *minauth_values[] = {
"none",
+ "lm",
+ "ntlm",
+ "ntlmv2",
+ "kerberos",
NULL
};
@@ -291,43 +327,57 @@ eval_minauth(char *auth)
{
int i;
- for (i = 0; minauth[i]; i++)
- if (strcmp(auth, minauth[i]) == 0)
- break;
- return (i);
+ for (i = 0; minauth_values[i]; i++)
+ if (strcmp(auth, minauth_values[i]) == 0)
+ return (i);
+ return (-1);
}
/*
- * Ensure that "minauth" is set to the highest level (lowest array offset)
+ * Ensure that "minauth" is set to the highest level
*/
+/*ARGSUSED*/
static void
set_value(struct rcfile *rcp, struct rcsection *rsp, struct rckey *rkp,
char *ptr)
{
int now, new;
+#ifdef DEBUG
+ char *from;
+
+ if (smb_debug)
+ from = (home_nsmbrc) ?
+ "user file" : "SMF";
+#endif
if (strcmp(rkp->rk_name, "minauth") == 0) {
now = eval_minauth(rkp->rk_value);
new = eval_minauth(ptr);
- if (new >= now) {
+ if (new <= now) {
#ifdef DEBUG
if (smb_debug)
- printf(
- "set_value: rejecting %s=%s from %s\n",
- rkp->rk_name, ptr, home_nsmbrc ?
- "user file" : "SMF");
+ fprintf(stderr,
+ "set_value: rejecting %s=%s"
+ " in %s from %s\n",
+ rkp->rk_name, ptr,
+ rsp->rs_name, from);
#endif
return;
}
}
#ifdef DEBUG
if (smb_debug)
- printf("set_value: applying %s=%s from %s\n",
- rkp->rk_name, ptr, home_nsmbrc ? "user file" : "SMF");
+ fprintf(stderr,
+ "set_value: applying %s=%s in %s from %s\n",
+ rkp->rk_name, ptr, rsp->rs_name, from);
#endif
rkp->rk_value = strdup(ptr);
}
+
+/* states in rc_parse */
+enum { stNewLine, stHeader, stSkipToEOL, stGetKey, stGetValue};
+
static void
rc_parse(struct rcfile *rcp)
{
@@ -338,6 +388,8 @@ rc_parse(struct rcfile *rcp)
char buf[2048];
char *next = buf, *last = &buf[sizeof (buf)-1];
+ assert(MUTEX_HELD(&rcfile_mutex));
+
while ((c = getc(f)) != EOF) {
if (c == '\r')
continue;
@@ -393,8 +445,8 @@ rc_parse(struct rcfile *rcp)
state = stSkipToEOL;
continue;
}
- if (home_nsmbrc &&
- (strcmp(buf, "nbns") == 0 ||
+ if (home_nsmbrc != 0 && (
+ strcmp(buf, "nbns") == 0 ||
strcmp(buf, "nbns_enable") == 0 ||
strcmp(buf, "nbns_broadcast") == 0 ||
strcmp(buf, "signing") == 0)) {
@@ -405,7 +457,8 @@ rc_parse(struct rcfile *rcp)
state = stNewLine;
continue;
}
- if (insecure_nsmbrc && (strcmp(buf, "password") == 0)) {
+ if (insecure_nsmbrc != 0 &&
+ strcmp(buf, "password") == 0) {
fprintf(stderr, dgettext(TEXT_DOMAIN,
"Warning: .nsmbrc file not secure, "
"ignoring passwords\n"));
@@ -445,16 +498,27 @@ rc_getstringptr(struct rcfile *rcp, const char *section, const char *key,
{
struct rcsection *rsp;
struct rckey *rkp;
+ int err;
+
+ mutex_lock(&rcfile_mutex);
*dest = NULL;
rsp = rc_findsect(rcp, section);
- if (!rsp)
- return (ENOENT);
+ if (!rsp) {
+ err = ENOENT;
+ goto out;
+ }
rkp = rc_sect_findkey(rsp, key);
- if (!rkp)
- return (ENOENT);
+ if (!rkp) {
+ err = ENOENT;
+ goto out;
+ }
*dest = rkp->rk_value;
- return (0);
+ err = 0;
+
+out:
+ mutex_unlock(&rcfile_mutex);
+ return (err);
}
int
@@ -468,7 +532,7 @@ rc_getstring(struct rcfile *rcp, const char *section, const char *key,
if (error)
return (error);
if (strlen(value) >= maxlen) {
- fprintf(stdout, dgettext(TEXT_DOMAIN,
+ fprintf(stderr, dgettext(TEXT_DOMAIN,
"line too long for key '%s' in section '%s', max = %d\n"),
key, section, maxlen);
return (EINVAL);
@@ -482,22 +546,31 @@ rc_getint(struct rcfile *rcp, const char *section, const char *key, int *value)
{
struct rcsection *rsp;
struct rckey *rkp;
+ int err;
+
+ mutex_lock(&rcfile_mutex);
rsp = rc_findsect(rcp, section);
- if (!rsp)
- return (ENOENT);
+ if (!rsp) {
+ err = ENOENT;
+ goto out;
+ }
rkp = rc_sect_findkey(rsp, key);
- if (!rkp)
- return (ENOENT);
+ if (!rkp) {
+ err = ENOENT;
+ goto out;
+ }
errno = 0;
*value = strtol(rkp->rk_value, NULL, 0);
- if (errno) {
- fprintf(stdout, dgettext(TEXT_DOMAIN,
+ if ((err = errno) != 0) {
+ fprintf(stderr, dgettext(TEXT_DOMAIN,
"invalid int value '%s' for key '%s' in section '%s'\n"),
rkp->rk_value, key, section);
- return (errno);
}
- return (0);
+
+out:
+ mutex_unlock(&rcfile_mutex);
+ return (err);
}
/*
@@ -510,139 +583,136 @@ rc_getbool(struct rcfile *rcp, const char *section, const char *key, int *value)
struct rcsection *rsp;
struct rckey *rkp;
char *p;
+ int err;
+
+ mutex_lock(&rcfile_mutex);
rsp = rc_findsect(rcp, section);
- if (!rsp)
- return (ENOENT);
+ if (!rsp) {
+ err = ENOENT;
+ goto out;
+ }
rkp = rc_sect_findkey(rsp, key);
- if (!rkp)
- return (ENOENT);
+ if (!rkp) {
+ err = ENOENT;
+ goto out;
+ }
p = rkp->rk_value;
while (*p && isspace(*p)) p++;
if (*p == '0' ||
strcasecmp(p, "no") == 0 ||
strcasecmp(p, "false") == 0) {
*value = 0;
- return (0);
+ err = 0;
+ goto out;
}
if (*p == '1' ||
strcasecmp(p, "yes") == 0 ||
strcasecmp(p, "true") == 0) {
*value = 1;
- return (0);
+ err = 0;
+ goto out;
}
fprintf(stderr, dgettext(TEXT_DOMAIN,
"invalid boolean value '%s' for key '%s' in section '%s' \n"),
p, key, section);
- return (EINVAL);
+ err = EINVAL;
+
+out:
+ mutex_unlock(&rcfile_mutex);
+ return (err);
+}
+
+#ifdef DEBUG
+void
+dump_props(char *where)
+{
+ struct rcsection *rsp = NULL;
+ struct rckey *rkp = NULL;
+
+ fprintf(stderr, "Settings %s\n", where);
+ SLIST_FOREACH(rsp, &smb_rc->rf_sect, rs_next) {
+ fprintf(stderr, "section=%s\n", rsp->rs_name);
+ fflush(stderr);
+
+ SLIST_FOREACH(rkp, &rsp->rs_keys, rk_next) {
+ fprintf(stderr, " key=%s, value=%s\n",
+ rkp->rk_name, rkp->rk_value);
+ fflush(stderr);
+ }
+ }
}
+#endif
/*
- * Unified command line/rc file parser
+ * first parse "sharectl get smbfs, then $HOME/.nsmbrc
+ * This is called by library consumers (commands)
*/
int
-opt_args_parse(struct rcfile *rcp, struct opt_args *ap, const char *sect,
- opt_callback_t *callback)
+smb_open_rcfile(char *home)
{
- int len, error;
-
- for (; ap->opt; ap++) {
- switch (ap->type) {
- case OPTARG_STR:
- if (rc_getstringptr(rcp, sect, ap->name, &ap->str) != 0)
- break;
- len = strlen(ap->str);
- if (len > ap->ival) {
- fprintf(stdout, dgettext(TEXT_DOMAIN,
- "rc: argument for option '%c' (%s) too long\n"),
- ap->opt, ap->name);
- return (EINVAL);
- }
- callback(ap);
- break;
- case OPTARG_BOOL:
- error = rc_getbool(rcp, sect, ap->name, &ap->ival);
- if (error == ENOENT)
- break;
- if (error)
- return (EINVAL);
- callback(ap);
- break;
- case OPTARG_INT:
- if (rc_getint(rcp, sect, ap->name, &ap->ival) != 0)
- break;
- if (((ap->flag & OPTFL_HAVEMIN) &&
- ap->ival < ap->min) ||
- ((ap->flag & OPTFL_HAVEMAX) &&
- ap->ival > ap->max)) {
- fprintf(stdout, dgettext(TEXT_DOMAIN,
- "rc: argument for option '%c' (%s) "
- "should be in [%d-%d] range\n"),
- ap->opt, ap->name, ap->min, ap->max);
- return (EINVAL);
- }
- callback(ap);
- break;
- default:
- break;
+ char *fn;
+ int len, error = 0;
+
+ mutex_lock(&rcfile_mutex);
+
+ smb_rc = NULL;
+#if 0 /* before SMF */
+ fn = SMB_CFG_FILE;
+ error = rc_open(fn, &smb_rc);
+#else
+ fn = SMBFS_SHARECTL_CMD;
+ error = rc_popen_cmd(fn, &smb_rc);
+#endif
+ if (error != 0 && error != ENOENT) {
+ /* Error from fopen. strerror is OK. */
+ fprintf(stderr, dgettext(TEXT_DOMAIN,
+ "Can't open %s: %s\n"), fn, strerror(errno));
+ }
+#ifdef DEBUG
+ if (smb_debug)
+ dump_props(fn);
+#endif
+
+ if (home) {
+ len = strlen(home) + 20;
+ fn = malloc(len);
+ snprintf(fn, len, "%s/.nsmbrc", home);
+ home_nsmbrc = 1;
+ error = rc_merge(fn, &smb_rc);
+ if (error != 0 && error != ENOENT) {
+ fprintf(stderr, dgettext(TEXT_DOMAIN,
+ "Can't open %s: %s\n"), fn, strerror(errno));
}
+ home_nsmbrc = 0;
+#ifdef DEBUG
+ if (smb_debug)
+ dump_props(fn);
+#endif
+ free(fn);
}
- return (0);
+
+ /* Mostly ignore error returns above. */
+ if (smb_rc == NULL)
+ error = ENOENT;
+ else
+ error = 0;
+
+ mutex_unlock(&rcfile_mutex);
+
+ return (error);
}
-int
-opt_args_parseopt(struct opt_args *ap, int opt, char *arg,
- opt_callback_t *callback)
+/*
+ * This is called by library consumers (commands)
+ */
+void
+smb_close_rcfile(void)
{
- int len;
+ struct rcfile *rcp;
- for (; ap->opt; ap++) {
- if (ap->opt != opt)
- continue;
- switch (ap->type) {
- case OPTARG_STR:
- ap->str = arg;
- if (arg) {
- len = strlen(ap->str);
- if (len > ap->ival) {
- fprintf(stdout, dgettext(TEXT_DOMAIN,
- "opt: Argument for option '%c' (%s) too long\n"),
- ap->opt, ap->name);
- return (EINVAL);
- }
- callback(ap);
- }
- break;
- case OPTARG_BOOL:
- ap->ival = 0;
- callback(ap);
- break;
- case OPTARG_INT:
- errno = 0;
- ap->ival = strtol(arg, NULL, 0);
- if (errno) {
- fprintf(stdout, dgettext(TEXT_DOMAIN,
- "opt: Invalid integer value for "
- "option '%c' (%s).\n"),
- ap->opt, ap->name);
- return (EINVAL);
- }
- if (((ap->flag & OPTFL_HAVEMIN) &&
- (ap->ival < ap->min)) ||
- ((ap->flag & OPTFL_HAVEMAX) &&
- (ap->ival > ap->max))) {
- fprintf(stdout, dgettext(TEXT_DOMAIN,
- "opt: Argument for option '%c' (%s) "
- "should be in [%d-%d] range\n"),
- ap->opt, ap->name, ap->min, ap->max);
- return (EINVAL);
- }
- callback(ap);
- break;
- default:
- break;
- }
- break;
+ if ((rcp = smb_rc) != NULL) {
+ smb_rc = NULL;
+ rc_close(rcp);
}
- return (0);
}
diff --git a/usr/src/lib/libsmbfs/smb/rcfile_priv.h b/usr/src/lib/libsmbfs/smb/rcfile_priv.h
index 85ed97e1fd..ef9e31d7fc 100644
--- a/usr/src/lib/libsmbfs/smb/rcfile_priv.h
+++ b/usr/src/lib/libsmbfs/smb/rcfile_priv.h
@@ -1,4 +1,34 @@
-#pragma ident "%Z%%M% %I% %E% SMI"
+/*
+ * Copyright (c) 2000, Boris Popov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Boris Popov.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
struct rckey {
SLIST_ENTRY(rckey) rk_next;
@@ -8,14 +38,17 @@ struct rckey {
struct rcsection {
SLIST_ENTRY(rcsection) rs_next;
- SLIST_HEAD(rckey_head,rckey) rs_keys;
+ SLIST_HEAD(rckey_head, rckey) rs_keys;
char *rs_name;
};
-
+
struct rcfile {
SLIST_ENTRY(rcfile) rf_next;
SLIST_HEAD(rcsec_head, rcsection) rf_sect;
char *rf_name;
FILE *rf_f;
+ int rf_flags; /* RCFILE_... */
};
+#define RCFILE_HOME_NSMBRC 1
+#define RCFILE_IS_INSECURE 2
diff --git a/usr/src/lib/libsmbfs/smb/rq.c b/usr/src/lib/libsmbfs/smb/rq.c
index 6390a3c157..7b21708428 100644
--- a/usr/src/lib/libsmbfs/smb/rq.c
+++ b/usr/src/lib/libsmbfs/smb/rq.c
@@ -47,26 +47,66 @@
#include <sysexits.h>
#include <libintl.h>
+#include <netsmb/smb.h>
#include <netsmb/smb_lib.h>
#include "private.h"
+static uint32_t smb_map_doserr(uint8_t, uint16_t);
+/*
+ * Create and initialize a request structure, for either an
+ * "internal" request (one that does not use the driver) or
+ * a regular "driver" request, that uses driver ioctls.
+ *
+ * The two kinds are built a little differently:
+ * Driver requests are composed starting with the
+ * first word of the "variable word vector" section.
+ * The driver prepends the SMB header and word count.
+ * The driver also needs an output buffer to receive
+ * the response, filled in via copyout in the ioctl.
+ *
+ * Internal requests are composed entirely in this library.
+ * Space for the SMB header is reserved here, and later
+ * filled in by smb_rq_internal before the send/receive.
+ */
int
-smb_rq_init(struct smb_ctx *ctx, uchar_t cmd, size_t rpbufsz,
- struct smb_rq **rqpp)
+smb_rq_init(struct smb_ctx *ctx, uchar_t cmd, struct smb_rq **rqpp)
{
struct smb_rq *rqp;
rqp = malloc(sizeof (*rqp));
if (rqp == NULL)
- return (ENOMEM);
+ goto errout;
bzero(rqp, sizeof (*rqp));
rqp->rq_cmd = cmd;
rqp->rq_ctx = ctx;
- mb_init(&rqp->rq_rq, M_MINSIZE);
- mb_init(&rqp->rq_rp, rpbufsz);
+
+ /*
+ * Setup the request buffer.
+ * Do the reply buffer later.
+ */
+ if (mb_init(&rqp->rq_rq, M_MINSIZE))
+ goto errout;
+
+ /* Space for the SMB header. (filled in later) */
+ mb_put_mem(&rqp->rq_rq, NULL, SMB_HDRLEN);
+
+ /*
+ * Copy the ctx flags here, so the caller can
+ * update the req flags before the OTW call.
+ */
+ rqp->rq_hflags = ctx->ct_hflags;
+ rqp->rq_hflags2 = ctx->ct_hflags2;
+
*rqpp = rqp;
return (0);
+
+errout:
+ if (rqp) {
+ smb_rq_done(rqp);
+ free(rqp);
+ }
+ return (ENOMEM);
}
void
@@ -77,81 +117,162 @@ smb_rq_done(struct smb_rq *rqp)
free(rqp);
}
+/*
+ * Reserve space for the word count, which is filled in later by
+ * smb_rq_wend(). Also initialize the counter that it uses
+ * to figure out what value to fill in.
+ *
+ * Note that the word count happens to be 8-bits,
+ * which can lead to confusion.
+ */
void
-smb_rq_wend(struct smb_rq *rqp)
+smb_rq_wstart(struct smb_rq *rqp)
{
- if (rqp->rq_rq.mb_count & 1)
- smb_error(dgettext(TEXT_DOMAIN,
- "smbrq_wend: odd word count\n"), 0);
- rqp->rq_wcount = rqp->rq_rq.mb_count / 2;
- rqp->rq_rq.mb_count = 0;
+ struct mbdata *mbp = &rqp->rq_rq;
+
+ mb_fit(mbp, 1, &rqp->rq_wcntp);
+ rqp->rq_wcbase = mbp->mb_count;
}
-int
-smb_rq_dmem(struct mbdata *mbp, const char *src, size_t size)
+/*
+ * Fill in the word count, in the space reserved by
+ * smb_rq_wstart().
+ */
+void
+smb_rq_wend(struct smb_rq *rqp)
{
- struct mbuf *m;
- char *dst;
- int cplen, error;
+ struct mbdata *mbp = &rqp->rq_rq;
+ int wcnt;
- if (size == 0)
- return (0);
- m = mbp->mb_cur;
- if ((error = m_getm(m, size, &m)) != 0)
- return (error);
- while (size > 0) {
- cplen = M_TRAILINGSPACE(m);
- if (cplen == 0) {
- m = m->m_next;
- continue;
- }
- if (cplen > (int)size)
- cplen = size;
- dst = mtod(m, char *) + m->m_len;
- nls_mem_toext(dst, src, cplen);
- size -= cplen;
- src += cplen;
- m->m_len += cplen;
- mbp->mb_count += cplen;
+ if (rqp->rq_wcntp == NULL) {
+ DPRINT("no wcount ptr\n");
+ return;
}
- mbp->mb_pos = mtod(m, char *) + m->m_len;
- mbp->mb_cur = m;
- return (0);
+ wcnt = mbp->mb_count - rqp->rq_wcbase;
+ if (wcnt > 0x1ff)
+ DPRINT("word count too large (%d)\n", wcnt);
+ if (wcnt & 1)
+ DPRINT("odd word count\n");
+ wcnt >>= 1;
+
+ /*
+ * Fill in the word count (8-bits).
+ * Also store it in the rq, in case
+ * we're using the ioctl path.
+ */
+ *rqp->rq_wcntp = (char)wcnt;
}
-int
-smb_rq_dstring(struct mbdata *mbp, const char *s)
+/*
+ * Reserve space for the byte count, which is filled in later by
+ * smb_rq_bend(). Also initialize the counter that it uses
+ * to figure out what value to fill in.
+ *
+ * Note that the byte count happens to be 16-bits,
+ * which can lead to confusion.
+ */
+void
+smb_rq_bstart(struct smb_rq *rqp)
+{
+ struct mbdata *mbp = &rqp->rq_rq;
+
+ mb_fit(mbp, 2, &rqp->rq_bcntp);
+ rqp->rq_bcbase = mbp->mb_count;
+}
+
+/*
+ * Fill in the byte count, in the space reserved by
+ * smb_rq_bstart().
+ */
+void
+smb_rq_bend(struct smb_rq *rqp)
{
- return (smb_rq_dmem(mbp, s, strlen(s) + 1));
+ struct mbdata *mbp = &rqp->rq_rq;
+ int bcnt;
+
+ if (rqp->rq_bcntp == NULL) {
+ DPRINT("no bcount ptr\n");
+ return;
+ }
+ bcnt = mbp->mb_count - rqp->rq_bcbase;
+ if (bcnt > 0xffff)
+ DPRINT("byte count too large (%d)\n", bcnt);
+ /*
+ * Fill in the byte count (16-bits).
+ * Also store it in the rq, in case
+ * we're using the ioctl path.
+ *
+ * The pointer is char * type due to
+ * typical off-by-one alignment.
+ */
+ rqp->rq_bcntp[0] = bcnt & 0xFF;
+ rqp->rq_bcntp[1] = (bcnt >> 8);
}
+/*
+ * Removed: smb_rq_dmem
+ * which was mostly like: mb_put_mem
+ */
+
int
smb_rq_simple(struct smb_rq *rqp)
{
struct smbioc_rq krq;
struct mbdata *mbp;
char *data;
- int i;
+ uint32_t len;
+ size_t rpbufsz;
+
+ bzero(&krq, sizeof (krq));
+ krq.ioc_cmd = rqp->rq_cmd;
+ /*
+ * Make the SMB request body contiguous,
+ * and fill in the ioctl request.
+ */
mbp = smb_rq_getrequest(rqp);
m_lineup(mbp->mb_top, &mbp->mb_top);
data = mtod(mbp->mb_top, char *);
- bzero(&krq, sizeof (krq));
- krq.ioc_cmd = rqp->rq_cmd;
- krq.ioc_twc = rqp->rq_wcount;
- krq.ioc_twords = data;
- krq.ioc_tbc = mbp->mb_count;
- krq.ioc_tbytes = data + rqp->rq_wcount * 2;
+ len = m_totlen(mbp->mb_top);
+ /*
+ * _rq_init left space for the SMB header,
+ * which makes mb_count the offset from
+ * the beginning of the header (useful).
+ * However, in this code path the driver
+ * prepends the header, so we skip it.
+ */
+ krq.ioc_tbufsz = len - SMB_HDRLEN;
+ krq.ioc_tbuf = data + SMB_HDRLEN;
+
+ /*
+ * Setup a buffer to hold the reply.
+ *
+ * Default size is M_MINSIZE, but the
+ * caller may increase rq_rpbufsz
+ * before calling this.
+ */
mbp = smb_rq_getreply(rqp);
- krq.ioc_rpbufsz = mbp->mb_top->m_maxlen;
- krq.ioc_rpbuf = mtod(mbp->mb_top, char *);
- if (ioctl(rqp->rq_ctx->ct_fd, SMBIOC_REQUEST, &krq) == -1) {
+ rpbufsz = rqp->rq_rpbufsz;
+ if (rpbufsz < M_MINSIZE)
+ rpbufsz = M_MINSIZE;
+ if (mb_init(mbp, rpbufsz))
+ return (ENOMEM);
+ krq.ioc_rbufsz = rpbufsz;
+ krq.ioc_rbuf = mtod(mbp->mb_top, char *);
+
+ /*
+ * Call the driver
+ */
+ if (ioctl(rqp->rq_ctx->ct_dev_fd, SMBIOC_REQUEST, &krq) == -1)
return (errno);
- }
- mbp->mb_top->m_len = krq.ioc_rwc * 2 + krq.ioc_rbc;
- rqp->rq_wcount = krq.ioc_rwc;
- rqp->rq_bcount = krq.ioc_rbc;
+
+ /*
+ * Initialize returned mbdata.
+ * SMB header already parsed.
+ */
+ mbp->mb_top->m_len = krq.ioc_rbufsz;
+
return (0);
}
@@ -167,13 +288,11 @@ smb_t2_request(struct smb_ctx *ctx, int setupcount, uint16_t *setup,
{
smbioc_t2rq_t *krq;
int i;
- char *pass;
-
krq = (smbioc_t2rq_t *)malloc(sizeof (smbioc_t2rq_t));
bzero(krq, sizeof (*krq));
- if (setupcount < 0 || setupcount >= SMB_MAXSETUPWORDS) {
+ if (setupcount < 0 || setupcount >= SMBIOC_T2RQ_MAXSETUP) {
/* Bogus setup count, or too many setup words */
return (EINVAL);
}
@@ -191,7 +310,7 @@ smb_t2_request(struct smb_ctx *ctx, int setupcount, uint16_t *setup,
krq->ioc_rparam = rparam;
krq->ioc_rdata = rdata;
- if (ioctl(ctx->ct_fd, SMBIOC_T2RQ, krq) == -1) {
+ if (ioctl(ctx->ct_dev_fd, SMBIOC_T2RQ, krq) == -1) {
return (errno);
}
@@ -200,5 +319,149 @@ smb_t2_request(struct smb_ctx *ctx, int setupcount, uint16_t *setup,
*buffer_oflow = (krq->ioc_rpflags2 & SMB_FLAGS2_ERR_STATUS) &&
(krq->ioc_error == NT_STATUS_BUFFER_OVERFLOW);
free(krq);
+
return (0);
}
+
+
+/*
+ * Do an over-the-wire call without using the nsmb driver.
+ * This is all "internal" to this library, and used only
+ * for connection setup (negotiate protocol, etc.)
+ */
+int
+smb_rq_internal(struct smb_ctx *ctx, struct smb_rq *rqp)
+{
+ static const uint8_t ffsmb[4] = SMB_SIGNATURE;
+ struct smb_iods *is = &ctx->ct_iods;
+ uint32_t sigbuf[2];
+ struct mbdata mbtmp, *mbp;
+ int err, save_mlen;
+ uint8_t ctmp;
+
+ rqp->rq_uid = is->is_smbuid;
+ rqp->rq_tid = SMB_TID_UNKNOWN;
+ rqp->rq_mid = is->is_next_mid++;
+
+ /*
+ * Fill in the NBT and SMB headers
+ * Using mbtmp so we can rewind without
+ * affecting the passed request mbdata.
+ */
+ bcopy(&rqp->rq_rq, &mbtmp, sizeof (mbtmp));
+ mbp = &mbtmp;
+ mbp->mb_cur = mbp->mb_top;
+ mbp->mb_pos = mbp->mb_cur->m_data;
+ mbp->mb_count = 0;
+ /* Have to save and restore m_len */
+ save_mlen = mbp->mb_cur->m_len;
+ mbp->mb_cur->m_len = 0;
+
+ /*
+ * rewind done; fill it in
+ */
+ mb_put_mem(mbp, (char *)SMB_SIGNATURE, SMB_SIGLEN);
+ mb_put_uint8(mbp, rqp->rq_cmd);
+ mb_put_mem(mbp, NULL, 4); /* status */
+ mb_put_uint8(mbp, rqp->rq_hflags);
+ mb_put_uint16le(mbp, rqp->rq_hflags2);
+ mb_put_uint16le(mbp, 0); /* pid_hi */
+ mb_put_mem(mbp, NULL, 8); /* signature */
+ mb_put_uint16le(mbp, 0); /* reserved */
+ mb_put_uint16le(mbp, rqp->rq_tid);
+ mb_put_uint16le(mbp, 0); /* pid_lo */
+ mb_put_uint16le(mbp, rqp->rq_uid);
+ mb_put_uint16le(mbp, rqp->rq_mid);
+
+ /* Restore original m_len */
+ mbp->mb_cur->m_len = save_mlen;
+
+ /*
+ * Sign the message, if flags2 indicates.
+ */
+ if (rqp->rq_hflags2 & SMB_FLAGS2_SECURITY_SIGNATURE) {
+ smb_rq_sign(rqp);
+ }
+
+ /*
+ * Send it, wait for the reply.
+ */
+ if ((err = smb_ssn_send(ctx, &rqp->rq_rq)) != 0)
+ return (err);
+
+ if ((err = smb_ssn_recv(ctx, &rqp->rq_rp)) != 0)
+ return (err);
+
+ /*
+ * Should have an SMB header, at least.
+ */
+ mbp = &rqp->rq_rp;
+ if (mbp->mb_cur->m_len < SMB_HDRLEN) {
+ DPRINT("len < 32");
+ return (EBADRPC);
+ }
+
+ /*
+ * If the request was signed, validate the
+ * signature on the response.
+ */
+ if (rqp->rq_hflags2 & SMB_FLAGS2_SECURITY_SIGNATURE) {
+ err = smb_rq_verify(rqp);
+ if (err) {
+ DPRINT("bad signature");
+ return (err);
+ }
+ }
+
+ /*
+ * Decode the SMB header.
+ */
+ mb_get_mem(mbp, (char *)sigbuf, 4);
+ if (0 != bcmp(sigbuf, ffsmb, 4)) {
+ DPRINT("not SMB");
+ return (EBADRPC);
+ }
+ mb_get_uint8(mbp, &ctmp); /* SMB cmd */
+ mb_get_uint32le(mbp, &rqp->rq_status);
+ mb_get_uint8(mbp, &rqp->rq_hflags);
+ mb_get_uint16le(mbp, &rqp->rq_hflags2);
+ mb_get_uint16le(mbp, NULL); /* pid_hi */
+ mb_get_mem(mbp, NULL, 8); /* signature */
+ mb_get_uint16le(mbp, NULL); /* reserved */
+ mb_get_uint16le(mbp, &rqp->rq_tid);
+ mb_get_uint16le(mbp, NULL); /* pid_lo */
+ mb_get_uint16le(mbp, &rqp->rq_uid);
+ mb_get_uint16le(mbp, &rqp->rq_mid);
+
+ /*
+ * Figure out the status return.
+ * Caller looks at rq_status.
+ */
+ if ((rqp->rq_hflags2 & SMB_FLAGS2_ERR_STATUS) == 0) {
+ uint16_t serr;
+ uint8_t class;
+
+ class = rqp->rq_status & 0xff;
+ serr = rqp->rq_status >> 16;
+ rqp->rq_status = smb_map_doserr(class, serr);
+ }
+
+ return (0);
+}
+
+/*
+ * Map old DOS errors (etc.) to NT status codes.
+ * We probably don't need this anymore, since
+ * the oldest server we talk to is NT. But if
+ * later find we do need this, add support here
+ * for the DOS errors we care about.
+ */
+static uint32_t
+smb_map_doserr(uint8_t class, uint16_t serr)
+{
+ if (class == 0 && serr == 0)
+ return (0);
+
+ DPRINT("class 0x%x serr 0x%x", (int)class, (int)serr);
+ return (NT_STATUS_UNSUCCESSFUL);
+}
diff --git a/usr/src/lib/libsmbfs/smb/signing.c b/usr/src/lib/libsmbfs/smb/signing.c
new file mode 100644
index 0000000000..ef9a32e2e0
--- /dev/null
+++ b/usr/src/lib/libsmbfs/smb/signing.c
@@ -0,0 +1,265 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Signing support, using libmd
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <strings.h>
+
+#include <sys/types.h>
+#include <sys/md5.h>
+
+#include <netsmb/mchain.h>
+#include <netsmb/smb.h>
+#include <netsmb/smb_lib.h>
+
+#include "private.h"
+
+#define SMBSIGOFF 14 /* SMB signature offset */
+#define SMBSIGLEN 8 /* SMB signature length */
+
+/*
+ * Set this to a small number to debug sequence numbers
+ * that seem to get out of step.
+ */
+#ifdef DEBUG
+int nsmb_signing_fudge = 4;
+#endif
+
+/*
+ * Compute MD5 digest of packet data, using the stored MAC key.
+ *
+ * See similar code in the driver:
+ * uts/common/fs/smbclnt/netsmb/smb_signing.c
+ * and on the server side:
+ * uts/common/fs/smbsrv/smb_signing.c
+ */
+static int
+smb_compute_MAC(struct smb_ctx *ctx, mbuf_t *m,
+ uint32_t seqno, uchar_t *signature)
+{
+ MD5_CTX md5;
+ uchar_t digest[MD5_DIGEST_LENGTH];
+
+ /*
+ * This union is a little bit of trickery to:
+ * (1) get the sequence number int aligned, and
+ * (2) reduce the number of digest calls, at the
+ * cost of a copying 32 bytes instead of 8.
+ * Both sides of this union are 2+32 bytes.
+ */
+ union {
+ struct {
+ uint8_t skip[2]; /* not used - just alignment */
+ uint8_t raw[SMB_HDRLEN]; /* header length (32) */
+ } r;
+ struct {
+ uint8_t skip[2]; /* not used - just alignment */
+ uint8_t hdr[SMBSIGOFF]; /* sig. offset (14) */
+ uint32_t sig[2]; /* MAC signature, aligned! */
+ uint16_t ids[5]; /* pad, Tid, Pid, Uid, Mid */
+ } s;
+ } smbhdr;
+
+ if (m->m_len < SMB_HDRLEN)
+ return (EIO);
+ if (ctx->ct_mackey == NULL)
+ return (EINVAL);
+
+ /*
+ * Make an aligned copy of the SMB header
+ * and fill in the sequence number.
+ */
+ bcopy(m->m_data, smbhdr.r.raw, SMB_HDRLEN);
+ smbhdr.s.sig[0] = htolel(seqno);
+ smbhdr.s.sig[1] = 0;
+
+ /*
+ * Compute the MAC: MD5(concat(Key, message))
+ */
+ MD5Init(&md5);
+
+ /* Digest the MAC Key */
+ MD5Update(&md5, ctx->ct_mackey, ctx->ct_mackeylen);
+
+ /* Digest the (copied) SMB header */
+ MD5Update(&md5, smbhdr.r.raw, SMB_HDRLEN);
+
+ /* Digest the rest of the first mbuf */
+ if (m->m_len > SMB_HDRLEN) {
+ MD5Update(&md5, m->m_data + SMB_HDRLEN,
+ m->m_len - SMB_HDRLEN);
+ }
+ m = m->m_next;
+
+ /* Digest rest of the SMB message. */
+ while (m) {
+ MD5Update(&md5, m->m_data, m->m_len);
+ m = m->m_next;
+ }
+
+ /* Final */
+ MD5Final(digest, &md5);
+
+ /*
+ * Finally, store the signature.
+ * (first 8 bytes of the digest)
+ */
+ if (signature)
+ bcopy(digest, signature, SMBSIGLEN);
+
+ return (0);
+}
+
+/*
+ * Sign a request with HMAC-MD5.
+ */
+int
+smb_rq_sign(struct smb_rq *rqp)
+{
+ struct smb_ctx *ctx = rqp->rq_ctx;
+ mbuf_t *m = rqp->rq_rq.mb_top;
+ uint8_t *sigloc;
+ int err;
+
+ /*
+ * Our mblk allocation ensures this,
+ * but just in case...
+ */
+ if (m->m_len < SMB_HDRLEN)
+ return (EIO);
+ sigloc = (uchar_t *)m->m_data + SMBSIGOFF;
+
+ if (ctx->ct_mackey == NULL) {
+ /*
+ * Signing is required, but we have no key yet
+ * fill in with the magic fake signing value.
+ * This happens with SPNEGO, NTLMSSP, ...
+ */
+ bcopy("BSRSPLY", sigloc, 8);
+ return (0);
+ }
+
+ /*
+ * This will compute the MAC and store it
+ * directly into the message at sigloc.
+ */
+ rqp->rq_seqno = ctx->ct_mac_seqno;
+ ctx->ct_mac_seqno += 2;
+ err = smb_compute_MAC(ctx, m, rqp->rq_seqno, sigloc);
+ if (err) {
+ DPRINT("compute MAC, err %d", err);
+ bzero(sigloc, SMBSIGLEN);
+ return (ENOTSUP);
+ }
+ return (0);
+}
+
+/*
+ * Verify reply signature.
+ */
+int
+smb_rq_verify(struct smb_rq *rqp)
+{
+ struct smb_ctx *ctx = rqp->rq_ctx;
+ mbuf_t *m = rqp->rq_rp.mb_top;
+ uint8_t sigbuf[SMBSIGLEN];
+ uint8_t *sigloc;
+ uint32_t rseqno;
+ int err, fudge;
+
+ /*
+ * Note ct_mackey and ct_mackeylen gets initialized by
+ * smb_smb_ssnsetup. It's normal to have a null MAC key
+ * during extended security session setup.
+ */
+ if (ctx->ct_mackey == NULL)
+ return (0);
+
+ /*
+ * Let caller deal with empty reply or short messages by
+ * returning zero. Caller will fail later, in parsing.
+ */
+ if (m == NULL) {
+ DPRINT("empty reply");
+ return (0);
+ }
+ if (m->m_len < SMB_HDRLEN) {
+ DPRINT("short reply");
+ return (0);
+ }
+
+ sigloc = (uchar_t *)m->m_data + SMBSIGOFF;
+ rseqno = rqp->rq_seqno + 1;
+
+ DPRINT("rq_rseqno = 0x%x", rseqno);
+
+ err = smb_compute_MAC(ctx, m, rseqno, sigbuf);
+ if (err) {
+ DPRINT("compute MAC, err %d", err);
+ /*
+ * If we can't compute a MAC, then there's
+ * no point trying other seqno values.
+ */
+ return (EBADRPC);
+ }
+
+ /*
+ * Compare the computed signature with the
+ * one found in the message (at sigloc)
+ */
+ if (bcmp(sigbuf, sigloc, SMBSIGLEN) == 0)
+ return (0);
+
+ DPRINT("BAD signature, MID=0x%x", rqp->rq_mid);
+
+#ifdef DEBUG
+ /*
+ * For diag purposes, we check whether the client/server idea
+ * of the sequence # has gotten a bit out of sync.
+ */
+ for (fudge = 1; fudge <= nsmb_signing_fudge; fudge++) {
+ smb_compute_MAC(ctx, m, rseqno + fudge, sigbuf);
+ if (bcmp(sigbuf, sigloc, SMBSIGLEN) == 0)
+ break;
+ smb_compute_MAC(ctx, m, rseqno - fudge, sigbuf);
+ if (bcmp(sigbuf, sigloc, SMBSIGLEN) == 0) {
+ fudge = -fudge;
+ break;
+ }
+ }
+ if (fudge <= nsmb_signing_fudge) {
+ DPRINT("rseqno=%d, but %d would have worked",
+ rseqno, rseqno + fudge);
+ }
+#endif
+ return (EBADRPC);
+}
diff --git a/usr/src/lib/libsmbfs/smb/smb_crypt.h b/usr/src/lib/libsmbfs/smb/smb_crypt.h
new file mode 100644
index 0000000000..15005ddab6
--- /dev/null
+++ b/usr/src/lib/libsmbfs/smb/smb_crypt.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2000-2001 Boris Popov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Boris Popov.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Various crypto stuff.
+ * from the driver: smb_crypt.c
+ */
+
+int
+smb_encrypt_DES(uchar_t *Result, int ResultLen,
+ const uchar_t *Key, int KeyLen,
+ const uchar_t *Data, int DataLen);
+
+int
+smb_get_urandom(void *data, size_t dlen);
diff --git a/usr/src/lib/libsmbfs/smb/spnegoparse.c b/usr/src/lib/libsmbfs/smb/spnegoparse.c
index 5da1983c27..e9f1e2781b 100644
--- a/usr/src/lib/libsmbfs/smb/spnegoparse.c
+++ b/usr/src/lib/libsmbfs/smb/spnegoparse.c
@@ -20,8 +20,6 @@
//
/////////////////////////////////////////////////////////////
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <stdlib.h>
#include <stdio.h>
#include <memory.h>
@@ -1615,7 +1613,7 @@ int ValidateMechList( unsigned char* pbMechListData, long nBoundaryLength )
int IsValidMechOid( SPNEGO_MECH_OID mechOid )
{
return ( mechOid >= spnego_mech_oid_Kerberos_V5_Legacy &&
- mechOid <= spnego_mech_oid_Spnego );
+ mechOid <= spnego_mech_oid_NTLMSSP );
}
/////////////////////////////////////////////////////////////////////////////
diff --git a/usr/src/lib/libsmbfs/smb/ssnsetup.c b/usr/src/lib/libsmbfs/smb/ssnsetup.c
new file mode 100644
index 0000000000..e4b5ec4f20
--- /dev/null
+++ b/usr/src/lib/libsmbfs/smb/ssnsetup.c
@@ -0,0 +1,519 @@
+/*
+ * Copyright (c) 2000-2001 Boris Popov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Boris Popov.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * SMB Session Setup, and related.
+ * Copied from the driver: smb_smb.c
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <strings.h>
+#include <netdb.h>
+#include <libintl.h>
+#include <xti.h>
+#include <assert.h>
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/byteorder.h>
+#include <sys/socket.h>
+#include <sys/fcntl.h>
+
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+
+#include <netsmb/mchain.h>
+#include <netsmb/netbios.h>
+#include <netsmb/smb_dev.h>
+#include <netsmb/smb.h>
+
+#include <netsmb/smb_lib.h>
+#include <netsmb/nb_lib.h>
+
+#include "private.h"
+#include "charsets.h"
+#include "ntlm.h"
+#include "smb_crypt.h"
+
+/*
+ * When we have a _real_ ntstatus.h, eliminate this.
+ * XXX: Current smb.h defines it without the high bits.
+ */
+#define STATUS_MORE_PROCESSING_REQUIRED 0xC0000016
+
+static int
+smb__ssnsetup(struct smb_ctx *ctx,
+ struct mbdata *mbc1, struct mbdata *mbc2,
+ uint32_t *statusp, uint16_t *actionp);
+
+/*
+ * Session Setup: NULL session (anonymous)
+ */
+int
+smb_ssnsetup_null(struct smb_ctx *ctx)
+{
+ int err;
+ uint32_t ntstatus;
+ uint16_t action = 0;
+
+ if (ctx->ct_sopt.sv_caps & SMB_CAP_EXT_SECURITY) {
+ /* Should not get here with... */
+ err = EINVAL;
+ goto out;
+ }
+
+ err = smb__ssnsetup(ctx, NULL, NULL, &ntstatus, &action);
+ if (err)
+ goto out;
+
+ DPRINT("status 0x%x action 0x%x", ntstatus, (int)action);
+ if (ntstatus != 0)
+ err = EAUTH;
+
+out:
+ return (err);
+}
+
+
+/*
+ * SMB Session Setup, using NTLMv1 (and maybe LMv1)
+ */
+int
+smb_ssnsetup_ntlm1(struct smb_ctx *ctx)
+{
+ struct mbdata lm_mbc, nt_mbc;
+ int err;
+ uint32_t ntstatus;
+ uint16_t action = 0;
+
+ if (ctx->ct_sopt.sv_caps & SMB_CAP_EXT_SECURITY) {
+ /* Should not get here with... */
+ err = EINVAL;
+ goto out;
+ }
+
+ /* Make mb_done calls at out safe. */
+ bzero(&lm_mbc, sizeof (lm_mbc));
+ bzero(&nt_mbc, sizeof (nt_mbc));
+
+ /* Put the LM,NTLM responses (as mbdata). */
+ err = ntlm_put_v1_responses(ctx, &lm_mbc, &nt_mbc);
+ if (err)
+ goto out;
+
+ /*
+ * If we negotiated signing, compute the MAC key
+ * and start signing messages, but only on the
+ * first non-null session login.
+ */
+ if ((ctx->ct_vcflags & SMBV_WILL_SIGN) &&
+ (ctx->ct_hflags2 & SMB_FLAGS2_SECURITY_SIGNATURE) == 0) {
+ struct mbuf *m = nt_mbc.mb_top;
+ char *p;
+
+ /*
+ * MAC_key = concat(session_key, nt_response)
+ */
+ ctx->ct_mackeylen = NTLM_HASH_SZ + m->m_len;
+ ctx->ct_mackey = malloc(ctx->ct_mackeylen);
+ if (ctx->ct_mackey == NULL) {
+ ctx->ct_mackeylen = 0;
+ err = ENOMEM;
+ goto out;
+ }
+ p = ctx->ct_mackey;
+ memcpy(p, ctx->ct_ssn_key, NTLM_HASH_SZ);
+ memcpy(p + NTLM_HASH_SZ, m->m_data, m->m_len);
+
+ /* OK, start signing! */
+ ctx->ct_hflags2 |= SMB_FLAGS2_SECURITY_SIGNATURE;
+ }
+
+ err = smb__ssnsetup(ctx, &lm_mbc, &nt_mbc, &ntstatus, &action);
+ if (err)
+ goto out;
+
+ DPRINT("status 0x%x action 0x%x", ntstatus, (int)action);
+ if (ntstatus != 0)
+ err = EAUTH;
+
+out:
+ mb_done(&lm_mbc);
+ mb_done(&nt_mbc);
+
+ return (err);
+}
+
+/*
+ * SMB Session Setup, using NTLMv2 (and LMv2)
+ */
+int
+smb_ssnsetup_ntlm2(struct smb_ctx *ctx)
+{
+ struct mbdata lm_mbc, nt_mbc, ti_mbc;
+ int err;
+ uint32_t ntstatus;
+ uint16_t action = 0;
+
+ if (ctx->ct_sopt.sv_caps & SMB_CAP_EXT_SECURITY) {
+ /* Should not get here with... */
+ err = EINVAL;
+ goto out;
+ }
+
+ /* Make mb_done calls at out safe. */
+ bzero(&lm_mbc, sizeof (lm_mbc));
+ bzero(&nt_mbc, sizeof (nt_mbc));
+ bzero(&ti_mbc, sizeof (ti_mbc));
+
+ /* Build the NTLMv2 "target info" blob (as mbdata) */
+ err = ntlm_build_target_info(ctx, NULL, &ti_mbc);
+ if (err)
+ goto out;
+
+ /* Put the LMv2, NTLMv2 responses (as mbdata). */
+ err = ntlm_put_v2_responses(ctx, &ti_mbc, &lm_mbc, &nt_mbc);
+ if (err)
+ goto out;
+
+ /*
+ * If we negotiated signing, compute the MAC key
+ * and start signing messages, but only on the
+ * first non-null session login.
+ */
+ if ((ctx->ct_vcflags & SMBV_WILL_SIGN) &&
+ (ctx->ct_hflags2 & SMB_FLAGS2_SECURITY_SIGNATURE) == 0) {
+ struct mbuf *m = nt_mbc.mb_top;
+ char *p;
+
+ /*
+ * MAC_key = concat(session_key, nt_response)
+ */
+ ctx->ct_mackeylen = NTLM_HASH_SZ + m->m_len;
+ ctx->ct_mackey = malloc(ctx->ct_mackeylen);
+ if (ctx->ct_mackey == NULL) {
+ ctx->ct_mackeylen = 0;
+ err = ENOMEM;
+ goto out;
+ }
+ p = ctx->ct_mackey;
+ memcpy(p, ctx->ct_ssn_key, NTLM_HASH_SZ);
+ memcpy(p + NTLM_HASH_SZ, m->m_data, m->m_len);
+
+ /* OK, start signing! */
+ ctx->ct_hflags2 |= SMB_FLAGS2_SECURITY_SIGNATURE;
+ }
+
+ err = smb__ssnsetup(ctx, &lm_mbc, &nt_mbc, &ntstatus, &action);
+ if (err)
+ goto out;
+
+ DPRINT("status 0x%x action 0x%x", ntstatus, (int)action);
+ if (ntstatus != 0)
+ err = EAUTH;
+
+out:
+ mb_done(&ti_mbc);
+ mb_done(&lm_mbc);
+ mb_done(&nt_mbc);
+
+ return (err);
+}
+
+int
+smb_ssnsetup_spnego(struct smb_ctx *ctx, struct mbdata *hint_mb)
+{
+ struct mbdata send_mb, recv_mb;
+ int err;
+ uint32_t ntstatus;
+ uint16_t action = 0;
+
+ err = ssp_ctx_create_client(ctx, hint_mb);
+ if (err)
+ goto out;
+
+ bzero(&send_mb, sizeof (send_mb));
+ bzero(&recv_mb, sizeof (recv_mb));
+
+ /* NULL input indicates first call. */
+ err = ssp_ctx_next_token(ctx, NULL, &send_mb);
+ if (err)
+ goto out;
+
+ for (;;) {
+ err = smb__ssnsetup(ctx, &send_mb, &recv_mb,
+ &ntstatus, &action);
+ if (err)
+ goto out;
+ if (ntstatus == 0)
+ break; /* normal loop termination */
+ if (ntstatus != STATUS_MORE_PROCESSING_REQUIRED) {
+ err = EAUTH;
+ break;
+ }
+
+ /* middle calls get both in, out */
+ err = ssp_ctx_next_token(ctx, &recv_mb, &send_mb);
+ if (err)
+ goto out;
+ }
+ DPRINT("status 0x%x action 0x%x", ntstatus, (int)action);
+
+ /* NULL output indicates last call. */
+ (void) ssp_ctx_next_token(ctx, &recv_mb, NULL);
+
+out:
+ ssp_ctx_destroy(ctx);
+
+ return (err);
+}
+
+/*
+ * Session Setup function used for all the forms we support.
+ * To allow this sharing, the crypto stuff is computed by
+ * callers and passed in as mbdata chains. Also, the args
+ * have different meanings for extended security vs. old.
+ * Some may be used as either IN or OUT parameters.
+ *
+ * For NTLM (v1, v2), all parameters are inputs
+ * mbc1: [in] LM password hash
+ * mbc2: [in] NT password hash
+ * For Extended security (spnego)
+ * mbc1: [in] outgoing blob data
+ * mbc2: [out] received blob data
+ * For both forms, these are optional:
+ * statusp: [out] NT status
+ * actionp: [out] Logon Action (i.e. SMB_ACT_GUEST)
+ */
+static int
+smb__ssnsetup(struct smb_ctx *ctx,
+ struct mbdata *mbc1, struct mbdata *mbc2,
+ uint32_t *statusp, uint16_t *actionp)
+{
+ static const char NativeOS[] = "Solaris";
+ static const char LanMan[] = "NETSMB";
+ struct smb_sopt *sv = &ctx->ct_sopt;
+ struct smb_iods *is = &ctx->ct_iods;
+ struct smb_rq *rqp = NULL;
+ struct mbdata *mbp;
+ struct mbuf *m;
+ int err, uc;
+ uint32_t caps;
+ uint16_t bc, len1, len2, sblen;
+ uint8_t wc;
+
+ /*
+ * Some of the "capability" bits we offer will be copied
+ * from those offered by the server, with a mask applied.
+ * This is the mask of capabilies copied from the server.
+ * Some others get special handling below.
+ */
+ static const uint32_t caps_mask =
+ SMB_CAP_UNICODE |
+ SMB_CAP_LARGE_FILES |
+ SMB_CAP_NT_SMBS |
+ SMB_CAP_STATUS32 |
+ SMB_CAP_EXT_SECURITY;
+
+ caps = ctx->ct_sopt.sv_caps & caps_mask;
+ uc = ctx->ct_hflags2 & SMB_FLAGS2_UNICODE;
+
+ err = smb_rq_init(ctx, SMB_COM_SESSION_SETUP_ANDX, &rqp);
+ if (err)
+ goto out;
+
+ /*
+ * Build the SMB request.
+ */
+ mbp = &rqp->rq_rq;
+ smb_rq_wstart(rqp);
+ mb_put_uint16le(mbp, 0xff); /* 0: AndXCommand */
+ mb_put_uint16le(mbp, 0); /* 1: AndXOffset */
+ mb_put_uint16le(mbp, sv->sv_maxtx); /* 2: MaxBufferSize */
+ mb_put_uint16le(mbp, sv->sv_maxmux); /* 3: MaxMpxCount */
+ mb_put_uint16le(mbp, 1); /* 4: VcNumber */
+ mb_put_uint32le(mbp, sv->sv_skey); /* 5,6: Session Key */
+
+ if (caps & SMB_CAP_EXT_SECURITY) {
+ len1 = mbc1 ? mbc1->mb_count : 0;
+ mb_put_uint16le(mbp, len1); /* 7: Sec. Blob Len */
+ mb_put_uint32le(mbp, 0); /* 8,9: reserved */
+ mb_put_uint32le(mbp, caps); /* 10,11: Capabilities */
+ smb_rq_wend(rqp); /* 12: Byte Count */
+ smb_rq_bstart(rqp);
+ if (mbc1 && mbc1->mb_top) {
+ mb_put_mbuf(mbp, mbc1->mb_top); /* sec. blob */
+ mbc1->mb_top = NULL; /* consumed */
+ }
+ /* mbc2 is required below */
+ if (mbc2 == NULL) {
+ err = EINVAL;
+ goto out;
+ }
+ } else {
+ len1 = mbc1 ? mbc1->mb_count : 0;
+ len2 = mbc2 ? mbc2->mb_count : 0;
+ mb_put_uint16le(mbp, len1); /* 7: LM pass. len */
+ mb_put_uint16le(mbp, len2); /* 8: NT pass. len */
+ mb_put_uint32le(mbp, 0); /* 9,10: reserved */
+ mb_put_uint32le(mbp, caps); /* 11,12: Capabilities */
+ smb_rq_wend(rqp); /* 13: Byte Count */
+ smb_rq_bstart(rqp);
+ if (mbc1 && mbc1->mb_top) {
+ mb_put_mbuf(mbp, mbc1->mb_top); /* LM password */
+ mbc1->mb_top = NULL; /* consumed */
+ }
+ if (mbc2 && mbc2->mb_top) {
+ mb_put_mbuf(mbp, mbc2->mb_top); /* NT password */
+ mbc2->mb_top = NULL; /* consumed */
+ }
+ mb_put_dstring(mbp, ctx->ct_user, uc);
+ mb_put_dstring(mbp, ctx->ct_domain, uc);
+ }
+ mb_put_dstring(mbp, NativeOS, uc);
+ mb_put_dstring(mbp, LanMan, uc);
+ smb_rq_bend(rqp);
+
+ err = smb_rq_internal(ctx, rqp);
+ if (err)
+ goto out;
+
+ if (statusp)
+ *statusp = rqp->rq_status;
+
+ /*
+ * If we have a real error, the response probably has
+ * no more data, so don't try to parse any more.
+ * Note: err=0, means rq_status is valid.
+ */
+ if (rqp->rq_status != 0 &&
+ rqp->rq_status != STATUS_MORE_PROCESSING_REQUIRED) {
+ goto out;
+ }
+
+ /*
+ * Parse the reply
+ */
+ uc = rqp->rq_hflags2 & SMB_FLAGS2_UNICODE;
+ is->is_smbuid = rqp->rq_uid;
+ mbp = &rqp->rq_rp;
+
+ err = mb_get_uint8(mbp, &wc);
+ if (err)
+ goto out;
+
+ err = EBADRPC; /* for any problems in this section */
+ if (caps & SMB_CAP_EXT_SECURITY) {
+ if (wc != 4)
+ goto out;
+ mb_get_uint16le(mbp, NULL); /* secondary cmd */
+ mb_get_uint16le(mbp, NULL); /* andxoffset */
+ mb_get_uint16le(mbp, actionp); /* action */
+ mb_get_uint16le(mbp, &sblen); /* sec. blob len */
+ mb_get_uint16le(mbp, &bc); /* byte count */
+ /*
+ * Get the security blob, after
+ * sanity-checking the length.
+ */
+ if (sblen == 0 || bc < sblen)
+ goto out;
+ err = mb_get_mbuf(mbp, sblen, &m);
+ if (err)
+ goto out;
+ mb_initm(mbc2, m);
+ mbc2->mb_count = sblen;
+ } else {
+ if (wc != 3)
+ goto out;
+ mb_get_uint16le(mbp, NULL); /* secondary cmd */
+ mb_get_uint16le(mbp, NULL); /* andxoffset */
+ mb_get_uint16le(mbp, actionp); /* action */
+ err = mb_get_uint16le(mbp, &bc); /* byte count */
+ if (err)
+ goto out;
+ }
+
+ /*
+ * Native OS, LANMGR, & Domain follow here.
+ * Parse these strings and store for later.
+ * If unicode, they should be aligned.
+ *
+ * Note that with Extended security, we may use
+ * multiple calls to this function. Only parse
+ * these strings on the last one (status == 0).
+ * Ditto for the CAP_LARGE work-around.
+ */
+ if (rqp->rq_status != 0)
+ goto out;
+
+ /* Ignore any parsing errors for these strings. */
+ err = mb_get_string(mbp, &ctx->ct_srv_OS, uc);
+ DPRINT("server OS: %s", err ? "?" : ctx->ct_srv_OS);
+ err = mb_get_string(mbp, &ctx->ct_srv_LM, uc);
+ DPRINT("server LM: %s", err ? "?" : ctx->ct_srv_LM);
+ /*
+ * There's sometimes a server domain folloing
+ * at this point, but we don't need it.
+ */
+
+ /* Success! (See Ignore any ... above) */
+ err = 0;
+
+ /*
+ * Windows systems don't suport CAP_LARGE_READX,WRITEX
+ * when signing is enabled, so adjust sv_caps.
+ */
+ if (ctx->ct_srv_OS &&
+ 0 == strncmp(ctx->ct_srv_OS, "Windows ", 8)) {
+ DPRINT("Server is Windows");
+ if (ctx->ct_vcflags & SMBV_WILL_SIGN) {
+ DPRINT("disable CAP_LARGE_(r/w)");
+ ctx->ct_sopt.sv_caps &=
+ ~(SMB_CAP_LARGE_READX | SMB_CAP_LARGE_WRITEX);
+ }
+ }
+
+out:
+ if (rqp)
+ smb_rq_done(rqp);
+
+ return (err);
+}
diff --git a/usr/src/lib/libsmbfs/smb/ssp.c b/usr/src/lib/libsmbfs/smb/ssp.c
new file mode 100644
index 0000000000..f8433ba8e5
--- /dev/null
+++ b/usr/src/lib/libsmbfs/smb/ssp.c
@@ -0,0 +1,395 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Security Provider glue
+ *
+ * Modeled after SSPI for now, only because we're currently
+ * using the Microsoft sample spnego code.
+ *
+ * ToDo: Port all of this to GSS-API plugins.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <strings.h>
+#include <netdb.h>
+#include <libintl.h>
+#include <xti.h>
+#include <assert.h>
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/byteorder.h>
+#include <sys/socket.h>
+#include <sys/fcntl.h>
+
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+
+#include <netsmb/smb_lib.h>
+#include <netsmb/mchain.h>
+
+#include "private.h"
+#include "charsets.h"
+#include "spnego.h"
+#include "derparse.h"
+#include "ssp.h"
+
+
+/*
+ * ssp_ctx_create_client
+ *
+ * This is the first function called for SMB "extended security".
+ * Here we select a security support provider (SSP), or mechanism,
+ * and build the security context used throughout authentication.
+ *
+ * Note that we receive a "hint" in the SMB Negotiate response
+ * that contains the list of mechanisms supported by the server.
+ * We use this to help us select a mechanism.
+ *
+ * With SSPI this would call:
+ * ssp->InitSecurityInterface()
+ * ssp->AcquireCredentialsHandle()
+ * ssp->InitializeSecurityContext()
+ * With GSS-API this will become:
+ * gss_import_name(... service_principal_name)
+ * gss_init_sec_context(), etc.
+ */
+int
+ssp_ctx_create_client(struct smb_ctx *ctx, struct mbdata *hint_mb)
+{
+ struct ssp_ctx *sp;
+ mbuf_t *m;
+ SPNEGO_MECH_OID oid;
+ int indx, rc;
+ int err = ENOTSUP; /* in case nothing matches */
+
+ sp = malloc(sizeof (*sp));
+ if (sp == NULL)
+ return (ENOMEM);
+ bzero(sp, sizeof (*sp));
+ ctx->ct_ssp_ctx = sp;
+ sp->smb_ctx = ctx;
+
+ /*
+ * Parse the SPNEGO "hint" to get the server's list of
+ * supported mechanisms. If the "hint" is empty,
+ * assume NTLMSSP. (Or could use "raw NTLMSSP")
+ */
+ m = hint_mb->mb_top;
+ if (m == NULL)
+ goto use_ntlm;
+ rc = spnegoInitFromBinary((uchar_t *)m->m_data, m->m_len,
+ &sp->sp_hint);
+ if (rc) {
+ DPRINT("parse hint, rc %d", rc);
+ goto use_ntlm;
+ }
+
+ /*
+ * Did the server offer Kerberos?
+ * Either spec. OID or legacy is OK,
+ * but have to remember what we got.
+ */
+ oid = spnego_mech_oid_NotUsed;
+ if (0 == spnegoIsMechTypeAvailable(sp->sp_hint,
+ spnego_mech_oid_Kerberos_V5, &indx))
+ oid = spnego_mech_oid_Kerberos_V5;
+ else if (0 == spnegoIsMechTypeAvailable(sp->sp_hint,
+ spnego_mech_oid_Kerberos_V5_Legacy, &indx))
+ oid = spnego_mech_oid_Kerberos_V5_Legacy;
+ if (oid != spnego_mech_oid_NotUsed) {
+ /*
+ * Yes! Server offers Kerberos.
+ * Try to init our krb5 mechanism.
+ * It will fail if the calling user
+ * does not have krb5 credentials.
+ */
+ sp->sp_mech = oid;
+ err = krb5ssp_init_client(sp);
+ if (err == 0) {
+ DPRINT("using Kerberos");
+ return (0);
+ }
+ /* else fall back to NTLMSSP */
+ }
+
+ /*
+ * Did the server offer NTLMSSP?
+ */
+ if (0 == spnegoIsMechTypeAvailable(sp->sp_hint,
+ spnego_mech_oid_NTLMSSP, &indx)) {
+ /*
+ * OK, we'll use NTLMSSP
+ */
+ use_ntlm:
+ sp->sp_mech = spnego_mech_oid_NTLMSSP;
+ err = ntlmssp_init_client(sp);
+ if (err == 0) {
+ DPRINT("using NTLMSSP");
+ return (0);
+ }
+ }
+
+ /* No supported mechanisms! */
+ return (err);
+}
+
+
+/*
+ * ssp_ctx_destroy
+ *
+ * Dispatch to the mechanism-specific destroy.
+ */
+void
+ssp_ctx_destroy(struct smb_ctx *ctx)
+{
+ ssp_ctx_t *sp;
+
+ sp = ctx->ct_ssp_ctx;
+ ctx->ct_ssp_ctx = NULL;
+
+ if (sp == NULL)
+ return;
+
+ if (sp->sp_destroy != NULL)
+ (sp->sp_destroy)(sp);
+
+ if (sp->sp_hint != NULL)
+ spnegoFreeData(sp->sp_hint);
+
+ free(sp);
+}
+
+
+/*
+ * ssp_ctx_next_token
+ *
+ * This is the function called to generate the next token to send,
+ * given a token just received, using the selected back-end method.
+ * The back-end method is called a security service provider (SSP).
+ *
+ * This is also called to generate the first token to send
+ * (when called with caller_in == NULL) and to handle the last
+ * token received (when called with caller_out == NULL).
+ * See caller: smb_ssnsetup_spnego
+ *
+ * Note that if the back-end SSP "next token" function ever
+ * returns an error, the conversation ends, and there are
+ * no further calls to this function for this context.
+ *
+ * General outline of this funcion:
+ * if (caller_in)
+ * Unwrap caller_in spnego blob,
+ * store payload in body_in
+ * Call back-end SSP "next token" method (body_in, body_out)
+ * if (caller_out)
+ * Wrap returned body_out in spnego,
+ * store in caller_out
+ *
+ * With SSPI this would call:
+ * ssp->InitializeSecurityContext()
+ * With GSS-API this will become:
+ * gss_init_sec_context()
+ */
+int
+ssp_ctx_next_token(struct smb_ctx *ctx,
+ struct mbdata *caller_in,
+ struct mbdata *caller_out)
+{
+ struct mbdata body_in, body_out;
+ SPNEGO_TOKEN_HANDLE stok_in, stok_out;
+ SPNEGO_NEGRESULT result;
+ ssp_ctx_t *sp;
+ struct mbuf *m;
+ ulong_t toklen;
+ int err, rc;
+
+ bzero(&body_in, sizeof (body_in));
+ bzero(&body_out, sizeof (body_out));
+ stok_out = stok_in = NULL;
+ sp = ctx->ct_ssp_ctx;
+
+ /*
+ * If we have an spnego input token, parse it,
+ * extract the payload for the back-end SSP.
+ */
+ if (caller_in != NULL) {
+
+ /*
+ * Let the spnego code parse it.
+ */
+ m = caller_in->mb_top;
+ rc = spnegoInitFromBinary((uchar_t *)m->m_data,
+ m->m_len, &stok_in);
+ if (rc) {
+ DPRINT("parse reply, rc %d", rc);
+ err = EBADRPC;
+ goto out;
+ }
+ /* Note: Allocated stok_in */
+
+ /*
+ * Now get the payload. Two calls:
+ * first gets the size, 2nd the data.
+ *
+ * Expect SPNEGO_E_BUFFER_TOO_SMALL here,
+ * but if the payload is missing, we'll
+ * get SPNEGO_E_ELEMENT_UNAVAILABLE.
+ */
+ rc = spnegoGetMechToken(stok_in, NULL, &toklen);
+ switch (rc) {
+ case SPNEGO_E_ELEMENT_UNAVAILABLE:
+ toklen = 0;
+ break;
+ case SPNEGO_E_BUFFER_TOO_SMALL:
+ /* have toklen */
+ break;
+ default:
+ DPRINT("GetMechTok1, rc %d", rc);
+ err = EBADRPC;
+ goto out;
+ }
+ err = mb_init(&body_in, (size_t)toklen);
+ if (err)
+ goto out;
+ m = body_in.mb_top;
+ if (toklen > 0) {
+ rc = spnegoGetMechToken(stok_in,
+ (uchar_t *)m->m_data, &toklen);
+ if (rc) {
+ DPRINT("GetMechTok2, rc %d", rc);
+ err = EBADRPC;
+ goto out;
+ }
+ body_in.mb_count = m->m_len = (size_t)toklen;
+ }
+ }
+
+ /*
+ * Call the back-end security provider (SSP) to
+ * handle the received token (if present) and
+ * generate an output token (if requested).
+ */
+ err = sp->sp_nexttok(sp,
+ caller_in ? &body_in : NULL,
+ caller_out ? &body_out : NULL);
+ if (err)
+ goto out;
+
+ /*
+ * Wrap the outgoing body if requested,
+ * either negTokenInit on first call, or
+ * negTokenTarg on subsequent calls.
+ */
+ if (caller_out != NULL) {
+ m = body_out.mb_top;
+
+ if (caller_in == NULL) {
+ /*
+ * This is the first call, so create a
+ * negTokenInit.
+ */
+ rc = spnegoCreateNegTokenInit(
+ sp->sp_mech, 0,
+ (uchar_t *)m->m_data, m->m_len,
+ NULL, 0, &stok_out);
+ /* Note: allocated stok_out */
+ } else {
+ /*
+ * Note: must pass spnego_mech_oid_NotUsed,
+ * instead of sp->sp_mech so that the spnego
+ * code will not marshal a mech OID list.
+ * The mechanism is determined at this point,
+ * and some servers won't parse an unexpected
+ * mech. OID list in a negTokenTarg
+ */
+ rc = spnegoCreateNegTokenTarg(
+ spnego_mech_oid_NotUsed,
+ spnego_negresult_NotUsed,
+ (uchar_t *)m->m_data, m->m_len,
+ NULL, 0, &stok_out);
+ /* Note: allocated stok_out */
+ }
+ if (rc) {
+ DPRINT("CreateNegTokenX, rc 0x%x", rc);
+ err = EBADRPC;
+ goto out;
+ }
+
+ /*
+ * Copy binary from stok_out to caller_out
+ * Two calls: get the size, get the data.
+ */
+ rc = spnegoTokenGetBinary(stok_out, NULL, &toklen);
+ if (rc != SPNEGO_E_BUFFER_TOO_SMALL) {
+ DPRINT("GetBinary1, rc 0x%x", rc);
+ err = EBADRPC;
+ goto out;
+ }
+ err = mb_init(caller_out, (size_t)toklen);
+ if (err)
+ goto out;
+ m = caller_out->mb_top;
+ rc = spnegoTokenGetBinary(stok_out,
+ (uchar_t *)m->m_data, &toklen);
+ if (rc) {
+ DPRINT("GetBinary2, rc 0x%x", rc);
+ err = EBADRPC;
+ goto out;
+ }
+ caller_out->mb_count = m->m_len = (size_t)toklen;
+ } else {
+ /*
+ * caller_out == NULL, so this is the "final" call.
+ * Get final SPNEGO result from the INPUT token.
+ */
+ rc = spnegoGetNegotiationResult(stok_in, &result);
+ if (rc) {
+ DPRINT("rc 0x%x", rc);
+ err = EBADRPC;
+ goto out;
+ }
+ DPRINT("spnego result: 0x%x", result);
+ if (result != spnego_negresult_success) {
+ err = EAUTH;
+ goto out;
+ }
+ }
+ err = 0;
+
+out:
+ mb_done(&body_in);
+ mb_done(&body_out);
+ spnegoFreeData(stok_in);
+ spnegoFreeData(stok_out);
+
+ return (err);
+}
diff --git a/usr/src/lib/libsmbfs/smb/ssp.h b/usr/src/lib/libsmbfs/smb/ssp.h
new file mode 100644
index 0000000000..7370fffc9c
--- /dev/null
+++ b/usr/src/lib/libsmbfs/smb/ssp.h
@@ -0,0 +1,56 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _SSP_H
+#define _SSP_H
+
+/*
+ * Security Support Package (SSP) interface,
+ * somewhat modeled on Microsoft's SSPI.
+ *
+ * XXX: Yes, should use GSS-API. See ssp.c
+ */
+
+typedef struct ssp_ctx {
+ struct smb_ctx *smb_ctx;
+
+ SPNEGO_TOKEN_HANDLE sp_hint;
+ SPNEGO_MECH_OID sp_mech;
+
+ /*
+ * Now the mechanism-specific stuff.
+ */
+ int (*sp_nexttok)(struct ssp_ctx *,
+ struct mbdata *, struct mbdata *);
+ void (*sp_destroy)(struct ssp_ctx *);
+ void *sp_private;
+
+} ssp_ctx_t;
+
+int ntlmssp_init_client(ssp_ctx_t *);
+int krb5ssp_init_client(ssp_ctx_t *);
+
+#endif /* _SSP_H */
diff --git a/usr/src/lib/libsmbfs/smb/subr.c b/usr/src/lib/libsmbfs/smb/subr.c
index 36a3c30ed9..0b81606f2c 100644
--- a/usr/src/lib/libsmbfs/smb/subr.c
+++ b/usr/src/lib/libsmbfs/smb/subr.c
@@ -49,15 +49,13 @@
#include <netsmb/netbios.h>
#include <netsmb/smb_lib.h>
#include <netsmb/nb_lib.h>
-#include <cflib.h>
+
#include <err.h>
-uid_t real_uid, eff_uid;
+#include "private.h"
static int smblib_initialized;
-struct rcfile *smb_rc;
-
int
smb_lib_init(void)
{
@@ -74,6 +72,23 @@ smb_lib_init(void)
return (0);
}
+int
+smb_getlocalname(char **namepp)
+{
+ char buf[SMBIOC_MAX_NAME], *cp;
+
+ if (gethostname(buf, sizeof (buf)) != 0)
+ return (errno);
+ cp = strchr(buf, '.');
+ if (cp)
+ *cp = '\0';
+ cp = strdup(buf);
+ if (cp == NULL)
+ return (ENOMEM);
+ *namepp = cp;
+ return (0);
+}
+
/*
* Private version of strerror(3C) that
* knows our special error codes.
@@ -162,93 +177,6 @@ smb_printb(char *dest, int flags, const struct smb_bitname *bnp) {
return (dest);
}
-extern int home_nsmbrc;
-
-#ifdef DEBUG
-#include "queue.h"
-#include "rcfile_priv.h"
-
-struct rcsection *rc_findsect(struct rcfile *rcp, const char *sectname);
-struct rckey *rc_sect_findkey(struct rcsection *rsp, const char *keyname);
-
-void
-dump_props(char *where)
-{
- struct rcsection *rsp = NULL;
- struct rckey *rkp = NULL;
-
- printf("Settings %s\n", where);
- SLIST_FOREACH(rsp, &smb_rc->rf_sect, rs_next) {
- printf("section=%s\n", rsp->rs_name);
- fflush(stdout);
-
- SLIST_FOREACH(rkp, &rsp->rs_keys, rk_next) {
- printf(" key=%s, value=%s\n",
- rkp->rk_name, rkp->rk_value);
- fflush(stdout);
- }
- }
-}
-#endif
-
-/*
- * first read ~/.smbrc, next try to merge SMB_CFG_FILE - if that fails
- * because SMB_CFG_FILE doesn't exist, try to merge OLD_SMB_CFG_FILE
- */
-int
-smb_open_rcfile(struct smb_ctx *ctx)
-{
- char *home, *fn;
- int error, len;
-
- smb_rc = NULL;
-#ifdef DEPRECATED
- fn = SMB_CFG_FILE;
- error = rc_merge(fn, &smb_rc);
- if (error == ENOENT) {
- /*
- * OK, try to read a config file in the old location.
- */
- fn = OLD_SMB_CFG_FILE;
- error = rc_merge(fn, &smb_rc);
- }
-#endif
- fn = "/usr/sbin/sharectl get smbfs";
- error = rc_merge_pipe(fn, &smb_rc);
- if (error != 0 && error != ENOENT)
- fprintf(stderr, dgettext(TEXT_DOMAIN,
- "Can't open %s: %s\n"), fn, smb_strerror(errno));
-#ifdef DEBUG
- if (smb_debug)
- dump_props("after reading global repository");
-#endif
-
- home = getenv("HOME");
- if (home == NULL && ctx && ctx->ct_home)
- home = ctx->ct_home;
- if (home) {
- len = strlen(home) + 20;
- fn = malloc(len);
- snprintf(fn, len, "%s/.nsmbrc", home);
- home_nsmbrc = 1;
- error = rc_merge(fn, &smb_rc);
- if (error != 0 && error != ENOENT) {
- fprintf(stderr, dgettext(TEXT_DOMAIN,
- "Can't open %s: %s\n"), fn, smb_strerror(errno));
- }
- free(fn);
- }
- home_nsmbrc = 0;
-#ifdef DEBUG
- if (smb_debug)
- dump_props("after reading user settings");
-#endif
- if (smb_rc == NULL) {
- return (ENOENT);
- }
- return (0);
-}
-
void
smb_simplecrypt(char *dst, const char *src)
{
@@ -303,6 +231,79 @@ smb_simpledecrypt(char *dst, const char *src)
return (0);
}
+/*
+ * Number of seconds between 1970 and 1601 year
+ * (134774 * 24 * 60 * 60)
+ */
+static const uint64_t DIFF1970TO1601 = 11644473600ULL;
+
+void
+smb_time_local2server(struct timeval *tsp, int tzoff, long *seconds)
+{
+ *seconds = tsp->tv_sec - tzoff * 60;
+}
+
+void
+smb_time_server2local(ulong_t seconds, int tzoff, struct timeval *tsp)
+{
+ tsp->tv_sec = seconds + tzoff * 60;
+ tsp->tv_usec = 0;
+}
+
+/*
+ * Time from server comes as UTC, so no need to use tz
+ */
+/*ARGSUSED*/
+void
+smb_time_NT2local(uint64_t nsec, int tzoff, struct timeval *tsp)
+{
+ smb_time_server2local(nsec / 10000000 - DIFF1970TO1601, 0, tsp);
+}
+
+/*ARGSUSED*/
+void
+smb_time_local2NT(struct timeval *tsp, int tzoff, uint64_t *nsec)
+{
+ long seconds;
+
+ smb_time_local2server(tsp, 0, &seconds);
+ *nsec = (((uint64_t)(seconds) & ~1) + DIFF1970TO1601) *
+ (uint64_t)10000000;
+}
+
+void
+smb_hexdump(const void *buf, int len)
+{
+ const uchar_t *p = buf;
+ int ofs = 0;
+
+ while (len--) {
+ if (ofs % 16 == 0)
+ fprintf(stderr, "%02X: ", ofs);
+ fprintf(stderr, "%02x ", *p++);
+ ofs++;
+ if (ofs % 16 == 0)
+ fprintf(stderr, "\n");
+ }
+ if (ofs % 16 != 0)
+ fprintf(stderr, "\n");
+}
+
+void
+dprint(const char *fname, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+
+ if (smb_debug) {
+ fprintf(stderr, "%s: ", fname);
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+ }
+ va_end(ap);
+}
+
#undef __progname
char *__progname = NULL;
diff --git a/usr/src/lib/libsmbfs/smb/ui-sun.c b/usr/src/lib/libsmbfs/smb/ui-sun.c
index 7512d2c964..69aa3161ab 100644
--- a/usr/src/lib/libsmbfs/smb/ui-sun.c
+++ b/usr/src/lib/libsmbfs/smb/ui-sun.c
@@ -22,8 +22,6 @@
* @APPLE_LICENSE_HEADER_END@
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
/*
* Routines for interacting with the user to get credentials
* (workgroup/domain, username, password, etc.)
@@ -38,11 +36,11 @@
#include <ctype.h>
#include <netsmb/smb_lib.h>
-#include <netsmb/smb_keychain.h>
+#include "private.h"
+#include "ntlm.h"
+#if 0 /* not yet */
#define MAXLINE 127
-#define MAXPASSWD 256 /* from libc:getpass */
-
static void
smb_tty_prompt(char *prmpt,
char *buf, size_t buflen)
@@ -72,78 +70,69 @@ smb_tty_prompt(char *prmpt,
/* Use input as new value. */
strncpy(buf, temp, buflen);
}
+#endif /* not yet */
+/*
+ * Prompt for a new password after auth. failure.
+ * (and maybe new user+domain, but not yet)
+ */
int
-smb_get_authentication(
- char *dom, size_t domlen,
- char *usr, size_t usrlen,
- char *passwd, size_t passwdlen,
- const char *systemname, struct smb_ctx *ctx)
+smb_get_authentication(struct smb_ctx *ctx)
{
char *npw;
- int error, i, kcask, kcerr;
+ int err;
- if (ctx->ct_flags & SMBCF_KCFOUND || ctx->ct_flags & SMBCF_KCBAD) {
- ctx->ct_flags &= ~SMBCF_KCFOUND;
- } else {
- ctx->ct_flags &= ~(SMBCF_KCFOUND | SMBCF_KCDOMAIN);
+ /*
+ * If we're getting a password, we must be doing
+ * some kind of NTLM, possibly after a failure to
+ * authenticate using Kerberos. Turn off krb5.
+ */
+ ctx->ct_authflags &= ~SMB_AT_KRB5;
- /*
- * 1st: try lookup using system name
- */
- kcerr = smbfs_keychain_chk(systemname, usr);
- if (!kcerr) {
- /*
- * Need passwd to be not empty for existing logic.
- * The string here is arbitrary (a debugging hint)
- * and will be replaced in the driver by the real
- * password from the keychain.
- */
- strcpy(passwd, "$KC_SYSTEM");
- ctx->ct_flags |= SMBCF_KCFOUND;
- if (smb_debug) {
- printf("found keychain entry for"
- " server/user: %s/%s\n",
- systemname, usr);
- }
- return (0);
- }
+ if (ctx->ct_flags & SMBCF_KCFOUND) {
+ /* Tried a keychain hash and failed. */
+ /* XXX: delete the KC entry? */
+ ctx->ct_flags |= SMBCF_KCBAD;
+ }
+
+ if (ctx->ct_flags & SMBCF_NOPWD)
+ return (ENOTTY);
+
+ if (isatty(STDIN_FILENO)) {
+
+ /* Need command-line prompting. */
+ npw = getpassphrase(dgettext(TEXT_DOMAIN, "Password:"));
+ if (npw == NULL)
+ return (EINTR);
+ memset(ctx->ct_password, 0, sizeof (ctx->ct_password));
+ strlcpy(ctx->ct_password, npw, sizeof (ctx->ct_password));
+ } else {
/*
- * 2nd: try lookup using domain name
+ * XXX: Ask the user for help, possibly via
+ * GNOME dbus or some such... (todo).
*/
- kcerr = smbfs_keychain_chk(dom, usr);
- if (!kcerr) {
- /* Need passwd to be not empty... (see above) */
- strcpy(passwd, "$KC_DOMAIN");
- ctx->ct_flags |= (SMBCF_KCFOUND | SMBCF_KCDOMAIN);
- if (smb_debug) {
- printf("found keychain entry for"
- " domain/user: %s/%s\n",
- dom, usr);
- }
- return (0);
- }
- }
-
- if (isatty(STDIN_FILENO)) { /* need command-line prompting? */
- if (passwd && passwd[0] == '\0') {
- npw = getpassphrase(dgettext(TEXT_DOMAIN, "Password:"));
- strncpy(passwd, npw, passwdlen);
- }
- return (0);
+ smb_error(dgettext(TEXT_DOMAIN,
+ "Cannot prompt for a password when input is redirected."), 0);
+ return (ENOTTY);
}
/*
- * XXX: Ask the user for help, possibly via
- * GNOME dbus or some such... (todo).
+ * Recompute the password hashes.
*/
- smb_error(dgettext(TEXT_DOMAIN,
- "Cannot prompt for a password when input is redirected."), 0);
+ if (ctx->ct_password[0]) {
+ err = ntlm_compute_lm_hash(ctx->ct_lmhash, ctx->ct_password);
+ if (err != 0)
+ return (err);
+ err = ntlm_compute_nt_hash(ctx->ct_nthash, ctx->ct_password);
+ if (err != 0)
+ return (err);
+ }
- return (ENOTTY);
+ return (0);
}
+/*ARGSUSED*/
int
smb_browse(struct smb_ctx *ctx, int anon)
{
diff --git a/usr/src/lib/libsmbfs/smb/utf_str.c b/usr/src/lib/libsmbfs/smb/utf_str.c
index e80b1180f2..f53189e64c 100644
--- a/usr/src/lib/libsmbfs/smb/utf_str.c
+++ b/usr/src/lib/libsmbfs/smb/utf_str.c
@@ -20,7 +20,7 @@
*/
/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -205,3 +205,67 @@ convert_utf8_to_ucs2xx(iconv_t cd, const char *utf8_string)
return (obuf);
}
+
+
+/*
+ * A simple wrapper around u8_textprep_str() that returns the Unicode
+ * upper-case version of some string. Returns memory from malloc.
+ * Borrowed from idmapd.
+ */
+static char *
+utf8_str_to_upper_or_lower(const char *s, int upper_lower)
+{
+ char *res = NULL;
+ char *outs;
+ size_t inlen, outlen, inbleft, outbleft;
+ int rc, err;
+
+ /*
+ * u8_textprep_str() does not allocate memory. The input and
+ * output buffers may differ in size (though that would be more
+ * likely when normalization is done). We have to loop over it...
+ *
+ * To improve the chances that we can avoid looping we add 10
+ * bytes of output buffer room the first go around.
+ */
+ inlen = inbleft = strlen(s);
+ outlen = outbleft = inlen + 10;
+ if ((res = malloc(outlen)) == NULL)
+ return (NULL);
+ outs = res;
+
+ while ((rc = u8_textprep_str((char *)s, &inbleft, outs,
+ &outbleft, upper_lower, U8_UNICODE_LATEST, &err)) < 0 &&
+ err == E2BIG) {
+ if ((res = realloc(res, outlen + inbleft)) == NULL)
+ return (NULL);
+ /* adjust input/output buffer pointers */
+ s += (inlen - inbleft);
+ outs = res + outlen - outbleft;
+ /* adjust outbleft and outlen */
+ outlen += inbleft;
+ outbleft += inbleft;
+ }
+
+ if (rc < 0) {
+ free(res);
+ res = NULL;
+ return (NULL);
+ }
+
+ res[outlen - outbleft] = '\0';
+
+ return (res);
+}
+
+char *
+utf8_str_toupper(const char *s)
+{
+ return (utf8_str_to_upper_or_lower(s, U8_TEXTPREP_TOUPPER));
+}
+
+char *
+utf8_str_tolower(const char *s)
+{
+ return (utf8_str_to_upper_or_lower(s, U8_TEXTPREP_TOLOWER));
+}