diff options
author | Gordon Ross <Gordon.Ross@Sun.COM> | 2009-07-02 12:58:38 -0400 |
---|---|---|
committer | Gordon Ross <Gordon.Ross@Sun.COM> | 2009-07-02 12:58:38 -0400 |
commit | 613a2f6ba31e891e3d947a356daf5e563d43c1ce (patch) | |
tree | 0f7f3438a5792c05ed156a43e8cd84f25695d7f2 /usr/src | |
parent | bf73eaa5a8ea69ac16a1e6e7b736f09286d073f9 (diff) | |
download | illumos-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')
113 files changed, 12465 insertions, 8560 deletions
diff --git a/usr/src/cmd/fs.d/smbclnt/Makefile b/usr/src/cmd/fs.d/smbclnt/Makefile index 8845071f42..9e66620c60 100644 --- a/usr/src/cmd/fs.d/smbclnt/Makefile +++ b/usr/src/cmd/fs.d/smbclnt/Makefile @@ -18,20 +18,20 @@ # # CDDL HEADER END # + # -#ident "%Z%%M% %I% %E% SMI" -# -# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -# cmd/fs.d/smbclnt/Makefile + # -# cmd/fs.d/smbclnt contains the CIFS client commands +# cmd/fs.d/smbclnt/Makefile +# contains the CIFS client commands # include $(SRC)/Makefile.master -SUBDIRS_CATALOG= smbutil mount umount +SUBDIRS_CATALOG= smbutil smbiod mount umount SUBDIRS= $(SUBDIRS_CATALOG) lsacl svc # for messaging catalog files @@ -50,9 +50,9 @@ catalog:= TARGET= catalog .PARALLEL: $(SUBDIRS) -install clean clobber: $(SUBDIRS) +all install clean clobber lint: $(SUBDIRS) -all lint catalog: $(SUBDIRS_CATALOG) +catalog: $(SUBDIRS_CATALOG) $(RM) $(POFILE) cat $(POFILES) > $(POFILE) diff --git a/usr/src/cmd/fs.d/smbclnt/lsacl/Makefile b/usr/src/cmd/fs.d/smbclnt/lsacl/Makefile index 22ce8510c0..4fbb347a73 100644 --- a/usr/src/cmd/fs.d/smbclnt/lsacl/Makefile +++ b/usr/src/cmd/fs.d/smbclnt/lsacl/Makefile @@ -19,11 +19,9 @@ # CDDL HEADER END # # -# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -# ident "%Z%%M% %I% %E% SMI" -# FSTYPE= smbfs LIBPROG= lsacl @@ -38,8 +36,6 @@ LDLIBS += -lsmbfs -lsec CFLAGS += $(CCVERBOSE) C99MODE= $(C99_ENABLE) -CPPFLAGS += -I$(SRC)/lib/libsmbfs \ - -I$(SRC)/uts/common/smbclnt -I$(SRC)/uts/common CLOBBERFILES += $(LIBPROG) diff --git a/usr/src/cmd/fs.d/smbclnt/lsacl/lsacl.c b/usr/src/cmd/fs.d/smbclnt/lsacl/lsacl.c index 9f73f8eb1e..1285251898 100644 --- a/usr/src/cmd/fs.d/smbclnt/lsacl/lsacl.c +++ b/usr/src/cmd/fs.d/smbclnt/lsacl/lsacl.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" - /* * This is the smbfs/lsacl command. * (just for testing - not installed) @@ -42,7 +40,6 @@ #include <unistd.h> #include <string.h> -#include <netsmb/smb_lib.h> #include <netsmb/smbfs_acl.h> char *progname; @@ -81,7 +78,7 @@ main(int argc, char **argv) error = smbfs_acl_getsd(fd, 7, &sd); if (error) { fprintf(stderr, "getsd: %s\n", - smb_strerror(error)); + strerror(error)); exit(1); } @@ -101,7 +98,7 @@ main(int argc, char **argv) error = smbfs_acl_get(fd, &acl, &uid, &gid); if (error) { fprintf(stderr, "getacl: %s\n", - smb_strerror(error)); + strerror(error)); exit(1); } printf("Solaris security data:\n"); diff --git a/usr/src/cmd/fs.d/smbclnt/mount/Makefile b/usr/src/cmd/fs.d/smbclnt/mount/Makefile index d23874bf57..5d5da5ec5c 100644 --- a/usr/src/cmd/fs.d/smbclnt/mount/Makefile +++ b/usr/src/cmd/fs.d/smbclnt/mount/Makefile @@ -19,10 +19,10 @@ # CDDL HEADER END # # -# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -# ident "%Z%%M% %I% %E% SMI" + # # cmd/fs.d/smbclnt/mount/Makefile # @@ -36,15 +36,20 @@ include ../../Makefile.fstype OBJS= $(LIBPROG).o SRCS= $(LIBPROG).c $(FSLIBSRC) POFILE= $(LIBPROG).po - -LDLIBS += -lsmbfs -lscf +CLOBBERFILES += $(LIBPROG) CFLAGS += $(CCVERBOSE) C99MODE= $(C99_ENABLE) -CPPFLAGS += -I$(SRC)/lib/libsmbfs \ - -I$(SRC)/uts/common/smbclnt -I$(SRC)/uts/common -CLOBBERFILES += $(LIBPROG) +LDLIBS += -lsmbfs -lscf + +CPPFLAGS += -I$(SRC)/uts/common -I$(SRC)/lib/libsmbfs + +# uncomment these for dbx debugging +#COPTFLAG = -g +#CTF_FLAGS = +#CTFCONVERT_O= +#CTFMERGE_LIB= .KEEP_STATE: diff --git a/usr/src/cmd/fs.d/smbclnt/mount/mount.c b/usr/src/cmd/fs.d/smbclnt/mount/mount.c index 61bc463321..9068d093b6 100644 --- a/usr/src/cmd/fs.d/smbclnt/mount/mount.c +++ b/usr/src/cmd/fs.d/smbclnt/mount/mount.c @@ -37,11 +37,6 @@ * Use is subject to license terms. */ -#include <sys/param.h> -#include <sys/stat.h> -#include <sys/errno.h> -#include <sys/mount.h> - #include <stdio.h> #include <string.h> #include <strings.h> @@ -57,33 +52,38 @@ #include <locale.h> #include <libscf.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/errno.h> +#include <sys/mount.h> #include <sys/mntent.h> #include <sys/mnttab.h> -#include <cflib.h> - -#include <netsmb/smb.h> +/* This needs to know ctx->ct_dev_fd, etc. */ #include <netsmb/smb_lib.h> #include <sys/fs/smbfs_mount.h> #include "mntopts.h" +extern char *optarg; +extern int optind; + static char mount_point[MAXPATHLEN + 1]; static void usage(void); -static int setsubopt(int, char *, struct smbfs_args *); +static int setsubopt(smb_ctx_t *, struct smbfs_args *, int, char *); /* smbfs options */ -#define MNTOPT_RETRY "retry" -#define MNTOPT_TIMEOUT "timeout" +#define MNTOPT_DOMAIN "domain" +#define MNTOPT_USER "user" #define MNTOPT_DIRPERMS "dirperms" #define MNTOPT_FILEPERMS "fileperms" #define MNTOPT_GID "gid" #define MNTOPT_UID "uid" #define MNTOPT_NOPROMPT "noprompt" -#define OPT_RETRY 1 -#define OPT_TIMEOUT 2 +#define OPT_DOMAIN 1 +#define OPT_USER 2 #define OPT_DIRPERMS 3 #define OPT_FILEPERMS 4 #define OPT_GID 5 @@ -108,8 +108,8 @@ struct smbfsopts { }; struct smbfsopts opts[] = { - {MNTOPT_RETRY, OPT_RETRY}, - {MNTOPT_TIMEOUT, OPT_TIMEOUT}, + {MNTOPT_DOMAIN, OPT_DOMAIN}, + {MNTOPT_USER, OPT_USER}, {MNTOPT_DIRPERMS, OPT_DIRPERMS}, {MNTOPT_FILEPERMS, OPT_FILEPERMS}, {MNTOPT_GID, OPT_GID}, @@ -132,22 +132,20 @@ static int Oflg = 0; /* Overlay mounts */ static int qflg = 0; /* quiet - don't print warnings on bad options */ static int ro = 0; /* read-only mount */ static int noprompt = 0; /* don't prompt for password */ -static int retry = -1; -static int timeout = -1; #define RET_ERR 33 #define SERVICE "svc:/network/smb/client:default" +struct smbfs_args mdata; +struct mnttab mnt; +char optbuf[MAX_MNTOPT_STR]; + int main(int argc, char *argv[]) { - struct smb_ctx sctx, *ctx = &sctx; - struct smbfs_args mdata; + struct smb_ctx *ctx = NULL; struct stat st; - int opt, error, mntflags; - struct mnttab mnt; - struct mnttab *mntp = &mnt; - char optbuf[MAX_MNTOPT_STR]; + int opt, error, err2, mntflags; static char *fstype = MNTTYPE_SMBFS; char *env, *state; @@ -174,8 +172,9 @@ main(int argc, char *argv[]) fprintf(stderr, gettext("mount_smbfs: service \"%s\" not enabled.\n"), SERVICE); - exit(1); + exit(RET_ERR); } + free(state); /* Debugging support. */ if ((env = getenv("SMBFS_DEBUG")) != NULL) { @@ -186,22 +185,35 @@ main(int argc, char *argv[]) error = smb_lib_init(); if (error) - exit(error); + exit(RET_ERR); mnt.mnt_mntopts = optbuf; mntflags = MS_DATA; + bzero(&mdata, sizeof (mdata)); + mdata.version = SMBFS_VERSION; /* smbfs mount version */ mdata.uid = (uid_t)-1; mdata.gid = (gid_t)-1; mdata.caseopt = SMB_CS_NONE; - error = smb_ctx_init(ctx, argc, argv, SMBL_SHARE, SMBL_SHARE, - SMB_ST_DISK); + error = smb_ctx_alloc(&ctx); if (error) - exit(error); + exit(RET_ERR); + + /* + * Parse the UNC path so we have the server (etc.) + * that we need during rcfile+sharectl parsing. + */ + if (argc < 3) + usage(); + error = smb_ctx_parseunc(ctx, argv[argc - 2], + SMBL_SHARE, SMBL_SHARE, USE_DISKDEV, NULL); + if (error) + exit(RET_ERR); + error = smb_ctx_readrc(ctx); if (error) - exit(error); + exit(RET_ERR); while ((opt = getopt(argc, argv, "ro:Oq")) != -1) { switch (opt) { @@ -254,7 +266,8 @@ main(int argc, char *argv[]) *comma = ','; continue; } - ret = setsubopt(opts[i].index, soptval, &mdata); + ret = setsubopt(ctx, &mdata, + opts[i].index, soptval); if (ret != 0) exit(RET_ERR); if (equals) @@ -280,24 +293,19 @@ main(int argc, char *argv[]) mntflags |= MS_RDONLY; /* convert "rw"->"ro" */ - if (p = strstr(mntp->mnt_mntopts, "rw")) { + if (p = strstr(mnt.mnt_mntopts, "rw")) { if (*(p+2) == ',' || *(p+2) == '\0') *(p+1) = 'o'; } } + if (optind + 2 != argc) + usage(); + mnt.mnt_special = argv[optind]; mnt.mnt_mountp = argv[optind+1]; - mdata.version = SMBFS_VERSION; /* smbfs mount version */ - - if (optind == argc - 2) - optind++; - - if (optind != argc - 1) - usage(); - realpath(unpercent(argv[optind]), mount_point); - + realpath(argv[optind+1], mount_point); if (stat(mount_point, &st) == -1) err(EX_OSERR, gettext("could not find mount point %s"), mount_point); @@ -325,90 +333,76 @@ main(int argc, char *argv[]) mdata.dir_mode |= S_IXOTH; } - /* - * XXX: The driver can fill these in more reliably, - * so why do we set them here? (Just set both = -1) - */ - ctx->ct_ssn.ioc_owner = ctx->ct_sh.ioc_owner = getuid(); - ctx->ct_ssn.ioc_group = ctx->ct_sh.ioc_group = getgid(); - opt = 0; - if (mdata.dir_mode & S_IXGRP) - opt |= SMBM_EXECGRP; - if (mdata.dir_mode & S_IXOTH) - opt |= SMBM_EXECOTH; - ctx->ct_ssn.ioc_rights |= opt; - ctx->ct_sh.ioc_rights |= opt; + ctx->ct_ssn.ssn_owner = SMBM_ANY_OWNER; if (noprompt) ctx->ct_flags |= SMBCF_NOPWD; - if (retry != -1) - ctx->ct_ssn.ioc_retrycount = retry; - if (timeout != -1) - ctx->ct_ssn.ioc_timeout = timeout; /* - * If we got our password from the keychain and get an - * authorization error, we come back here to obtain a new - * password from user input. + * Resolve the server address, + * setup derived defaults. */ -reauth: error = smb_ctx_resolve(ctx); if (error) - exit(error); - - mdata.devfd = ctx->ct_fd; /* file descriptor */ + exit(RET_ERR); + /* + * Have server, share, etc. from above: + * smb_ctx_scan_argv, option settings. + * Get the session and tree. + */ again: - error = smb_ctx_lookup(ctx, SMBL_SHARE, SMBLK_CREATE); - if (error == ENOENT && ctx->ct_origshare) { - strcpy(ctx->ct_sh.ioc_share, ctx->ct_origshare); - free(ctx->ct_origshare); - ctx->ct_origshare = NULL; - goto again; /* try again using share name as given */ + error = smb_ctx_get_ssn(ctx); + if (error == EAUTH && noprompt == 0) { + err2 = smb_get_authentication(ctx); + if (err2 == 0) + goto again; } - if (ctx->ct_flags & SMBCF_KCFOUND && smb_autherr(error)) { - ctx->ct_ssn.ioc_password[0] = '\0'; - smb_error(gettext("main(lookup): bad keychain entry"), 0); - ctx->ct_flags |= SMBCF_KCBAD; - goto reauth; + if (error) { + smb_error(gettext("//%s: login failed"), + error, ctx->ct_fullserver); + exit(RET_ERR); + } + + error = smb_ctx_get_tree(ctx); + if (error) { + smb_error(gettext("//%s/%s: tree connect failed"), + error, ctx->ct_fullserver, ctx->ct_origshare); + exit(RET_ERR); } - if (error) - exit(error); - mdata.version = SMBFS_VERSION; - mdata.devfd = ctx->ct_fd; + /* + * Have tree connection, now mount it. + */ + mdata.devfd = ctx->ct_dev_fd; - if (mount(mntp->mnt_special, mntp->mnt_mountp, + if (mount(mnt.mnt_special, mnt.mnt_mountp, mntflags, fstype, &mdata, sizeof (mdata), - mntp->mnt_mntopts, MAX_MNTOPT_STR) < 0) { + mnt.mnt_mntopts, MAX_MNTOPT_STR) < 0) { if (errno != ENOENT) { err(EX_OSERR, gettext("mount_smbfs: %s"), - mntp->mnt_mountp); + mnt.mnt_mountp); } else { struct stat sb; - if (stat(mntp->mnt_mountp, &sb) < 0 && + if (stat(mnt.mnt_mountp, &sb) < 0 && errno == ENOENT) err(EX_OSERR, gettext("mount_smbfs: %s"), - mntp->mnt_mountp); + mnt.mnt_mountp); else err(EX_OSERR, gettext("mount_smbfs: %s"), - mntp->mnt_special); - - error = smb_ctx_tdis(ctx); - if (error) /* unable to clean up?! */ - exit(error); + mnt.mnt_special); } } - smb_ctx_done(ctx); + smb_ctx_free(ctx); if (error) { smb_error(gettext("mount error: %s"), error, mount_point); - exit(errno); + exit(RET_ERR); } return (0); } int -setsubopt(int index, char *optarg, struct smbfs_args *mdatap) +setsubopt(smb_ctx_t *ctx, struct smbfs_args *mdatap, int index, char *optarg) { struct passwd *pwd; struct group *grp; @@ -429,6 +423,15 @@ setsubopt(int index, char *optarg, struct smbfs_args *mdatap) case OPT_NOEXEC: /* We don't have to handle generic options here */ return (0); + + case OPT_DOMAIN: + err = smb_ctx_setdomain(ctx, optarg, B_TRUE); + break; + + case OPT_USER: + err = smb_ctx_setuser(ctx, optarg, B_TRUE); + break; + case OPT_UID: pwd = isdigit(optarg[0]) ? getpwuid(atoi(optarg)) : getpwnam(optarg); @@ -474,12 +477,6 @@ setsubopt(int index, char *optarg, struct smbfs_args *mdatap) mdatap->file_mode = l; } break; - case OPT_RETRY: - retry = atoi(optarg); - break; - case OPT_TIMEOUT: - timeout = atoi(optarg); - break; case OPT_NOPROMPT: noprompt++; } diff --git a/usr/src/cmd/fs.d/smbclnt/smbiod/Makefile b/usr/src/cmd/fs.d/smbclnt/smbiod/Makefile new file mode 100644 index 0000000000..626304a88a --- /dev/null +++ b/usr/src/cmd/fs.d/smbclnt/smbiod/Makefile @@ -0,0 +1,70 @@ +# +# 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. +# + +# +# cmd/fs.d/smbclnt/smbiod/Makefile +# + +FSTYPE= smbfs +TYPEPROG= smbiod + +include ../../Makefile.fstype + +OBJS= $(TYPEPROG).o +SRCS= $(TYPEPROG).c +POFILE= $(TYPEPROG).po + +CLOBBERFILES += $(TYPEPROG) + +CFLAGS += $(CCVERBOSE) +C99MODE= $(C99_ENABLE) + +# This is a multi-thread program but Nevada +# no longer needs -lthread +LDLIBS += -lsmbfs -ldoor + +CPPFLAGS += -I$(SRC)/lib/libsmbfs \ + -I$(SRC)/uts/common/smbclnt -I$(SRC)/uts/common + +# Debugging +${NOT_RELEASE_BUILD} CPPFLAGS += -DDEBUG + +# uncomment these for dbx debugging +#COPTFLAG = -g +#CTF_FLAGS = +#CTFCONVERT_O= +#CTFMERGE_LIB= + +all: $(TYPEPROG) + +catalog: $(POFILE) + +lint: lint_SRCS + +clean: + $(RM) $(OBJS) $(POFILE) + +.KEEP_STATE: diff --git a/usr/src/cmd/fs.d/smbclnt/smbiod/smbiod.c b/usr/src/cmd/fs.d/smbclnt/smbiod/smbiod.c new file mode 100644 index 0000000000..27677874ac --- /dev/null +++ b/usr/src/cmd/fs.d/smbclnt/smbiod/smbiod.c @@ -0,0 +1,362 @@ +/* + * 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. + */ + +/* + * SMBFS I/O Deamon (smbiod) + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/note.h> + +#include <errno.h> +#include <fcntl.h> +#include <signal.h> +#include <stdarg.h> +#include <stdio.h> +#include <string.h> +#include <strings.h> +#include <stdlib.h> +#include <synch.h> +#include <time.h> +#include <unistd.h> +#include <ucred.h> + +#include <err.h> +#include <door.h> +#include <thread.h> + +#include <netsmb/smb_lib.h> + +#define ALARM_TIME 30 /* sec. */ +#define EXIT_FAIL 1 +#define EXIT_OK 0 + +#if defined(DEBUG) || defined(__lint) +#define DPRINT(...) do \ +{ \ + if (smb_debug) \ + fprintf(stderr, __VA_ARGS__); \ + _NOTE(CONSTCOND) \ +} while (0) +#else +#define DPRINT(...) ((void)0) +#endif + +mutex_t iod_mutex = DEFAULTMUTEX; +int iod_thr_count; /* threads, excluding main */ +int iod_terminating; + +void iod_dispatch(void *cookie, char *argp, size_t argsz, + door_desc_t *dp, uint_t n_desc); +int iod_newvc(smb_iod_ssn_t *clnt_ssn); +void * iod_work(void *arg); + +int +main(int argc, char **argv) +{ + static const int door_attrs = + DOOR_REFUSE_DESC | DOOR_NO_CANCEL; + sigset_t oldmask, tmpmask; + char *env, *door_path = NULL; + int door_fd = -1, tmp_fd = -1; + int err, i, sig; + int rc = EXIT_FAIL; + + /* Debugging support. */ + if ((env = getenv("SMBFS_DEBUG")) != NULL) { + smb_debug = atoi(env); + if (smb_debug < 1) + smb_debug = 1; + } + + /* + * Find out if an IOD is already running. + * If so, we lost a harmless startup race. + * An IOD did start, so exit success. + */ + err = smb_iod_open_door(&door_fd); + if (err == 0) { + close(door_fd); + door_fd = -1; + DPRINT("main: already running\n"); + exit(EXIT_OK); + } + + /* + * Create a file for the door. + */ + door_path = smb_iod_door_path(); + unlink(door_path); + tmp_fd = open(door_path, O_RDWR|O_CREAT|O_EXCL, 0600); + if (tmp_fd < 0) { + perror(door_path); + exit(EXIT_FAIL); + } + close(tmp_fd); + tmp_fd = -1; + + + /* + * Close FDs 0,1,2 so we don't have a TTY, and + * re-open them on /dev/null so they won't be + * used for device handles (etc.) later, and + * we don't have to worry about printf calls + * or whatever going to these FDs. + */ + for (i = 0; i < 3; i++) { + /* Exception: If smb_debug, keep stderr */ + if (smb_debug && i == 2) + break; + close(i); + tmp_fd = open("/dev/null", O_RDWR); + if (tmp_fd < 0) + perror("/dev/null"); + if (tmp_fd != i) + DPRINT("Open /dev/null - wrong fd?\n"); + } + + /* + * Become session leader. + */ + setsid(); + + /* + * Create door service threads with signals blocked. + */ + sigfillset(&tmpmask); + sigprocmask(SIG_BLOCK, &tmpmask, &oldmask); + + /* Setup the door service. */ + door_fd = door_create(iod_dispatch, NULL, door_attrs); + if (door_fd < 0) { + fprintf(stderr, "%s: door_create failed\n", argv[0]); + rc = EXIT_FAIL; + goto errout; + } + fdetach(door_path); + if (fattach(door_fd, door_path) < 0) { + fprintf(stderr, "%s: fattach failed\n", argv[0]); + rc = EXIT_FAIL; + goto errout; + } + + /* + * Post the initial alarm, and then just + * wait for signals. + */ + alarm(ALARM_TIME); +again: + sig = sigwait(&tmpmask); + DPRINT("main: sig=%d\n", sig); + + /* + * If a door call races with the alarm, ignore the alarm. + * It will be rescheduled when the threads go away. + */ + mutex_lock(&iod_mutex); + if (sig == SIGALRM && iod_thr_count > 0) { + mutex_unlock(&iod_mutex); + goto again; + } + iod_terminating = 1; + mutex_unlock(&iod_mutex); + rc = EXIT_OK; + +errout: + fdetach(door_path); + door_revoke(door_fd); + door_fd = -1; + unlink(door_path); + + return (rc); +} + +/*ARGSUSED*/ +void +iod_dispatch(void *cookie, char *argp, size_t argsz, + door_desc_t *dp, uint_t n_desc) +{ + smb_iod_ssn_t *ssn; + ucred_t *ucred; + uid_t cl_uid; + int rc; + + /* + * Verify that the calling process has the same UID. + * Paranoia: The door we created has mode 0600, so + * this check is probably redundant. + */ + ucred = NULL; + if (door_ucred(&ucred) != 0) { + rc = EACCES; + goto out; + } + cl_uid = ucred_getruid(ucred); + ucred_free(ucred); + ucred = NULL; + if (cl_uid != getuid()) { + DPRINT("iod_dispatch: wrong UID\n"); + rc = EACCES; + goto out; + } + + /* + * The library uses a NULL arg call to check if + * the deamon is running. Just return zero. + */ + if (argp == NULL) { + rc = 0; + goto out; + } + + /* + * Otherwise, the arg must be the (fixed size) + * smb_iod_ssn_t + */ + if (argsz != sizeof (*ssn)) { + rc = EINVAL; + goto out; + } + + mutex_lock(&iod_mutex); + if (iod_terminating) { + mutex_unlock(&iod_mutex); + DPRINT("iod_dispatch: terminating\n"); + rc = EINTR; + goto out; + } + if (iod_thr_count++ == 0) { + alarm(0); + DPRINT("iod_dispatch: cancelled alarm\n"); + } + mutex_unlock(&iod_mutex); + + ssn = (void *) argp; + rc = iod_newvc(ssn); + + mutex_lock(&iod_mutex); + if (--iod_thr_count == 0) { + DPRINT("iod_dispatch: schedule alarm\n"); + alarm(ALARM_TIME); + } + mutex_unlock(&iod_mutex); + +out: + door_return((void *)&rc, sizeof (rc), NULL, 0); +} + +/* + * Try making a connection with the server described by + * the info in the smb_iod_ssn_t arg. If successful, + * start an IOD thread to service it, then return to + * the client side of the door. + */ +int +iod_newvc(smb_iod_ssn_t *clnt_ssn) +{ + smb_ctx_t *ctx; + thread_t tid; + int err; + + + /* + * This needs to essentially "clone" the smb_ctx_t + * from the client side of the door, or at least + * as much of it as we need while creating a VC. + */ + err = smb_ctx_alloc(&ctx); + if (err) + return (err); + bcopy(clnt_ssn, &ctx->ct_iod_ssn, sizeof (ctx->ct_iod_ssn)); + + /* + * Do the initial connection setup here, so we can + * report the outcome to the door client. + */ + err = smb_iod_connect(ctx); + if (err != 0) + goto out; + + /* + * Create the driver session now, so we don't + * race with the door client findvc call. + */ + if ((err = smb_ctx_gethandle(ctx)) != 0) + goto out; + if (ioctl(ctx->ct_dev_fd, SMBIOC_SSN_CREATE, &ctx->ct_ssn) < 0) { + err = errno; + goto out; + } + + /* The rest happens in the iod_work thread. */ + err = thr_create(NULL, 0, iod_work, ctx, THR_DETACHED, &tid); + if (err == 0) { + /* + * Given to the new thread. + * free at end of iod_work + */ + ctx = NULL; + } + +out: + if (ctx) + smb_ctx_free(ctx); + + return (err); +} + +/* + * Be the reader thread for some VC. + * + * This is started by a door call thread, which means + * this is always at least the 2nd thread, therefore + * it should never see thr_count==0 or terminating. + */ +void * +iod_work(void *arg) +{ + smb_ctx_t *ctx = arg; + + mutex_lock(&iod_mutex); + if (iod_thr_count++ == 0) { + alarm(0); + DPRINT("iod_work: cancelled alarm\n"); + } + mutex_unlock(&iod_mutex); + + (void) smb_iod_work(ctx); + + mutex_lock(&iod_mutex); + if (--iod_thr_count == 0) { + DPRINT("iod_work: schedule alarm\n"); + alarm(ALARM_TIME); + } + mutex_unlock(&iod_mutex); + + smb_ctx_free(ctx); + return (NULL); +} diff --git a/usr/src/cmd/fs.d/smbclnt/smbutil/Makefile b/usr/src/cmd/fs.d/smbclnt/smbutil/Makefile index 9d6017b35b..31d52961fe 100644 --- a/usr/src/cmd/fs.d/smbclnt/smbutil/Makefile +++ b/usr/src/cmd/fs.d/smbclnt/smbutil/Makefile @@ -18,11 +18,12 @@ # # CDDL HEADER END # + # -# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -# ident "%Z%%M% %I% %E% SMI" + # # cmd/fs.d/smbclnt/smbutil/Makefile # @@ -32,14 +33,31 @@ PROG= smbutil include $(SRC)/cmd/Makefile.cmd OBJS= smbutil.o login.o lookup.o print.o status.o view.o + SRCS= $(OBJS:%.o=%.c) POFILE= smbutil_all.po POFILES= $(OBJS:%.o=%.po) +CLOBBERFILES+= $(POFILE) $(POFILES) C99MODE= $(C99_ENABLE) + +LDLIBS += -lsmbfs -lnsl + CPPFLAGS += -I$(SRC)/lib/libsmbfs \ -I$(SRC)/uts/common/smbclnt -I$(SRC)/uts/common -LDLIBS += -lsmbfs -lnsl + +# Debugging +${NOT_RELEASE_BUILD} CPPFLAGS += -DDEBUG + +# uncomment these for dbx debugging +#COPTFLAG = -g +#CTF_FLAGS = +#CTFCONVERT_O= +#CTFMERGE_LIB= + +# disable some of the less important lint +LINTFLAGS += -erroff=E_FUNC_RET_ALWAYS_IGNOR2 +LINTFLAGS += -erroff=E_FUNC_RET_MAYBE_IGNORED2 all: $(PROG) @@ -58,8 +76,11 @@ $(POFILE): $(POFILES) $(RM) $@ $(CAT) $(POFILES) > $@ +lint: lint_SRCS + clean : $(RM) $(OBJS) -clobber: clean - $(RM) $(PROG) $(POFILES) +.KEEP_STATE: + +include ../../../Makefile.targ diff --git a/usr/src/cmd/fs.d/smbclnt/smbutil/common.h b/usr/src/cmd/fs.d/smbclnt/smbutil/common.h index 280c4744f4..24cb1436a0 100644 --- a/usr/src/cmd/fs.d/smbclnt/smbutil/common.h +++ b/usr/src/cmd/fs.d/smbclnt/smbutil/common.h @@ -1,8 +1,38 @@ +/* + * 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. + */ + #ifndef _SMBUTIL_COMMON_H #define _SMBUTIL_COMMON_H -#pragma ident "%Z%%M% %I% %E% SMI" - #ifdef __cplusplus extern "C" { #endif diff --git a/usr/src/cmd/fs.d/smbclnt/smbutil/lookup.c b/usr/src/cmd/fs.d/smbclnt/smbutil/lookup.c index 199d785277..5e8049e71b 100644 --- a/usr/src/cmd/fs.d/smbclnt/smbutil/lookup.c +++ b/usr/src/cmd/fs.d/smbclnt/smbutil/lookup.c @@ -32,7 +32,10 @@ * $Id: lookup.c,v 1.1.1.1 2001/06/09 00:28:13 zarzycki 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/errno.h> @@ -74,9 +77,9 @@ cmd_lookup(int argc, char *argv[]) exit(1); } if (smb_open_rcfile(NULL) == 0) { - if (nb_ctx_readrcsection(smb_rc, ctx, "default", 0) != 0) + if (nb_ctx_readrcsection(NULL, ctx, "default", 0) != 0) exit(1); - rc_close(smb_rc); + smb_close_rcfile(); } if ((ctx->nb_flags & NBCF_NS_ENABLE) == 0) { fprintf(stderr, diff --git a/usr/src/cmd/fs.d/smbclnt/smbutil/print.c b/usr/src/cmd/fs.d/smbclnt/smbutil/print.c index dd986e1b49..7379acfd90 100644 --- a/usr/src/cmd/fs.d/smbclnt/smbutil/print.c +++ b/usr/src/cmd/fs.d/smbclnt/smbutil/print.c @@ -29,21 +29,22 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: print.c,v 1.7 2004/03/19 01:49:48 lindak Exp $ + * from: Id: print.c,v 1.4 2001/01/28 07:35:01 bp 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/errno.h> -#include <sys/stat.h> +#include <sys/types.h> +#include <err.h> +#include <errno.h> #include <fcntl.h> #include <stdio.h> -#include <err.h> -#include <unistd.h> -#include <strings.h> +#include <string.h> #include <stdlib.h> -#include <sysexits.h> +#include <unistd.h> #include <libintl.h> #include <cflib.h> @@ -52,13 +53,174 @@ #include "common.h" +static char titlebuf[256]; +static char databuf[4096]; +static int print_file(smb_ctx_t *, char *, int); void print_usage(void) { printf(gettext("usage: smbutil print [connection options] //" "[workgroup;][user[:password]@]" - "server/share\n")); + "server/share {print_file|-}\n")); exit(1); } + +int +cmd_print(int argc, char *argv[]) +{ + struct smb_ctx *ctx = NULL; + char *filename; + int error, opt; + int file = -1; + + /* last arg is the print file. */ + if (argc < 3) + print_usage(); + + error = smb_ctx_alloc(&ctx); + if (error) + goto out; + + error = smb_ctx_scan_argv(ctx, argc-1, argv, + SMBL_SHARE, SMBL_SHARE, USE_SPOOLDEV); + if (error) + goto out; + + error = smb_ctx_readrc(ctx); + if (error) + goto out; + + while ((opt = getopt(argc-1, argv, STDPARAM_OPT)) != EOF) { + if (opt == '?') + print_usage(); + error = smb_ctx_opt(ctx, opt, optarg); + if (error) + goto out; + } + if (optind != argc-2) + print_usage(); + filename = argv[argc-1]; + + if (strcmp(filename, "-") == 0) { + file = 0; /* stdin */ + filename = "stdin"; + } else { + file = open(filename, O_RDONLY, 0); + if (file < 0) { + smb_error("could not open file %s\n", errno, filename); + exit(1); + } + } + + /* + * Resolve the server address, + * setup derived defaults. + */ + error = smb_ctx_resolve(ctx); + if (error) + goto out; + + /* + * Have server + share names, options etc. + * Get the session and tree. + */ +again: + error = smb_ctx_get_ssn(ctx); + if (error == EAUTH) { + int err2 = smb_get_authentication(ctx); + if (err2 == 0) + goto again; + } + if (error) { + smb_error(gettext("//%s: login failed"), + error, ctx->ct_fullserver); + goto out; + } + + error = smb_ctx_get_tree(ctx); + if (error) { + smb_error(gettext("//%s/%s: tree connect failed"), + error, ctx->ct_fullserver, ctx->ct_origshare); + goto out; + } + + /* + * Have the printer share connection. + * Print the file. + */ + snprintf(titlebuf, sizeof (titlebuf), "%s_%s", + ctx->ct_user, filename); + + error = print_file(ctx, titlebuf, file); + + +out: + /* don't close stdin (file=0) */ + if (file > 0) + close(file); + + smb_ctx_free(ctx); + + return (error); +} + +/* + * Documentation for OPEN_PRINT_FILE is scarse. + * It's in a 1996 MS doc. entitled: + * SMB FILE SHARING PROTOCOL + * + * The extra parameters are: + * SetupLength: what part of the file is printer setup + * Mode: text or graphics (raw data) + * IdentifierString: job title + */ +enum { + MODE_TEXT = 0, /* TAB expansion, etc. */ + MODE_GRAPHICS /* raw data */ +}; + +static int +print_file(smb_ctx_t *ctx, char *title, int file) +{ + off_t offset; + int error, rcnt, wcnt; + int setup_len = 0; /* No printer setup data */ + int mode = MODE_GRAPHICS; /* treat as raw data */ + int fh = -1; + + error = smb_printer_open(ctx, setup_len, mode, title, &fh); + if (error) { + smb_error("could not open print job", error); + return (error); + } + + offset = 0; + for (;;) { + rcnt = read(file, databuf, sizeof (databuf)); + if (rcnt < 0) { + error = errno; + smb_error("error reading input file\n", error); + break; + } + if (rcnt == 0) + break; + + wcnt = smb_fh_write(ctx, fh, offset, rcnt, databuf); + if (wcnt < 0) { + error = errno; + smb_error("error writing spool file\n", error); + break; + } + if (wcnt != rcnt) { + smb_error("incomplete write to spool file\n", 0); + error = EIO; + break; + } + offset += wcnt; + } + + (void) smb_printer_close(ctx, fh); + return (error); +} diff --git a/usr/src/cmd/fs.d/smbclnt/smbutil/smbutil.c b/usr/src/cmd/fs.d/smbclnt/smbutil/smbutil.c index ca5eeb425d..404b436d16 100644 --- a/usr/src/cmd/fs.d/smbclnt/smbutil/smbutil.c +++ b/usr/src/cmd/fs.d/smbclnt/smbutil/smbutil.c @@ -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. */ @@ -74,7 +74,8 @@ static struct commands { {"logout", cmd_logout, logout_usage, 0}, {"logoutall", cmd_logoutall, logoutall_usage, 0}, {"lookup", cmd_lookup, lookup_usage, CMDFL_NO_KMOD}, - {"status", cmd_status, status_usage, 0}, + {"print", cmd_print, print_usage, 0}, + {"status", cmd_status, status_usage, CMDFL_NO_KMOD}, {"view", cmd_view, view_usage, 0}, {NULL, NULL, NULL, 0} }; @@ -134,7 +135,7 @@ main(int argc, char *argv[]) { struct commands *cmd; char *cp; - int opt; + int err, opt; (void) setlocale(LC_ALL, ""); (void) textdomain(TEXT_DOMAIN); @@ -176,7 +177,8 @@ main(int argc, char *argv[]) argc -= optind; argv += optind; optind = 1; - return (cmd->fn(argc, argv)); + err = cmd->fn(argc, argv); + return ((err) ? 1 : 0); } static void @@ -191,7 +193,7 @@ help(void) { " logout logout from specified host\n" " logoutall logout all users (requires privilege)\n" " lookup resolve NetBIOS name to IP address\n" - /* " print print file to the specified remote printer\n" */ + " print print file to the specified remote printer\n" " status resolve IP address or DNS name to NetBIOS names\n" " view list resources on specified host\n" "\n")); diff --git a/usr/src/cmd/fs.d/smbclnt/smbutil/status.c b/usr/src/cmd/fs.d/smbclnt/smbutil/status.c index 9353c69e16..9707becaa6 100644 --- a/usr/src/cmd/fs.d/smbclnt/smbutil/status.c +++ b/usr/src/cmd/fs.d/smbclnt/smbutil/status.c @@ -32,7 +32,10 @@ * $Id: status.c,v 1.2 2001/08/18 05:44:50 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/errno.h> @@ -62,10 +65,10 @@ int cmd_status(int argc, char *argv[]) { struct nb_ctx *ctx; - struct sockaddr *sap; + struct in_addr ina; char *hostname; - char servername[SMB_MAXSRVNAMELEN + 1]; - char workgroupname[SMB_MAXUSERNAMELEN + 1]; + char servername[NB_NAMELEN]; + char workgroupname[NB_NAMELEN]; int error, opt; if (argc < 2) @@ -76,9 +79,9 @@ cmd_status(int argc, char *argv[]) exit(1); } if (smb_open_rcfile(NULL) == 0) { - if (nb_ctx_readrcsection(smb_rc, ctx, "default", 0) != 0) + if (nb_ctx_readrcsection(NULL, ctx, "default", 0) != 0) exit(1); - rc_close(smb_rc); + smb_close_rcfile(); } while ((opt = getopt(argc, argv, "")) != EOF) { switch (opt) { @@ -91,7 +94,7 @@ cmd_status(int argc, char *argv[]) status_usage(); hostname = argv[argc - 1]; - error = nb_resolvehost_in(hostname, &sap); + error = nb_resolvehost_in(hostname, &ina); if (error) { smb_error(gettext( "unable to resolve DNS hostname %s"), error, hostname); @@ -104,7 +107,7 @@ cmd_status(int argc, char *argv[]) } servername[0] = (char)0; workgroupname[0] = (char)0; - error = nbns_getnodestatus(sap, ctx, servername, workgroupname); + error = nbns_getnodestatus(ctx, &ina, servername, workgroupname); if (error) { smb_error( gettext("unable to get status from %s"), error, hostname); diff --git a/usr/src/cmd/fs.d/smbclnt/smbutil/view.c b/usr/src/cmd/fs.d/smbclnt/smbutil/view.c index 035d15132d..365e5b6f37 100644 --- a/usr/src/cmd/fs.d/smbclnt/smbutil/view.c +++ b/usr/src/cmd/fs.d/smbclnt/smbutil/view.c @@ -32,9 +32,13 @@ * $Id: view.c,v 1.9 2004/12/13 00:25:39 lindak Exp $ */ -#include <sys/param.h> -#include <sys/errno.h> -#include <sys/stat.h> +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#include <sys/types.h> +#include <errno.h> #include <stdio.h> #include <err.h> #include <unistd.h> @@ -43,12 +47,101 @@ #include <sysexits.h> #include <libintl.h> -#include <cflib.h> +#include <netsmb/smb.h> #include <netsmb/smb_lib.h> #include <netsmb/smb_netshareenum.h> #include "common.h" +int enum_shares(smb_ctx_t *); +void print_shares(int, int, struct share_info *); + +void +view_usage(void) +{ + printf(gettext("usage: smbutil view [connection options] //" + "[workgroup;][user[:password]@]server\n")); + exit(1); +} + +int +cmd_view(int argc, char *argv[]) +{ + struct smb_ctx *ctx; + int error, err2, opt; + + if (argc < 2) + view_usage(); + + error = smb_ctx_alloc(&ctx); + if (error) + return (error); + + error = smb_ctx_scan_argv(ctx, argc, argv, + SMBL_SERVER, SMBL_SERVER, USE_WILDCARD); + if (error) + return (error); + + error = smb_ctx_readrc(ctx); + if (error) + return (error); + + while ((opt = getopt(argc, argv, STDPARAM_OPT)) != EOF) { + if (opt == '?') + view_usage(); + error = smb_ctx_opt(ctx, opt, optarg); + if (error) + return (error); + } + + smb_ctx_setshare(ctx, "IPC$", USE_IPC); + + /* + * Resolve the server address, + * setup derived defaults. + */ + error = smb_ctx_resolve(ctx); + if (error) + return (error); + + /* + * Have server, share, etc. from above: + * smb_ctx_scan_argv, option settings. + * Get the session and tree. + */ +again: + error = smb_ctx_get_ssn(ctx); + if (error == EAUTH) { + err2 = smb_get_authentication(ctx); + if (err2 == 0) + goto again; + } + if (error) { + smb_error(gettext("//%s: login failed"), + error, ctx->ct_fullserver); + return (error); + } + + error = smb_ctx_get_tree(ctx); + if (error) { + smb_error(gettext("//%s/%s: tree connect failed"), + error, ctx->ct_fullserver, ctx->ct_origshare); + return (error); + } + + /* + * Have IPC$ tcon, now list shares. + */ + error = enum_shares(ctx); + if (error) { + smb_error("cannot list shares", error); + return (error); + } + + smb_ctx_free(ctx); + return (0); +} + #ifdef I18N /* not defined, put here so xgettext(1) can find strings */ static char *shtype[] = { gettext("disk"), @@ -68,60 +161,34 @@ static char *shtype[] = { #endif int -cmd_view(int argc, char *argv[]) +enum_shares(smb_ctx_t *ctx) { - struct smb_ctx sctx, *ctx = &sctx; - struct share_info *share_info, *ep; - int error, opt, i, entries, total; + struct share_info *share_info; + int error, entries, total; - if (argc < 2) - view_usage(); - error = smb_ctx_init(ctx, argc, argv, SMBL_VC, SMBL_VC, SMB_ST_ANY); - if (error) - exit(error); - error = smb_ctx_readrc(ctx); - if (error) - exit(error); - if (smb_rc) - rc_close(smb_rc); - while ((opt = getopt(argc, argv, STDPARAM_OPT)) != EOF) { - switch (opt) { - case STDPARAM_ARGS: - error = smb_ctx_opt(ctx, opt, optarg); - if (error) - exit(error); - break; - default: - view_usage(); - /*NOTREACHED*/ - } - } -#ifdef APPLE - if (loadsmbvfs()) - fprintf(stderr, gettext("SMB filesystem is not available")); -#endif -reauth: - smb_ctx_setshare(ctx, "IPC$", SMB_ST_ANY); - error = smb_ctx_resolve(ctx); - if (error) - exit(error); - error = smb_ctx_lookup(ctx, SMBL_SHARE, SMBLK_CREATE); - if (ctx->ct_flags & SMBCF_KCFOUND && smb_autherr(error)) { - ctx->ct_ssn.ioc_password[0] = '\0'; - goto reauth; - } - if (error) { - smb_error(gettext("could not login to server %s"), - error, ctx->ct_ssn.ioc_srvname); - exit(error); - } - printf(gettext("Share Type Comment\n")); - printf("-------------------------------\n"); + /* + * XXX: Later, try RPC first, + * then fall back to RAP... + */ error = smb_netshareenum(ctx, &entries, &total, &share_info); if (error) { smb_error(gettext("unable to list resources"), error); - exit(error); + return (error); } + print_shares(entries, total, share_info); + return (0); +} + +void +print_shares(int entries, int total, + struct share_info *share_info) +{ + struct share_info *ep; + int i; + + printf(gettext("Share Type Comment\n")); + printf("-------------------------------\n"); + for (ep = share_info, i = 0; i < entries; i++, ep++) { int sti = ep->type & STYPE_MASK; if (sti > STYPE_UNKNOWN) @@ -136,18 +203,4 @@ reauth: entries, total); free(share_info); - smb_ctx_done(ctx); -#ifdef APPLE - smb_save2keychain(ctx); -#endif - return (0); -} - - -void -view_usage(void) -{ - printf(gettext("usage: smbutil view [connection options] //" - "[workgroup;][user[:password]@]server\n")); - exit(1); } diff --git a/usr/src/cmd/mdb/common/modules/nsmb/nsmb.c b/usr/src/cmd/mdb/common/modules/nsmb/nsmb.c index cea159d529..70a176bb46 100644 --- a/usr/src/cmd/mdb/common/modules/nsmb/nsmb.c +++ b/usr/src/cmd/mdb/common/modules/nsmb/nsmb.c @@ -20,15 +20,15 @@ */ /* - * 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" - #include <sys/mdb_modapi.h> +#include <mdb/mdb_ctf.h> #include <sys/types.h> +#include <sys/socket.h> #include "smb_conn.h" #include "smb_rq.h" @@ -210,6 +210,7 @@ smb_co_walk_step(mdb_walk_state_t *wsp) typedef struct smb_co_cbdata { int flags; /* OPT_... */ int printed_header; + mdb_ctf_id_t ctf_id; } smb_co_cbdata_t; /* @@ -221,9 +222,7 @@ smb_ss_cb(uintptr_t addr, const void *data, void *arg) const smb_share_t *ssp = data; smb_co_cbdata_t *cbd = arg; - mdb_printf(" %-p", addr); - print_str((uintptr_t)ssp->ss_name); - mdb_printf("\n"); + mdb_printf(" %-p\t%s\n", addr, ssp->ss_name); if (cbd->flags & OPT_VERBOSE) { mdb_inc_indent(2); @@ -234,6 +233,28 @@ smb_ss_cb(uintptr_t addr, const void *data, void *arg) return (WALK_NEXT); } +static const char * +vcstate_str(smb_co_cbdata_t *cbd, int stval) +{ + static const char prefix[] = "SMBIOD_ST_"; + int prefix_len = sizeof (prefix) - 1; + mdb_ctf_id_t vcst_enum; + const char *cp; + + /* Got this in smb_vc_dcmd. */ + vcst_enum = cbd->ctf_id; + + /* Get the name for the enum value. */ + if ((cp = mdb_ctf_enum_name(vcst_enum, stval)) == NULL) + return ("?"); + + /* Skip the prefix part. */ + if (strncmp(cp, prefix, prefix_len) == 0) + cp += prefix_len; + + return (cp); +} + /* * Call-back function for walking the VC list. */ @@ -245,14 +266,33 @@ smb_vc_cb(uintptr_t addr, const void *data, void *arg) if (cbd->printed_header == 0) { cbd->printed_header = 1; - mdb_printf("// smb_vc_t uid server user\n"); + mdb_printf("// smb_vc_t uid server \tuser\t\tstate\n"); } mdb_printf("%-p", addr); - mdb_printf(" %d", vcp->vc_uid); - print_str((uintptr_t)vcp->vc_srvname); - print_str((uintptr_t)vcp->vc_username); - mdb_printf("\n"); + mdb_printf(" %7d", vcp->vc_owner); + + switch (vcp->vc_srvaddr.sa.sa_family) { + case AF_INET: + mdb_printf(" %I", vcp->vc_srvaddr.sin.sin_addr); + break; + case AF_INET6: + mdb_printf(" %N", &vcp->vc_srvaddr.sin6.sin6_addr); + break; + default: + mdb_printf(" %15s", "(bad af)"); + break; + } + + if (vcp->vc_username[0] != '\0') + mdb_printf("\t%s", vcp->vc_username); + else + mdb_printf("\t%s", "(?)"); + + if (vcp->vc_domain[0] != '\0') + mdb_printf("@%s", vcp->vc_domain); + + mdb_printf("\t%s\n", vcstate_str(cbd, vcp->vc_state)); if (cbd->flags & OPT_RECURSE) { mdb_inc_indent(2); @@ -282,6 +322,10 @@ smb_vc_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) return (DCMD_USAGE); } + if (mdb_ctf_lookup_by_name("enum smbiod_state", &cbd.ctf_id) == -1) { + mdb_warn("Could not find enum smbiod_state"); + } + if (!(flags & DCMD_ADDRSPEC)) { if (mdb_walk("nsmb_vc", smb_vc_cb, &cbd) == -1) { mdb_warn("failed to walk 'nsmb_vc'"); diff --git a/usr/src/lib/Makefile b/usr/src/lib/Makefile index 934df8f983..60b65de7d3 100644 --- a/usr/src/lib/Makefile +++ b/usr/src/lib/Makefile @@ -475,6 +475,7 @@ HDRSUBDIRS= \ udapl \ libmapid \ libkrb5 \ + libsmbfs \ libshare \ libidmap \ libvscan \ diff --git a/usr/src/lib/libsmbfs/Makefile b/usr/src/lib/libsmbfs/Makefile index 0ab1f6c137..26e499e2fa 100644 --- a/usr/src/lib/libsmbfs/Makefile +++ b/usr/src/lib/libsmbfs/Makefile @@ -19,16 +19,22 @@ # CDDL HEADER END # # -# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -#ident "%Z%%M% %I% %E% SMI" + # # lib/libsmbfs/Makefile # include $(SRC)/lib/Makefile.lib +HDRS= smbfs_acl.h smbfs_api.h +HDRDIR= netsmb + +ROOTHDRDIR= $(ROOT)/usr/include/netsmb +ROOTHDRS= $(HDRS:%=$(ROOTHDRDIR)/%) + # ISA targets SUBDIRS = $(MACH) $(BUILD64)SUBDIRS += $(MACH64) @@ -40,29 +46,26 @@ clean := TARGET= clean clobber := TARGET= clobber lint := TARGET= lint +MSGFILES = `$(GREP) -l gettext smb/*.[ch]` POFILE = libsmbfs.po .KEEP_STATE: all install clean clobber lint: $(SUBDIRS) -include $(SRC)/Makefile.msg.targ +_msg: $(MSGDOMAINPOFILE) -MSGFILES=smb/cfopt.c smb/charsets.c smb/charsets.h smb/ctx.c smb/derparse.c \ - smb/derparse.h smb/file.c smb/keychain.c smb/mbuf.c smb/nb.c \ - smb/nb_name.c smb/nb_net.c smb/nbns_rq.c smb/netshareenum.c \ - smb/nls.c smb/print.c smb/queue.h smb/rap.c smb/rcfile.c \ - smb/rcfile_priv.h smb/rq.c smb/spnego.c smb/spnego.h \ - smb/spnegoparse.c smb/spnegoparse.h smb/subr.c smb/ui-sun.c +$(POFILE): pofile_MSGFILES -_msg: $(MSGDOMAINPOFILE) +install_h: $(ROOTHDRDIR) $(ROOTHDRS) -$(MSGDOMAINPOFILE): $(POFILE) +check: $(CHECKHDRS) -$(POFILE): $(MSGFILES) - $(BUILDPO.msgfiles) +$(ROOTHDRDIR)/%: % + $(INS.file) -install: $(ROOTLIBS) +$(ROOTHDRDIR): + $(INS.dir) $(SUBDIRS): FRC @cd $@; pwd; $(MAKE) $(TARGET) @@ -70,3 +73,4 @@ $(SUBDIRS): FRC FRC: include $(SRC)/lib/Makefile.targ +include $(SRC)/Makefile.msg.targ diff --git a/usr/src/lib/libsmbfs/Makefile.com b/usr/src/lib/libsmbfs/Makefile.com index a2232234de..656ba731b3 100644 --- a/usr/src/lib/libsmbfs/Makefile.com +++ b/usr/src/lib/libsmbfs/Makefile.com @@ -18,11 +18,15 @@ # # CDDL HEADER END # + # -# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # + +# # lib/libsmbfs/Makefile.com +# LIBRARY= libsmbfs.a VERS= .1 @@ -35,23 +39,38 @@ OBJECTS=\ acl_print.o \ charsets.o \ cfopt.o \ + connect.o \ + crypt.o \ ctx.o \ derparse.o \ file.o \ + findvc.o \ + getaddr.o \ + iod_cl.o \ + iod_wk.o \ keychain.o \ + krb5ssp.o \ mbuf.o \ nb.o \ nb_name.o \ nb_net.o \ + nb_ssn.o \ nbns_rq.o \ + negprot.o \ netshareenum.o \ + newvc.o \ nls.o \ + ntlm.o \ + ntlmssp.o \ print.o \ rap.o \ rcfile.o \ rq.o \ + signing.o \ spnego.o \ spnegoparse.o \ + ssnsetup.o \ + ssp.o \ subr.o \ ui-sun.o \ utf_str.o @@ -68,7 +87,7 @@ $(LINTLIB) := SRCS = $(SRCDIR)/$(LINTSRC) C99MODE= $(C99_ENABLE) -LDLIBS += -lsocket -lnsl -lc -lkrb5 -lsec -lidmap +LDLIBS += -lsocket -lnsl -lc -lmd -lpkcs11 -lkrb5 -lsec -lidmap # normal warnings... CFLAGS += $(CCVERBOSE) @@ -76,25 +95,24 @@ CFLAGS += $(CCVERBOSE) CPPFLAGS += -D__EXTENSIONS__ -D_REENTRANT -DMIA \ -I$(SRCDIR) -I.. -I$(SRC)/uts/common -# uncomment these if you want to use dbx +# Debugging +${NOT_RELEASE_BUILD} CPPFLAGS += -DDEBUG + +# uncomment these for dbx debugging #COPTFLAG = -g #CTF_FLAGS = #CTFCONVERT_O= #CTFMERGE_LIB= # disable some of the less important lint -LINTCHECKFLAGS += -erroff=E_FUNC_ARG_UNUSED LINTCHECKFLAGS += -erroff=E_FUNC_RET_ALWAYS_IGNOR2 LINTCHECKFLAGS += -erroff=E_FUNC_RET_MAYBE_IGNORED2 -LINTCHECKFLAGS += -erroff=E_FUNC_VAR_UNUSED -LINTCHECKFLAGS += -erroff=E_STATIC_UNUSED -LINTCHECKFLAGS += -erroff=E_CONSTANT_CONDITION -LINTCHECKFLAGS += -erroff=E_TRUE_LOGICAL_EXPR - -.KEEP_STATE: +LINTCHECKFLAGS += -DDEBUG all: $(LIBS) lint: lintcheck include ../../Makefile.targ + +.KEEP_STATE: diff --git a/usr/src/lib/libsmbfs/cflib.h b/usr/src/lib/libsmbfs/cflib.h index 0b6941931e..0e8d6b57ee 100644 --- a/usr/src/lib/libsmbfs/cflib.h +++ b/usr/src/lib/libsmbfs/cflib.h @@ -32,7 +32,10 @@ * $Id: cflib.h,v 1.1.1.1 2001/06/09 00:28:11 zarzycki Exp $ */ -#pragma ident "%Z%%M% %I% %E% SMI" +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ #ifndef _CFLIB_H_ #define _CFLIB_H_ @@ -68,6 +71,7 @@ typedef struct opt_args opt_args_t; extern int cf_opterr, cf_optind, cf_optopt, cf_optreset; extern const char *cf_optarg; +extern struct rcfile *smb_rc; #ifdef __cplusplus extern "C" { @@ -78,16 +82,17 @@ int opt_args_parse(struct rcfile *, struct opt_args *, const char *, int opt_args_parseopt(struct opt_args *, int, char *, opt_callback_t *); int cf_getopt(int, char * const *, const char *); +void cf_opt_lock(void); +void cf_opt_unlock(void); -int rc_open(const char *, const char *, struct rcfile **); -int rc_close(struct rcfile *); -int rc_merge(const char *, struct rcfile **); -int rc_merge_pipe(const char *, struct rcfile **); int rc_getstringptr(struct rcfile *, const char *, const char *, char **); int rc_getstring(struct rcfile *, const char *, const char *, size_t, char *); int rc_getint(struct rcfile *, const char *, const char *, int *); int rc_getbool(struct rcfile *, const char *, const char *, int *); +int smb_open_rcfile(char *); +void smb_close_rcfile(void); + #ifdef __cplusplus } #endif diff --git a/usr/src/lib/libsmbfs/netsmb/nb_lib.h b/usr/src/lib/libsmbfs/netsmb/nb_lib.h index d2403edf75..b758050959 100644 --- a/usr/src/lib/libsmbfs/netsmb/nb_lib.h +++ b/usr/src/lib/libsmbfs/netsmb/nb_lib.h @@ -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. */ @@ -107,10 +107,11 @@ struct sockaddr; extern "C" { #endif -int nb_resolvehost_in(const char *, struct sockaddr **); +struct rcfile; +int nb_resolvehost_in(const char *, struct in_addr *); 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 nbns_getnodestatus(struct nb_ctx *ctx, struct in_addr *, + char *system, char *workgroup); int nb_getlocalname(char *name, size_t maxlen); const char *nb_strerror(int error); diff --git a/usr/src/lib/libsmbfs/netsmb/smb_lib.h b/usr/src/lib/libsmbfs/netsmb/smb_lib.h index bec4a1c0b5..c13cb1b648 100644 --- a/usr/src/lib/libsmbfs/netsmb/smb_lib.h +++ b/usr/src/lib/libsmbfs/netsmb/smb_lib.h @@ -33,31 +33,29 @@ */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _NETSMB_SMB_LIB_H_ #define _NETSMB_SMB_LIB_H_ +/* + * Internal interface exported to our commands in: + * usr/src/cmd/fs.d/smbclnt/ + */ + #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <sys/byteorder.h> -#include <netsmb/smb.h> +#include <netsmb/smbfs_api.h> #include <netsmb/smb_dev.h> -#define SMB_CFG_FILE "/etc/nsmb.conf" -#define OLD_SMB_CFG_FILE "/usr/local/etc/nsmb.conf" - -#define STDPARAM_ARGS \ - 'A':case 'B':case 'C':case 'E':case 'I':case 'L':case \ - 'M':case 'N':case 'U':case 'R':case 'S':case 'T':case \ - 'W':case 'O':case 'P' - -#define STDPARAM_OPT "ABCE:I:L:M:NO:P:U:R:S:T:W:" +extern const char smbutil_std_opts[]; +#define STDPARAM_OPT smbutil_std_opts /* * bits to indicate the source of error @@ -68,33 +66,94 @@ #define SMB_NB_ERROR 0x20000 /* + * Size of all LM/NTLM hashes (16 bytes). + * The driver needs to know this, so it's + * defined by smb_dev.h + */ +#define NTLM_HASH_SZ SMBIOC_HASH_SZ +#define NTLM_CHAL_SZ 8 /* challenge size */ + +/* + * This is what goes across the door call to the IOD + * when asking for a new connection. + */ +struct smb_iod_ssn { + struct smbioc_ossn iod_ossn; + int iod_authflags; /* SMB_AT_x */ + uchar_t iod_nthash[NTLM_HASH_SZ]; + uchar_t iod_lmhash[NTLM_HASH_SZ]; + /* Kerberos cred. cache res. name? */ +}; +typedef struct smb_iod_ssn smb_iod_ssn_t; + + +/* * SMB work context. Used to store all values which are necessary * to establish connection to an SMB server. */ struct smb_ctx { int ct_flags; /* SMBCF_ */ - int ct_fd; /* handle of connection */ + int ct_dev_fd; /* device handle */ int ct_parsedlevel; int ct_minlevel; int ct_maxlevel; - char *ct_fullserver; /* original server name from cmd line */ - char *ct_srvaddr; /* hostname or IP address of server */ - struct sockaddr_in ct_srvinaddr; /* IP address of server */ - char ct_locname[SMB_MAXUSERNAMELEN + 1]; - struct nb_ctx *ct_nb; - struct smbioc_ossn ct_ssn; - struct smbioc_oshare ct_sh; + char *ct_fullserver; /* orig. server name from cmd line */ + char *ct_srvaddr_s; /* hostname or IP address of server */ + struct addrinfo *ct_addrinfo; /* IP addresses of the server */ + struct nb_ctx *ct_nb; /* NetBIOS info. */ + char *ct_locname; /* local (machine) name */ + smb_iod_ssn_t ct_iod_ssn; + /* smbioc_oshare_t ct_sh; XXX */ + int ct_minauth; + int ct_shtype_req; /* share type wanted */ char *ct_origshare; char *ct_home; - void *ct_secblob; - int ct_secbloblen; - /* krb5 stuff: all anonymous struct pointers here. */ - struct _krb5_context *ct_krb5ctx; - struct _krb5_ccache *ct_krb5cc; /* credentials cache */ - struct krb5_principal_data *ct_krb5cp; /* client principal */ + + /* Connection setup SMB stuff. */ + /* Strings from the SMB negotiate response. */ + char *ct_srv_OS; + char *ct_srv_LM; + + /* NTLM auth. stuff */ + uchar_t ct_clnonce[NTLM_CHAL_SZ]; + uchar_t ct_ntlm_chal[NTLM_CHAL_SZ]; + char ct_password[SMBIOC_MAX_NAME]; + + /* See ssp.c */ + void *ct_ssp_ctx; + smbioc_ssn_work_t ct_work; }; -typedef struct smb_ctx smb_ctx_t; + +/* + * Short-hand for some of the substruct fields above + */ +#define ct_ssn ct_iod_ssn.iod_ossn +#define ct_vopt ct_iod_ssn.iod_ossn.ssn_vopt +#define ct_owner ct_iod_ssn.iod_ossn.ssn_owner +#define ct_srvaddr ct_iod_ssn.iod_ossn.ssn_srvaddr +#define ct_domain ct_iod_ssn.iod_ossn.ssn_domain +#define ct_user ct_iod_ssn.iod_ossn.ssn_user +#define ct_srvname ct_iod_ssn.iod_ossn.ssn_srvname +#define ct_authflags ct_iod_ssn.iod_authflags +#define ct_nthash ct_iod_ssn.iod_nthash +#define ct_lmhash ct_iod_ssn.iod_lmhash + +#define ct_sopt ct_work.wk_sopt +#define ct_iods ct_work.wk_iods +#define ct_tran_fd ct_work.wk_iods.is_tran_fd +#define ct_hflags ct_work.wk_iods.is_hflags +#define ct_hflags2 ct_work.wk_iods.is_hflags2 +#define ct_vcflags ct_work.wk_iods.is_vcflags +#define ct_ssn_key ct_work.wk_iods.is_ssn_key +#define ct_mac_seqno ct_work.wk_iods.is_next_seq +#define ct_mackeylen ct_work.wk_iods.is_u_maclen +#define ct_mackey ct_work.wk_iods.is_u_mackey.lp_ptr + + +/* + * Bits in smb_ctx_t.ct_flags + */ #define SMBCF_NOPWD 0x0001 /* don't ask for a password */ #define SMBCF_SRIGHTS 0x0002 /* share access rights supplied */ #define SMBCF_LOCALE 0x0004 /* use current locale */ @@ -111,100 +170,35 @@ typedef struct smb_ctx smb_ctx_t; #define SMBCF_SSNACTIVE 0x02000000 /* session setup succeeded */ #define SMBCF_KCDOMAIN 0x04000000 /* use domain in KC lookup */ -/* - * access modes (see also smb_dev.h) - */ -#define SMBM_READ S_IRUSR /* read conn attrs. (like list shares) */ -#define SMBM_WRITE S_IWUSR /* modify conn attrs */ -#define SMBM_EXEC S_IXUSR /* can send SMB requests */ -#define SMBM_READGRP S_IRGRP -#define SMBM_WRITEGRP S_IWGRP -#define SMBM_EXECGRP S_IXGRP -#define SMBM_READOTH S_IROTH -#define SMBM_WRITEOTH S_IWOTH -#define SMBM_EXECOTH S_IXOTH -#define SMBM_ALL S_IRWXU -#define SMBM_DEFAULT S_IRWXU - /* - * Share type for smb_ctx_init + * Context management */ -#define SMB_ST_DISK STYPE_DISKTREE -#define SMB_ST_PRINTER STYPE_PRINTQ -#define SMB_ST_COMM STYPE_DEVICE -#define SMB_ST_PIPE STYPE_IPC -#define SMB_ST_ANY STYPE_UNKNOWN -#define SMB_ST_MAX STYPE_UNKNOWN -#define SMB_ST_NONE 0xff /* not a part of protocol */ - -struct mbdata { - struct mbuf *mb_top; - struct mbuf *mb_cur; - char *mb_pos; - int mb_count; -}; -typedef struct mbdata mbdata_t; -struct smb_bitname { - uint_t bn_bit; - char *bn_name; -}; -typedef struct smb_bitname smb_bitname_t; +int smb_ctx_init(struct smb_ctx *); +void smb_ctx_done(struct smb_ctx *); +int smb_open_driver(void); -extern int smb_debug, smb_verbose; -extern struct rcfile *smb_rc; +int smb_ctx_gethandle(struct smb_ctx *); +int smb_ctx_findvc(struct smb_ctx *); +int smb_ctx_newvc(struct smb_ctx *); -#ifdef __cplusplus -extern "C" { -#endif +/* + * I/O daemon stuff + */ -int smb_lib_init(void); -int smb_open_driver(void); -int smb_open_rcfile(struct smb_ctx *ctx); -void smb_error(const char *, int, ...); -char *smb_printb(char *, int, const struct smb_bitname *); +int smb_iod_cl_newvc(smb_ctx_t *ctx); +char *smb_iod_door_path(void); +int smb_iod_open_door(int *); +int smb_iod_connect(struct smb_ctx *); +int smb_iod_work(struct smb_ctx *); /* - * Context management + * Other stuff */ -int smb_ctx_init(struct smb_ctx *, int, char *[], int, int, int); -void smb_ctx_done(struct smb_ctx *); -int smb_ctx_parseunc(struct smb_ctx *, const char *, int, const char **); -int smb_ctx_setcharset(struct smb_ctx *, const char *); -int smb_ctx_setfullserver(struct smb_ctx *, const char *); -void smb_ctx_setserver(struct smb_ctx *, const char *); -int smb_ctx_setuser(struct smb_ctx *, const char *, int); -int smb_ctx_setshare(struct smb_ctx *, const char *, int); -int smb_ctx_setscope(struct smb_ctx *, const char *); -int smb_ctx_setworkgroup(struct smb_ctx *, const char *, int); -int smb_ctx_setpassword(struct smb_ctx *, const char *, int); -int smb_ctx_setsrvaddr(struct smb_ctx *, const char *); -int smb_ctx_opt(struct smb_ctx *, int, const char *); -int smb_ctx_findvc(struct smb_ctx *, int, int); -int smb_ctx_negotiate(struct smb_ctx *, int, int, char *); -int smb_ctx_tdis(struct smb_ctx *ctx); -int smb_ctx_lookup(struct smb_ctx *, int, int); -int smb_ctx_login(struct smb_ctx *); -int smb_ctx_readrc(struct smb_ctx *); -int smb_ctx_resolve(struct smb_ctx *); -int smb_ctx_setflags(struct smb_ctx *, int, int, int); -int smb_ctx_flags2(struct smb_ctx *); - -int smb_smb_open_print_file(struct smb_ctx *, int, int, const char *, smbfh*); -int smb_smb_close_print_file(struct smb_ctx *, smbfh); - -typedef void (*smb_ctx_close_hook_t)(struct smb_ctx *); -void smb_ctx_set_close_hook(smb_ctx_close_hook_t); -int smb_fh_close(struct smb_ctx *ctx, smbfh fh); -int smb_fh_open(struct smb_ctx *ctx, const char *, int, smbfh *); -int smb_fh_read(struct smb_ctx *, smbfh, off_t, size_t, char *); -int smb_fh_write(struct smb_ctx *, smbfh, off_t, size_t, const char *); -int smb_fh_xactnp(struct smb_ctx *, smbfh, int, const char *, - int *, char *, int *); - -int smb_t2_request(struct smb_ctx *, int, uint16_t *, const char *, - int, void *, int, void *, int *, void *, int *, void *, int *); + +int smb_open_rcfile(char *); +void smb_close_rcfile(void); void smb_simplecrypt(char *dst, const char *src); int smb_simpledecrypt(char *dst, const char *src); @@ -218,19 +212,7 @@ void *nls_mem_toloc(void *, const void *, int); char *nls_str_upper(char *, const char *); char *nls_str_lower(char *, const char *); -int smb_get_authentication(char *, size_t, char *, size_t, char *, size_t, - const char *, struct smb_ctx *); -int smb_browse(struct smb_ctx *, int); -void smb_save2keychain(struct smb_ctx *); -#define smb_autherr(e) ((e) == EAUTH || (e) == EACCES || (e) == EPERM) -char *smb_strerror(int); char *smb_getprogname(); #define __progname smb_getprogname() -extern char *unpercent(char *component); - -#ifdef __cplusplus -} -#endif - #endif /* _NETSMB_SMB_LIB_H_ */ diff --git a/usr/src/lib/libsmbfs/netsmb/smbfs_acl.h b/usr/src/lib/libsmbfs/netsmb/smbfs_acl.h index dc8972e5cb..d1da5bda22 100644 --- a/usr/src/lib/libsmbfs/netsmb/smbfs_acl.h +++ b/usr/src/lib/libsmbfs/netsmb/smbfs_acl.h @@ -20,15 +20,13 @@ */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _NETSMB_SMBFS_ACL_H #define _NETSMB_SMBFS_ACL_H -#pragma ident "%Z%%M% %I% %E% SMI" - /* * Get/set ACL via contracted interface in libsmbfs. * The ACL is in the form used by libsec (type=ACE_T) @@ -37,6 +35,10 @@ #include <sys/acl.h> +#ifdef __cplusplus +extern "C" { +#endif + /* * Get a ZFS-style acl from an FD opened in smbfs. * Intentionally similar to: facl_get(3SEC) @@ -87,6 +89,12 @@ int smbfs_acl_sd2zfs(i_ntsd_t *, acl_t *, uid_t *, gid_t *); int smbfs_acl_zfs2sd(acl_t *, uid_t, gid_t, i_ntsd_t **); void smbfs_acl_free_sd(i_ntsd_t *); -void smbfs_acl_print_sd(FILE *, i_ntsd_t *); + +struct __FILE; +void smbfs_acl_print_sd(struct __FILE *, i_ntsd_t *); + +#ifdef __cplusplus +} +#endif #endif /* _NETSMB_SMBFS_ACL_H */ diff --git a/usr/src/lib/libsmbfs/netsmb/smbfs_api.h b/usr/src/lib/libsmbfs/netsmb/smbfs_api.h new file mode 100644 index 0000000000..eeeb17ea1a --- /dev/null +++ b/usr/src/lib/libsmbfs/netsmb/smbfs_api.h @@ -0,0 +1,158 @@ +/* + * 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 _NETSMB_SMBFS_API_H +#define _NETSMB_SMBFS_API_H + +/* + * Define the API exported to our commands and to the + * MS-style RPC-over-named-pipes library (mlrpc). + */ + +#include <sys/types.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Some errno values we need to expose in this API. + * NB: These two defines are duplicated from the + * driver smb_dev.h to avoid exposing that here. + * + * EBADRPC is used for message decoding errors. + * EAUTH is used for CIFS authentication errors. + */ +#ifndef EBADRPC +#define EBADRPC 113 +#endif +#ifndef EAUTH +#define EAUTH 114 +#endif + + +/* + * Share type values for smb_ctx_new, _init + * Based on NetUseAdd() USE_INFO_[12] _asg_type values + * They also happen to match: STYPE_DISKTREE, etc. + */ +typedef enum { + USE_WILDCARD = -1, + USE_DISKDEV, + USE_SPOOLDEV, + USE_CHARDEV, + USE_IPC +} smb_use_shtype_t; + +/* + * Parse "level" spec. for smb_ctx_parseunc() + * i.e. whether we require a share name, etc. + */ +typedef enum { + SMBL_NONE = 0, /* have nothing */ + SMBL_SERVER, /* have server */ + SMBL_VC = 1, /* alias for _SERVER */ + SMBL_SHARE, /* have server share */ + SMBL_PATH /* have server share path */ +} smb_parse_level_t; + +/* + * Authentication type flags + * See: smb_ctx_setauthflags() + */ +#define SMB_AT_ANON 1 /* anonymous (NULL session) */ +#define SMB_AT_LM1 2 /* LM1 (with NTLM) */ +#define SMB_AT_NTLM1 4 /* NTLM (v1) */ +#define SMB_AT_NTLM2 8 /* NTLMv2 */ +#define SMB_AT_KRB5 0x10 /* Kerberos5 (AD) */ +#define SMB_AT_DEFAULT (SMB_AT_KRB5 | SMB_AT_NTLM2 | SMB_AT_NTLM1) + +struct smb_ctx; /* anonymous here; real one in smb_lib.h */ +typedef struct smb_ctx smb_ctx_t; + +extern int smb_debug, smb_verbose; + +int smb_lib_init(void); +void smb_error(const char *, int, ...); + +/* + * Context management + */ +int smb_ctx_alloc(struct smb_ctx **); +void smb_ctx_free(struct smb_ctx *); +int smb_ctx_kill(struct smb_ctx *); + +int smb_ctx_scan_argv(struct smb_ctx *, int, char **, int, int, int); +int smb_ctx_parseunc(struct smb_ctx *, const char *, int, int, int, + const char **); +int smb_ctx_readrc(struct smb_ctx *); +int smb_ctx_opt(struct smb_ctx *, int, const char *); +int smb_get_authentication(struct smb_ctx *); + +int smb_ctx_flags2(struct smb_ctx *); +int smb_ctx_resolve(struct smb_ctx *); +int smb_ctx_get_ssn(struct smb_ctx *); +int smb_ctx_get_ssnkey(struct smb_ctx *, uchar_t *, size_t); +int smb_ctx_get_tree(struct smb_ctx *); + +int smb_ctx_setauthflags(struct smb_ctx *, int); +int smb_ctx_setcharset(struct smb_ctx *, const char *); +int smb_ctx_setflags(struct smb_ctx *, int, int, int); +int smb_ctx_setfullserver(struct smb_ctx *, const char *); +int smb_ctx_setscope(struct smb_ctx *, const char *); +int smb_ctx_setwins(struct smb_ctx *, const char *, const char *); + +int smb_ctx_setsrvaddr(struct smb_ctx *, const char *); +int smb_ctx_setserver(struct smb_ctx *, const char *); +int smb_ctx_setshare(struct smb_ctx *, const char *, int); + +int smb_ctx_setdomain(struct smb_ctx *, const char *, int); +int smb_ctx_setuser(struct smb_ctx *, const char *, int); +int smb_ctx_setpassword(struct smb_ctx *, const char *, int); +int smb_ctx_setpwhash(struct smb_ctx *, const uchar_t *, const uchar_t *); + +typedef void (*smb_ctx_close_hook_t)(struct smb_ctx *); +void smb_ctx_set_close_hook(smb_ctx_close_hook_t); +int smb_fh_close(struct smb_ctx *ctx, int); +int smb_fh_open(struct smb_ctx *ctx, const char *, int, int *); +int smb_fh_read(struct smb_ctx *, int, off_t, size_t, char *); +int smb_fh_write(struct smb_ctx *, int, off_t, size_t, const char *); +int smb_fh_xactnp(struct smb_ctx *, int, int, const char *, + int *, char *, int *); + +int smb_t2_request(struct smb_ctx *, int, uint16_t *, const char *, + int, void *, int, void *, int *, void *, int *, void *, int *); + +int smb_printer_open(struct smb_ctx *, int, int, const char *, int *); +int smb_printer_close(struct smb_ctx *, int); + +char *smb_strerror(int); + +#ifdef __cplusplus +} +#endif + +#endif /* _NETSMB_SMBFS_API_H */ 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/netsmb/smbfs_isec.h b/usr/src/lib/libsmbfs/smb/acl_nt.h index f6b3555345..844a7e6543 100644 --- a/usr/src/lib/libsmbfs/netsmb/smbfs_isec.h +++ b/usr/src/lib/libsmbfs/smb/acl_nt.h @@ -20,17 +20,16 @@ */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ -#ifndef _SMBFS_ISEC_H -#define _SMBFS_ISEC_H - -#pragma ident "%Z%%M% %I% %E% SMI" +#ifndef _ACL_NT_H +#define _ACL_NT_H /* - * Internal Security Descriptor (SD) + * Internal functions for dealing with + * NT Security data structures. */ #include <netsmb/smbfs_acl.h> @@ -78,36 +77,37 @@ struct i_ntsd { 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(mbdata_t *mbp, i_ntsd_t **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(mbdata_t *mbp, i_ntsd_t *sd); +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, mbdata_t *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, mbdata_t *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 /* _SMBFS_ISEC_H */ +#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, >ok, >oklen))) - 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, >ok, >oklen)) != 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)); +} diff --git a/usr/src/pkgdefs/SUNWsmbfsu/prototype_com b/usr/src/pkgdefs/SUNWsmbfsu/prototype_com index 985c9cecb4..e1a79c3742 100644 --- a/usr/src/pkgdefs/SUNWsmbfsu/prototype_com +++ b/usr/src/pkgdefs/SUNWsmbfsu/prototype_com @@ -19,7 +19,7 @@ # CDDL HEADER END # # -# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # This required package information file contains a list of package contents. @@ -66,3 +66,5 @@ d none usr/lib/mdb/kvm 755 root sys d none usr/lib/security 755 root bin f none usr/lib/security/pam_smbfs_login.so.1 755 root bin s none usr/lib/security/pam_smbfs_login.so=pam_smbfs_login.so.1 +d none usr/lib/smbfs 755 root bin +f none usr/lib/smbfs/smbiod 555 root bin diff --git a/usr/src/pkgdefs/etc/exception_list_i386 b/usr/src/pkgdefs/etc/exception_list_i386 index d284303d1c..c6258111ff 100644 --- a/usr/src/pkgdefs/etc/exception_list_i386 +++ b/usr/src/pkgdefs/etc/exception_list_i386 @@ -1128,6 +1128,9 @@ usr/include/sys/sdcard/sda_ioctl.h i386 # # libsmbfs is private # +usr/include/netsmb i386 +usr/include/netsmb/smbfs_acl.h i386 +usr/include/netsmb/smbfs_api.h i386 usr/lib/libsmbfs.so i386 usr/lib/amd64/libsmbfs.so i386 usr/lib/llib-lsmbfs i386 diff --git a/usr/src/pkgdefs/etc/exception_list_sparc b/usr/src/pkgdefs/etc/exception_list_sparc index dc12bf72ae..0d4b524242 100644 --- a/usr/src/pkgdefs/etc/exception_list_sparc +++ b/usr/src/pkgdefs/etc/exception_list_sparc @@ -1206,6 +1206,9 @@ usr/include/sys/sdcard/sda_ioctl.h sparc # # libsmbfs is private # +usr/include/netsmb sparc +usr/include/netsmb/smbfs_acl.h sparc +usr/include/netsmb/smbfs_api.h sparc usr/lib/libsmbfs.so sparc usr/lib/sparcv9/libsmbfs.so sparc usr/lib/llib-lsmbfs sparc diff --git a/usr/src/tools/scripts/bfu.sh b/usr/src/tools/scripts/bfu.sh index c9258350af..a2ada6d6eb 100644 --- a/usr/src/tools/scripts/bfu.sh +++ b/usr/src/tools/scripts/bfu.sh @@ -7490,10 +7490,16 @@ mondo_loop() { rm -f $root/usr/sbin/pfild # Remove nsmb and smbfs modules from old locations - # Also remove new locations of kmdb stuff for BFU + # Also remove new locations of moved stuff for BFU # from newer to older build ("backward BFU"). # These will be reinstalled from the archive. # old locations: + rm -f $root/kernel/drv/nsmb + rm -f $root/kernel/drv/amd64/nsmb + rm -f $root/kernel/drv/sparcv9/nsmb + rm -f $root/kernel/fs/smbfs + rm -f $root/kernel/fs/amd64/smbfs + rm -f $root/kernel/fs/sparcv9/smbfs rm -f $root/kernel/kmdb/nsmb rm -f $root/kernel/kmdb/smbfs rm -f $root/kernel/kmdb/amd64/nsmb @@ -7504,6 +7510,12 @@ mondo_loop() { rm -f $usr/kernel/sys/amd64/smbfs rm -f $usr/kernel/sys/sparcv9/smbfs # new locations: + rm -f $usr/kernel/drv/nsmb + rm -f $usr/kernel/drv/amd64/nsmb + rm -f $usr/kernel/drv/sparcv9/nsmb + rm -f $usr/kernel/fs/smbfs + rm -f $usr/kernel/fs/amd64/smbfs + rm -f $usr/kernel/fs/sparcv9/smbfs rm -f $usr/kernel/kmdb/nsmb rm -f $usr/kernel/kmdb/smbfs rm -f $usr/kernel/kmdb/amd64/nsmb diff --git a/usr/src/uts/common/Makefile.files b/usr/src/uts/common/Makefile.files index c99d41bb4e..4201a1c0f3 100644 --- a/usr/src/uts/common/Makefile.files +++ b/usr/src/uts/common/Makefile.files @@ -1233,8 +1233,8 @@ UFS_OBJS += ufs_alloc.o ufs_bmap.o ufs_dir.o ufs_xattr.o \ lufs_log.o lufs_map.o lufs_top.o lufs_debug.o VSCAN_OBJS += vscan_drv.o vscan_svc.o vscan_door.o -NSMB_OBJS += smb_conn.o smb_crypt.o smb_dev.o smb_iod.o \ - smb_rq.o smb_smb.o smb_tran.o smb_trantcp.o \ +NSMB_OBJS += smb_conn.o smb_dev.o smb_iod.o smb_rq.o \ + smb_sign.o smb_smb.o smb_tran.o smb_trantcp.o \ smb_usr.o smb_subrs.o subr_mchain.o smb_pass.o SMBFS_OBJS += smbfs_vfsops.o smbfs_vnops.o smbfs_node.o \ diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/offsets.in b/usr/src/uts/common/fs/smbclnt/netsmb/offsets.in index 6c39fa5a22..61c06a738c 100644 --- a/usr/src/uts/common/fs/smbclnt/netsmb/offsets.in +++ b/usr/src/uts/common/fs/smbclnt/netsmb/offsets.in @@ -18,18 +18,18 @@ \ \ CDDL HEADER END \ + \ -\ Copyright 2007 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" - \ \ offsets.in: input file for the ctfstabs program, \ used to generate ioc_check.h - which verifies \ invariance of our ioctl data structures across \ 32-bit and 64-bit ABIs. +\ #ifndef _GENASSYM #define _GENASSYM @@ -43,56 +43,73 @@ #include <netsmb/netbios.h> #include <netsmb/smb_dev.h> -sockaddr_any SIZEOF_SOCKADDR_ANY - -sockaddr_in SIZEOF_SOCKADDR_IN - -sockaddr_nb SIZEOF_SOCKADDR_NB - -smbioc_ossn SIZEOF_SMBIOC_OSSN - ioc_server - ioc_local - ioc_localcs - ioc_servercs - ioc_srvname - ioc_user - ioc_workgroup - ioc_password IOC_SSN_PASSWD - ioc_opt IOC_SSN_OPT - ioc_timeout - ioc_retrycount - ioc_owner IOC_SSN_OWNER - ioc_group IOC_SSN_GROUP - ioc_mode IOC_SSN_MODE - ioc_rights IOC_SSN_RIGHTS - ioc_intoklen - ioc_outtoklen - _ioc_intok - _ioc_outtok - -smbioc_oshare SIZEOF_SMBIOC_OSHARE - ioc_share - ioc_password IOC_SH_PASSWD - ioc_opt IOC_SH_OPT - ioc_stype - ioc_owner IOC_SH_OWNER - ioc_group IOC_SH_GROUP - ioc_mode IOC_SH_MODE - ioc_rights IOC_SH_RIGHTS +smbioc_sockaddr + +smbioc_ssn_ident + id_srvaddr + id_domain + id_user + +smbioc_ossn + ssn_vopt + ssn_owner + ssn_id + ssn_srvname + +smbioc_oshare + sh_pwlen + sh_name + sh_pass + sh_type_req + sh_type_ret + +smbioc_tcon + tc_flags + tc_opt + tc_sh + +smb_sopt + sv_proto + sv_sm + sv_tz + sv_maxmux + sv_maxvcs + sv_rawmode + sv_maxtx + sv_maxraw + sv_skey + sv_caps + +smb_iods + is_tran_fd + is_vcflags + is_hflags + is_hflags2 + is_smbuid + is_next_mid + is_txmax + is_rwmax + is_rxmax + is_wxmax + is_ssn_key + is_next_seq + is_u_maclen + is_u_mackey + +smbioc_ssn_work + wk_iods + wk_sopt + wk_out_state smbioc_rq SIZEOF_SMBIOC_RQ ioc_cmd - ioc_twc - ioc_tbc - ioc_rpbufsz - ioc_rwc - ioc_rbc ioc_errclass IOC_RQ_ERRCLASS ioc_serror IOC_RQ_SERROR ioc_error IOC_RQ_ERROR - _ioc_twords - _ioc_tbytes - _ioc_rpbuf + ioc_tbufsz + ioc_rbufsz + _ioc_tbuf + _ioc_rbuf smbioc_t2rq SIZEOF_SMBIOC_T2RQ ioc_setup @@ -116,12 +133,6 @@ smbioc_flags SIZEOF_SMBIOC_FLAGS ioc_mask ioc_flags -smbioc_lookup SIZEOF_SMBIOC_LOOKUP - ioc_level IOC_LOOK_LEVEL - ioc_flags IOC_LOOK_FLAGS - ioc_sh - ioc_ssn - smbioc_rw SIZEOF_SMBIOC_RW ioc_fh ioc_cnt @@ -129,6 +140,8 @@ smbioc_rw SIZEOF_SMBIOC_RW _ioc_base smbioc_pk SIZEOF_SMBIOC_PK + pk_uid pk_dom pk_usr - pk_pass + pk_lmhash + pk_nthash diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/smb_conn.c b/usr/src/uts/common/fs/smbclnt/netsmb/smb_conn.c index 42475923d3..102b57fe28 100644 --- a/usr/src/uts/common/fs/smbclnt/netsmb/smb_conn.c +++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_conn.c @@ -32,7 +32,7 @@ * $Id: smb_conn.c,v 1.27.166.1 2005/05/27 02:35:29 lindak Exp $ */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -57,6 +57,7 @@ #include <sys/cmn_err.h> #include <sys/thread.h> #include <sys/atomic.h> +#include <sys/u8_textprep.h> #ifdef APPLE #include <sys/smb_apple.h> @@ -72,7 +73,6 @@ #include <netsmb/smb_pass.h> static struct smb_connobj smb_vclist; -static uint_t smb_vcnext = 0; /* next unique id for VC */ void smb_co_init(struct smb_connobj *cp, int level, char *objname); void smb_co_done(struct smb_connobj *cp); @@ -80,19 +80,12 @@ void smb_co_hold(struct smb_connobj *cp); void smb_co_rele(struct smb_connobj *cp); void smb_co_kill(struct smb_connobj *cp); -#ifdef APPLE -static void smb_sm_lockvclist(void); -static void smb_sm_unlockvclist(void); -#endif - static void smb_vc_free(struct smb_connobj *cp); static void smb_vc_gone(struct smb_connobj *cp); static void smb_share_free(struct smb_connobj *cp); static void smb_share_gone(struct smb_connobj *cp); -/* smb_dup_sockaddr moved to smb_tran.c */ - int smb_sm_init(void) { @@ -125,401 +118,7 @@ smb_sm_done(void) smb_co_done(&smb_vclist); } -/* - * Find a VC identified by the info in vcspec, - * and return it with a "hold", but not locked. - */ -/*ARGSUSED*/ -static int -smb_sm_lookupvc( - struct smb_vcspec *vcspec, - struct smb_cred *scred, - struct smb_vc **vcpp) -{ - struct smb_connobj *co; - struct smb_vc *vcp; - zoneid_t zoneid = getzoneid(); - - ASSERT(MUTEX_HELD(&smb_vclist.co_lock)); - - /* var, head, next_field */ - SLIST_FOREACH(co, &smb_vclist.co_children, co_next) { - vcp = CPTOVC(co); - - /* - * Some things we can check without - * holding the lock (those that are - * set at creation and never change). - */ - - /* VCs in other zones are invisibile. */ - if (vcp->vc_zoneid != zoneid) - continue; - - /* Also segregate by owner. */ - if (vcp->vc_uid != vcspec->owner) - continue; - - /* XXX: we ignore the group. Remove vc_gid? */ - - /* server */ - if (smb_cmp_sockaddr(vcp->vc_paddr, vcspec->sap)) - continue; - - /* domain+user */ - if (strcmp(vcp->vc_domain, vcspec->domain)) - continue; - if (strcmp(vcp->vc_username, vcspec->username)) - continue; - - SMB_VC_LOCK(vcp); - - /* No new references allowed when _GONE is set */ - if (vcp->vc_flags & SMBV_GONE) - goto unlock_continue; - - if (vcp->vc_vopt & SMBVOPT_PRIVATE) - goto unlock_continue; - - found: - /* - * Success! (Found one we can use) - * Return with it held, unlocked. - * In-line smb_vc_hold here. - */ - co->co_usecount++; - SMB_VC_UNLOCK(vcp); - *vcpp = vcp; - return (0); - - unlock_continue: - SMB_VC_UNLOCK(vcp); - /* keep looking. */ - } - - return (ENOENT); -} - -int -smb_sm_findvc( - struct smb_vcspec *vcspec, - struct smb_cred *scred, - struct smb_vc **vcpp) -{ - struct smb_vc *vcp; - int error; - - *vcpp = vcp = NULL; - - SMB_CO_LOCK(&smb_vclist); - error = smb_sm_lookupvc(vcspec, scred, &vcp); - SMB_CO_UNLOCK(&smb_vclist); - - /* Return if smb_sm_lookupvc fails */ - if (error != 0) - return (error); - - /* Ingore any VC that's not active. */ - if (vcp->vc_state != SMBIOD_ST_VCACTIVE) { - smb_vc_rele(vcp); - return (ENOENT); - } - - /* Active VC. Return it held. */ - *vcpp = vcp; - return (error); -} - -int -smb_sm_negotiate( - struct smb_vcspec *vcspec, - struct smb_cred *scred, - struct smb_vc **vcpp) -{ - struct smb_vc *vcp; - clock_t tmo; - int created, error; - -top: - *vcpp = vcp = NULL; - - SMB_CO_LOCK(&smb_vclist); - error = smb_sm_lookupvc(vcspec, scred, &vcp); - if (error) { - /* The VC was not found. Create? */ - if ((vcspec->optflags & SMBVOPT_CREATE) == 0) { - SMB_CO_UNLOCK(&smb_vclist); - return (error); - } - error = smb_vc_create(vcspec, scred, &vcp); - if (error) { - /* Could not create? Unusual. */ - SMB_CO_UNLOCK(&smb_vclist); - return (error); - } - /* Note: co_usecount == 1 */ - created = 1; - } else - created = 0; - SMB_CO_UNLOCK(&smb_vclist); - - if (created == 0) { - /* - * Found an existing VC. Reuse it, but first, - * wait for any other thread doing setup, etc. - * Note: We hold a reference on the VC. - */ - error = 0; - SMB_VC_LOCK(vcp); - while (vcp->vc_state < SMBIOD_ST_VCACTIVE) { - if (vcp->vc_flags & SMBV_GONE) - break; - tmo = lbolt + SEC_TO_TICK(2); - tmo = cv_timedwait_sig(&vcp->vc_statechg, - &vcp->vc_lock, tmo); - if (tmo == 0) { - error = EINTR; - break; - } - } - SMB_VC_UNLOCK(vcp); - - /* Interrupted? */ - if (error) - goto out; - - /* - * Was there a vc_kill while we waited? - * If so, this VC is gone. Start over. - */ - if (vcp->vc_flags & SMBV_GONE) { - smb_vc_rele(vcp); - goto top; - } - - /* - * The possible states here are: - * SMBIOD_ST_VCACTIVE, SMBIOD_ST_DEAD - * - * SMBIOD_ST_VCACTIVE is the normal case, - * where found a connection ready to use. - * - * We may find vc_state == SMBIOD_ST_DEAD - * if a previous session has disconnected. - * In this case, we'd like to reconnect, - * so take over setting up this VC as if - * this thread had created it. - */ - SMB_VC_LOCK(vcp); - if (vcp->vc_state == SMBIOD_ST_DEAD) { - vcp->vc_state = SMBIOD_ST_NOTCONN; - created = 1; - /* Will signal vc_statechg below */ - } - SMB_VC_UNLOCK(vcp); - } - - if (created) { - /* - * We have a NEW VC, held, but not locked. - */ - - SMBIODEBUG("vc_state=%d\n", vcp->vc_state); - switch (vcp->vc_state) { - - case SMBIOD_ST_NOTCONN: - (void) smb_vc_setup(vcspec, scred, vcp, 0); - vcp->vc_genid++; - /* XXX: Save credentials of caller here? */ - vcp->vc_state = SMBIOD_ST_RECONNECT; - /* FALLTHROUGH */ - - case SMBIOD_ST_RECONNECT: - error = smb_iod_connect(vcp); - if (error) - break; - vcp->vc_state = SMBIOD_ST_TRANACTIVE; - /* FALLTHROUGH */ - - case SMBIOD_ST_TRANACTIVE: - /* XXX: Just pass vcspec instead? */ - vcp->vc_intok = vcspec->tok; - vcp->vc_intoklen = vcspec->toklen; - error = smb_smb_negotiate(vcp, &vcp->vc_scred); - vcp->vc_intok = NULL; - vcp->vc_intoklen = 0; - if (error) - break; - vcp->vc_state = SMBIOD_ST_NEGOACTIVE; - /* FALLTHROUGH */ - - case SMBIOD_ST_NEGOACTIVE: - case SMBIOD_ST_SSNSETUP: - case SMBIOD_ST_VCACTIVE: - /* We can (re)use this VC. */ - error = 0; - break; - - default: - error = EINVAL; - break; - } - - if (error) { - /* - * Leave the VC in a state that allows the - * next open to attempt a new connection. - * This call does the cv_broadcast too, - * so that's in the else part. - */ - smb_iod_disconnect(vcp); - } else { - SMB_VC_LOCK(vcp); - cv_broadcast(&vcp->vc_statechg); - SMB_VC_UNLOCK(vcp); - } - } - -out: - if (error) { - /* - * Undo the hold from lookupvc, - * or destroy if from vc_create. - */ - smb_vc_rele(vcp); - } else { - /* Return it held. */ - *vcpp = vcp; - } - - return (error); -} - - -int -smb_sm_ssnsetup( - struct smb_vcspec *vcspec, - struct smb_cred *scred, - struct smb_vc *vcp) -{ - int error; - - /* - * We have a VC, held, but not locked. - * - * Code from smb_iod_ssnsetup, - * with lots of rework. - */ - - SMBIODEBUG("vc_state=%d\n", vcp->vc_state); - switch (vcp->vc_state) { - - case SMBIOD_ST_NEGOACTIVE: - /* - * This is the state we normally find. - * Calling _setup AGAIN to update the - * flags, security info, etc. - */ - error = smb_vc_setup(vcspec, scred, vcp, 1); - if (error) - break; - vcp->vc_state = SMBIOD_ST_SSNSETUP; - /* FALLTHROUGH */ - - case SMBIOD_ST_SSNSETUP: - /* XXX: Just pass vcspec instead? */ - vcp->vc_intok = vcspec->tok; - vcp->vc_intoklen = vcspec->toklen; - error = smb_smb_ssnsetup(vcp, &vcp->vc_scred); - vcp->vc_intok = NULL; - vcp->vc_intoklen = 0; - if (error) - break; - /* OK, start the reader thread... */ - error = smb_iod_create(vcp); - if (error) - break; - vcp->vc_state = SMBIOD_ST_VCACTIVE; - /* FALLTHROUGH */ - - case SMBIOD_ST_VCACTIVE: - /* We can (re)use this VC. */ - error = 0; - break; - - default: - error = EINVAL; - break; - } - - SMB_VC_LOCK(vcp); - cv_broadcast(&vcp->vc_statechg); - SMB_VC_UNLOCK(vcp); - - return (error); -} - -int -smb_sm_tcon( - struct smb_sharespec *shspec, - struct smb_cred *scred, - struct smb_vc *vcp, - struct smb_share **sspp) -{ - struct smb_share *ssp; - int error; - - *sspp = ssp = NULL; - - if (vcp->vc_state != SMBIOD_ST_VCACTIVE) { - /* - * The wait for vc_state in smb_sm_negotiate - * _should_ get us a VC in the right state. - */ - SMBIODEBUG("bad vc_state=%d\n", vcp->vc_state); - return (ENOTCONN); - } - - SMB_VC_LOCK(vcp); - error = smb_vc_lookupshare(vcp, shspec, scred, &ssp); - if (error) { - /* The share was not found. Create? */ - if ((shspec->optflags & SMBVOPT_CREATE) == 0) { - SMB_VC_UNLOCK(vcp); - return (error); - } - error = smb_share_create(vcp, shspec, scred, &ssp); - if (error) { - /* Could not create? Unusual. */ - SMB_VC_UNLOCK(vcp); - return (error); - } - /* Note: co_usecount == 1 */ - } - SMB_VC_UNLOCK(vcp); - - /* - * We have a share, held, but not locked. - * Make it connected... - */ - SMB_SS_LOCK(ssp); - if (!smb_share_valid(ssp)) - error = smb_share_tcon(ssp); - SMB_SS_UNLOCK(ssp); - - if (error) { - /* - * Undo hold from lookupshare, - * or destroy if from _create. - */ - smb_share_rele(ssp); - } else { - /* Return it held. */ - *sspp = ssp; - } - return (error); -} /* * Common code for connection object @@ -692,161 +291,11 @@ smb_co_kill(struct smb_connobj *co) /* - * Session implementation + * Session objects, which are referred to as "VC" for + * "virtual cirtuit". This has nothing to do with the + * CIFS notion of a "virtual cirtuit". See smb_conn.h */ -/* - * This sets the fields that are allowed to change - * when doing a reconnect. Many others are set in - * smb_vc_create and never change afterwards. - * Don't want domain or user to change here. - */ -int -smb_vc_setup(struct smb_vcspec *vcspec, struct smb_cred *scred, - struct smb_vc *vcp, int is_ss) -{ - int error, minauth; - - /* Just save all the SMBVOPT_ options. */ - vcp->vc_vopt = vcspec->optflags; - - if (is_ss) { - /* Called from smb_sm_ssnsetup */ - - if (vcspec->optflags & SMBVOPT_USE_KEYCHAIN) { - /* - * Get p/w hashes from the keychain. - * The password in vcspec->pass is - * fiction, so don't store it. - */ - error = smb_pkey_getpwh(vcp, scred->vc_ucred); - return (error); - } - - /* - * Note: this can be called more than once - * for a given vcp, so free the old strings. - */ - SMB_STRFREE(vcp->vc_pass); - - /* - * Don't store the cleartext password - * unless the minauth value was changed - * to allow use of cleartext passwords. - * (By default, this is not allowed.) - */ - minauth = vcspec->optflags & SMBVOPT_MINAUTH; - if (minauth == SMBVOPT_MINAUTH_NONE) - vcp->vc_pass = smb_strdup(vcspec->pass); - - /* Compute LM and NTLM hashes. */ - smb_oldlm_hash(vcspec->pass, vcp->vc_lmhash); - smb_ntlmv1hash(vcspec->pass, vcp->vc_nthash); - } - - /* Success! */ - error = 0; - return (error); -} - -/*ARGSUSED*/ -int -smb_vc_create(struct smb_vcspec *vcspec, - struct smb_cred *scred, struct smb_vc **vcpp) -{ - static char objtype[] = "smb_vc"; - struct smb_vc *vcp; - int error = 0; - - ASSERT(MUTEX_HELD(&smb_vclist.co_lock)); - - /* - * Checks for valid uid/gid are now in - * smb_usr_ioc2vcspec, so at this point - * we know the user has right to create - * with the uid/gid in the vcspec. - */ - - vcp = kmem_zalloc(sizeof (struct smb_vc), KM_SLEEP); - - smb_co_init(VCTOCP(vcp), SMBL_VC, objtype); - vcp->vc_co.co_free = smb_vc_free; - vcp->vc_co.co_gone = smb_vc_gone; - - cv_init(&vcp->vc_statechg, objtype, CV_DRIVER, NULL); - sema_init(&vcp->vc_sendlock, 1, objtype, SEMA_DRIVER, NULL); - rw_init(&vcp->iod_rqlock, objtype, RW_DRIVER, NULL); - cv_init(&vcp->iod_exit, objtype, CV_DRIVER, NULL); - - vcp->vc_number = atomic_inc_uint_nv(&smb_vcnext); - vcp->vc_state = SMBIOD_ST_NOTCONN; - vcp->vc_timo = SMB_DEFRQTIMO; - /* - * I think SMB_UID_UNKNOWN is not the correct - * initial value for vc_smbuid. See the long - * comment in smb_iod_sendrq() - */ - vcp->vc_smbuid = SMB_UID_UNKNOWN; /* XXX should be zero */ - vcp->vc_tdesc = &smb_tran_nbtcp_desc; - - /* - * These identify the connection. - */ - vcp->vc_zoneid = getzoneid(); - vcp->vc_uid = vcspec->owner; - vcp->vc_grp = vcspec->group; - vcp->vc_mode = vcspec->rights & SMBM_MASK; - - vcp->vc_domain = smb_strdup(vcspec->domain); - vcp->vc_username = smb_strdup(vcspec->username); - vcp->vc_srvname = smb_strdup(vcspec->srvname); - vcp->vc_paddr = smb_dup_sockaddr(vcspec->sap); - vcp->vc_laddr = smb_dup_sockaddr(vcspec->lap); - -#ifdef NOICONVSUPPORT - /* - * REVISIT - */ - error = iconv_open("tolower", vcspec->localcs, &vcp->vc_tolower); - if (error) - goto errout; - - error = iconv_open("toupper", vcspec->localcs, &vcp->vc_toupper); - if (error) - goto errout; - - if (vcspec->servercs[0]) { - - error = iconv_open(vcspec->servercs, vcspec->localcs, - &vcp->vc_toserver); - if (error) - goto errout; - - error = iconv_open(vcspec->localcs, vcspec->servercs, - &vcp->vc_tolocal); - if (error) - goto errout; - } -#endif /* NOICONVSUPPORT */ - - /* This fills in vcp->vc_tdata */ - if ((error = SMB_TRAN_CREATE(vcp, curproc)) != 0) - goto errout; - - /* Success! */ - smb_co_addchild(&smb_vclist, VCTOCP(vcp)); - *vcpp = vcp; - return (0); - -errout: - /* - * This will destroy the new vc. - * See: smb_vc_free - */ - smb_vc_rele(vcp); - return (error); -} - void smb_vc_hold(struct smb_vc *vcp) { @@ -883,37 +332,27 @@ smb_vc_gone(struct smb_connobj *cp) * Was smb_vc_disconnect(vcp); */ smb_iod_disconnect(vcp); - - /* Note: smb_iod_destroy in vc_free */ } +/* + * The VC has no more references. Free it. + * No locks needed here. + */ static void smb_vc_free(struct smb_connobj *cp) { struct smb_vc *vcp = CPTOVC(cp); /* - * The VC has no more references, so - * no locks should be needed here. - * Make sure the IOD is gone. + * The _gone call should have emptied the request list, + * but let's make sure, as requests may have references + * to this VC without taking a hold. (The hold is the + * responsibility of threads placing requests.) */ - smb_iod_destroy(vcp); + ASSERT(vcp->iod_rqlist.tqh_first == NULL); if (vcp->vc_tdata) - SMB_TRAN_DONE(vcp, curproc); - - SMB_STRFREE(vcp->vc_username); - SMB_STRFREE(vcp->vc_srvname); - SMB_STRFREE(vcp->vc_pass); - SMB_STRFREE(vcp->vc_domain); - if (vcp->vc_paddr) { - smb_free_sockaddr(vcp->vc_paddr); - vcp->vc_paddr = NULL; - } - if (vcp->vc_laddr) { - smb_free_sockaddr(vcp->vc_laddr); - vcp->vc_laddr = NULL; - } + SMB_TRAN_DONE(vcp); /* * We are not using the iconv routines here. So commenting them for now. @@ -929,17 +368,11 @@ smb_vc_free(struct smb_connobj *cp) if (vcp->vc_toserver) iconv_close(vcp->vc_toserver); #endif - if (vcp->vc_intok) - kmem_free(vcp->vc_intok, vcp->vc_intoklen); - if (vcp->vc_outtok) - kmem_free(vcp->vc_outtok, vcp->vc_outtoklen); - if (vcp->vc_negtok) - kmem_free(vcp->vc_negtok, vcp->vc_negtoklen); if (vcp->vc_mackey != NULL) kmem_free(vcp->vc_mackey, vcp->vc_mackeylen); - cv_destroy(&vcp->iod_exit); + cv_destroy(&vcp->iod_idle); rw_destroy(&vcp->iod_rqlock); sema_destroy(&vcp->vc_sendlock); cv_destroy(&vcp->vc_statechg); @@ -947,158 +380,232 @@ smb_vc_free(struct smb_connobj *cp) kmem_free(vcp, sizeof (*vcp)); } +/*ARGSUSED*/ +int +smb_vc_create(smbioc_ossn_t *ossn, smb_cred_t *scred, smb_vc_t **vcpp) +{ + static char objtype[] = "smb_vc"; + cred_t *cr = scred->scr_cred; + struct smb_vc *vcp; + int error = 0; + + ASSERT(MUTEX_HELD(&smb_vclist.co_lock)); + + vcp = kmem_zalloc(sizeof (struct smb_vc), KM_SLEEP); + + smb_co_init(VCTOCP(vcp), SMBL_VC, objtype); + vcp->vc_co.co_free = smb_vc_free; + vcp->vc_co.co_gone = smb_vc_gone; + + cv_init(&vcp->vc_statechg, objtype, CV_DRIVER, NULL); + sema_init(&vcp->vc_sendlock, 1, objtype, SEMA_DRIVER, NULL); + rw_init(&vcp->iod_rqlock, objtype, RW_DRIVER, NULL); + cv_init(&vcp->iod_idle, objtype, CV_DRIVER, NULL); + + /* Expanded TAILQ_HEAD_INITIALIZER */ + vcp->iod_rqlist.tqh_last = &vcp->iod_rqlist.tqh_first; + + vcp->vc_state = SMBIOD_ST_IDLE; + + /* + * These identify the connection. + */ + vcp->vc_zoneid = getzoneid(); + bcopy(ossn, &vcp->vc_ssn, sizeof (*ossn)); + + /* This fills in vcp->vc_tdata */ + vcp->vc_tdesc = &smb_tran_nbtcp_desc; + if ((error = SMB_TRAN_CREATE(vcp, cr)) != 0) + goto errout; + + /* Success! */ + smb_co_addchild(&smb_vclist, VCTOCP(vcp)); + *vcpp = vcp; + return (0); + +errout: + /* + * This will destroy the new vc. + * See: smb_vc_free + */ + smb_vc_rele(vcp); + return (error); +} /* - * Lookup share in the given VC. Share referenced and locked on return. - * VC expected to be locked on entry and will be left locked on exit. + * Find or create a VC identified by the info in ossn + * and return it with a "hold", but not locked. */ /*ARGSUSED*/ int -smb_vc_lookupshare(struct smb_vc *vcp, struct smb_sharespec *shspec, - struct smb_cred *scred, struct smb_share **sspp) +smb_vc_findcreate(smbioc_ossn_t *ossn, smb_cred_t *scred, smb_vc_t **vcpp) { struct smb_connobj *co; - struct smb_share *ssp = NULL; + struct smb_vc *vcp; + smbioc_ssn_ident_t *vc_id; + int error; + zoneid_t zoneid = getzoneid(); - ASSERT(MUTEX_HELD(&vcp->vc_lock)); + *vcpp = vcp = NULL; - *sspp = NULL; + SMB_CO_LOCK(&smb_vclist); /* var, head, next_field */ - SLIST_FOREACH(co, &(VCTOCP(vcp)->co_children), co_next) { - ssp = CPTOSS(co); + SLIST_FOREACH(co, &smb_vclist.co_children, co_next) { + vcp = CPTOVC(co); + + /* + * Some things we can check without + * holding the lock (those that are + * set at creation and never change). + */ - /* No new refs if _GONE is set. */ - if (ssp->ss_flags & SMBS_GONE) + /* VCs in other zones are invisibile. */ + if (vcp->vc_zoneid != zoneid) continue; - /* This has a hold, so no need to lock it. */ - if (strcmp(ssp->ss_name, shspec->name) == 0) - goto found; - } - return (ENOENT); + /* Also segregate by Unix owner. */ + if (vcp->vc_owner != ossn->ssn_owner) + continue; -found: - /* Return it with a hold. */ - smb_share_hold(ssp); - *sspp = ssp; - return (0); -} + /* + * Compare identifying info: + * server address, user, domain + * names are case-insensitive + */ + vc_id = &vcp->vc_ssn.ssn_id; + if (bcmp(&vc_id->id_srvaddr, + &ossn->ssn_id.id_srvaddr, + sizeof (vc_id->id_srvaddr))) + continue; + if (u8_strcmp(vc_id->id_user, ossn->ssn_id.id_user, 0, + U8_STRCMP_CI_LOWER, U8_UNICODE_LATEST, &error)) + continue; + if (u8_strcmp(vc_id->id_domain, ossn->ssn_id.id_domain, 0, + U8_STRCMP_CI_LOWER, U8_UNICODE_LATEST, &error)) + continue; + /* + * We have a match, but still have to check + * the _GONE flag, and do that with a lock. + * No new references when _GONE is set. + * + * Also clear SMBVOPT_CREATE which the caller + * may check to find out if we did create. + */ + SMB_VC_LOCK(vcp); + if ((vcp->vc_flags & SMBV_GONE) == 0) { + ossn->ssn_vopt &= ~SMBVOPT_CREATE; + /* + * Return it held, unlocked. + * In-line smb_vc_hold here. + */ + co->co_usecount++; + SMB_VC_UNLOCK(vcp); + *vcpp = vcp; + error = 0; + goto out; + } + SMB_VC_UNLOCK(vcp); + /* keep looking. */ + } + vcp = NULL; -static char smb_emptypass[] = ""; + /* Note: smb_vclist is still locked. */ -const char * -smb_vc_getpass(struct smb_vc *vcp) -{ - if (vcp->vc_pass) - return (vcp->vc_pass); - return (smb_emptypass); + if (ossn->ssn_vopt & SMBVOPT_CREATE) { + /* + * Create a new VC. It starts out with + * hold count = 1, so don't incr. here. + */ + error = smb_vc_create(ossn, scred, &vcp); + if (error == 0) + *vcpp = vcp; + } else + error = ENOENT; + +out: + SMB_CO_UNLOCK(&smb_vclist); + return (error); } -uint16_t -smb_vc_nextmid(struct smb_vc *vcp) -{ - uint16_t r; - r = atomic_inc_16_nv(&vcp->vc_mid); - return (r); -} +/* + * Helper functions that operate on VCs + */ /* * Get a pointer to the IP address suitable for passing to Trusted * Extensions find_tpc() routine. Used by smbfs_mount_label_policy(). * Compare this code to nfs_mount_label_policy() if problems arise. - * Without support for direct CIFS-over-TCP, we should always see - * an AF_NETBIOS sockaddr here. */ void * smb_vc_getipaddr(struct smb_vc *vcp, int *ipvers) { - switch (vcp->vc_paddr->sa_family) { - case AF_NETBIOS: { - struct sockaddr_nb *snb; - - *ipvers = IPV4_VERSION; - /*LINTED*/ - snb = (struct sockaddr_nb *)vcp->vc_paddr; - return ((void *)&snb->snb_ipaddr); - } - case AF_INET: { - struct sockaddr_in *sin; + smbioc_ssn_ident_t *id = &vcp->vc_ssn.ssn_id; + void *ret; + switch (id->id_srvaddr.sa.sa_family) { + case AF_INET: *ipvers = IPV4_VERSION; - /*LINTED*/ - sin = (struct sockaddr_in *)vcp->vc_paddr; - return ((void *)&sin->sin_addr); - } - case AF_INET6: { - struct sockaddr_in6 *sin6; + ret = &id->id_srvaddr.sin.sin_addr; + break; + case AF_INET6: *ipvers = IPV6_VERSION; - /*LINTED*/ - sin6 = (struct sockaddr_in6 *)vcp->vc_paddr; - return ((void *)&sin6->sin6_addr); - } + ret = &id->id_srvaddr.sin6.sin6_addr; + break; default: SMBSDEBUG("invalid address family %d\n", - vcp->vc_paddr->sa_family); + id->id_srvaddr.sa.sa_family); *ipvers = 0; - return (NULL); + ret = NULL; + break; } + return (ret); } -/* - * Share implementation - */ -/* - * Allocate share structure and attach it to the given VC - * Connection expected to be locked on entry. Share will be returned - * in locked state. - */ -/*ARGSUSED*/ -int -smb_share_create(struct smb_vc *vcp, struct smb_sharespec *shspec, - struct smb_cred *scred, struct smb_share **sspp) +void +smb_vc_walkshares(struct smb_vc *vcp, + walk_share_func_t func) { - static char objtype[] = "smb_ss"; - struct smb_share *ssp; + smb_connobj_t *co; + smb_share_t *ssp; - ASSERT(MUTEX_HELD(&vcp->vc_lock)); + /* + * Walk the share list calling func(ssp, arg) + */ + SMB_VC_LOCK(vcp); + SLIST_FOREACH(co, &(VCTOCP(vcp)->co_children), co_next) { + ssp = CPTOSS(co); + SMB_SS_LOCK(ssp); + func(ssp); + SMB_SS_UNLOCK(ssp); + } + SMB_VC_UNLOCK(vcp); +} - ssp = kmem_zalloc(sizeof (struct smb_share), KM_SLEEP); - smb_co_init(SSTOCP(ssp), SMBL_SHARE, objtype); - ssp->ss_co.co_free = smb_share_free; - ssp->ss_co.co_gone = smb_share_gone; - ssp->ss_name = smb_strdup(shspec->name); - ssp->ss_mount = NULL; - if (shspec->pass && shspec->pass[0]) - ssp->ss_pass = smb_strdup(shspec->pass); - ssp->ss_type = shspec->stype; - ssp->ss_tid = SMB_TID_UNKNOWN; - ssp->ss_mode = shspec->rights & SMBM_MASK; - ssp->ss_fsname = NULL; - smb_co_addchild(VCTOCP(vcp), SSTOCP(ssp)); - *sspp = ssp; +/* + * Share implementation + */ - return (0); +void +smb_share_hold(struct smb_share *ssp) +{ + smb_co_hold(SSTOCP(ssp)); } -/* - * Normally called via smb_share_rele() - * after co_usecount drops to zero. - */ -static void -smb_share_free(struct smb_connobj *cp) +void +smb_share_rele(struct smb_share *ssp) { - struct smb_share *ssp = CPTOSS(cp); + smb_co_rele(SSTOCP(ssp)); +} - SMB_STRFREE(ssp->ss_name); - SMB_STRFREE(ssp->ss_pass); - SMB_STRFREE(ssp->ss_fsname); - smb_co_done(SSTOCP(ssp)); - kmem_free(ssp, sizeof (*ssp)); +void +smb_share_kill(struct smb_share *ssp) +{ + smb_co_kill(SSTOCP(ssp)); } /* @@ -1112,80 +619,172 @@ smb_share_gone(struct smb_connobj *cp) struct smb_cred scred; struct smb_share *ssp = CPTOSS(cp); - smb_credinit(&scred, curproc, NULL); + smb_credinit(&scred, NULL); smb_iod_shutdown_share(ssp); smb_smb_treedisconnect(ssp, &scred); smb_credrele(&scred); } -void -smb_share_hold(struct smb_share *ssp) +/* + * Normally called via smb_share_rele() + * after co_usecount drops to zero. + */ +static void +smb_share_free(struct smb_connobj *cp) { - smb_co_hold(SSTOCP(ssp)); -} + struct smb_share *ssp = CPTOSS(cp); -void -smb_share_rele(struct smb_share *ssp) -{ - smb_co_rele(SSTOCP(ssp)); + cv_destroy(&ssp->ss_conn_done); + smb_co_done(SSTOCP(ssp)); + kmem_free(ssp, sizeof (*ssp)); } -void -smb_share_kill(struct smb_share *ssp) +/* + * Allocate share structure and attach it to the given VC + * Connection expected to be locked on entry. Share will be returned + * in locked state. + */ +/*ARGSUSED*/ +int +smb_share_create(smbioc_tcon_t *tcon, struct smb_vc *vcp, + struct smb_share **sspp, struct smb_cred *scred) { - smb_co_kill(SSTOCP(ssp)); -} + static char objtype[] = "smb_ss"; + struct smb_share *ssp; + ASSERT(MUTEX_HELD(&vcp->vc_lock)); -void -smb_share_invalidate(struct smb_share *ssp) -{ + ssp = kmem_zalloc(sizeof (struct smb_share), KM_SLEEP); + smb_co_init(SSTOCP(ssp), SMBL_SHARE, objtype); + ssp->ss_co.co_free = smb_share_free; + ssp->ss_co.co_gone = smb_share_gone; + + cv_init(&ssp->ss_conn_done, objtype, CV_DRIVER, NULL); ssp->ss_tid = SMB_TID_UNKNOWN; + + bcopy(&tcon->tc_sh, &ssp->ss_ioc, + sizeof (smbioc_oshare_t)); + + smb_co_addchild(VCTOCP(vcp), SSTOCP(ssp)); + *sspp = ssp; + + return (0); } /* - * Returns NON-zero if the share is valid. - * Called with the share locked. + * Find or create a share under the given VC + * and return it with a "hold", but not locked. */ + int -smb_share_valid(struct smb_share *ssp) +smb_share_findcreate(smbioc_tcon_t *tcon, struct smb_vc *vcp, + struct smb_share **sspp, struct smb_cred *scred) { - struct smb_vc *vcp = SSTOVC(ssp); + struct smb_connobj *co; + struct smb_share *ssp = NULL; + int error = 0; - ASSERT(MUTEX_HELD(&ssp->ss_lock)); + *sspp = NULL; - if ((ssp->ss_flags & SMBS_CONNECTED) == 0) - return (0); + SMB_VC_LOCK(vcp); - if (ssp->ss_tid == SMB_TID_UNKNOWN) { - SMBIODEBUG("found TID unknown\n"); - ssp->ss_flags &= ~SMBS_CONNECTED; - } + /* var, head, next_field */ + SLIST_FOREACH(co, &(VCTOCP(vcp)->co_children), co_next) { + ssp = CPTOSS(co); + + /* Share name */ + if (u8_strcmp(ssp->ss_name, tcon->tc_sh.sh_name, 0, + U8_STRCMP_CI_LOWER, U8_UNICODE_LATEST, &error)) + continue; - if (ssp->ss_vcgenid != vcp->vc_genid) { - SMBIODEBUG("wrong genid\n"); - ssp->ss_flags &= ~SMBS_CONNECTED; + /* + * We have a match, but still have to check + * the _GONE flag, and do that with a lock. + * No new references when _GONE is set. + * + * Also clear SMBSOPT_CREATE which the caller + * may check to find out if we did create. + */ + SMB_SS_LOCK(ssp); + if ((ssp->ss_flags & SMBS_GONE) == 0) { + tcon->tc_opt &= ~SMBSOPT_CREATE; + /* + * Return it held, unlocked. + * In-line smb_share_hold here. + */ + co->co_usecount++; + SMB_SS_UNLOCK(ssp); + *sspp = ssp; + error = 0; + goto out; + } + SMB_SS_UNLOCK(ssp); + /* keep looking. */ } + ssp = NULL; + + /* Note: vcp (list of shares) is still locked. */ + + if (tcon->tc_opt & SMBSOPT_CREATE) { + /* + * Create a new share. It starts out with + * hold count = 1, so don't incr. here. + */ + error = smb_share_create(tcon, vcp, &ssp, scred); + if (error == 0) + *sspp = ssp; + } else + error = ENOENT; - return (ssp->ss_flags & SMBS_CONNECTED); +out: + SMB_VC_UNLOCK(vcp); + return (error); +} + + +/* + * Helper functions that operate on shares + */ + +/* + * Mark this share as invalid, so consumers will know + * their file handles have become invalid. + * + * Most share consumers store a copy of ss_vcgenid when + * opening a file handle and compare that with what's in + * the share before using a file handle. If the genid + * doesn't match, the file handle has become "stale" + * due to disconnect. Therefore, zap ss_vcgenid here. + */ +void +smb_share_invalidate(struct smb_share *ssp) +{ + + ASSERT(MUTEX_HELD(&ssp->ss_lock)); + + ssp->ss_flags &= ~SMBS_CONNECTED; + ssp->ss_tid = SMB_TID_UNKNOWN; + ssp->ss_vcgenid = 0; } /* * Connect (or reconnect) a share object. - * Called with the share locked. + * + * Called by smb_usr_get_tree() for new connections, + * and called by smb_rq_enqueue() for reconnect. */ int -smb_share_tcon(struct smb_share *ssp) +smb_share_tcon(smb_share_t *ssp, smb_cred_t *scred) { - struct smb_vc *vcp = SSTOVC(ssp); clock_t tmo; int error; - ASSERT(MUTEX_HELD(&ssp->ss_lock)); + SMB_SS_LOCK(ssp); if (ssp->ss_flags & SMBS_CONNECTED) { SMBIODEBUG("alread connected?"); - return (0); + error = 0; + goto out; } /* @@ -1198,76 +797,41 @@ smb_share_tcon(struct smb_share *ssp) ssp->ss_conn_waiters--; if (tmo == 0) { /* Interrupt! */ - return (EINTR); + error = EINTR; + goto out; } } /* Did someone else do it for us? */ - if (ssp->ss_flags & SMBS_CONNECTED) - return (0); + if (ssp->ss_flags & SMBS_CONNECTED) { + error = 0; + goto out; + } /* * OK, we'll do the work. */ ssp->ss_flags |= SMBS_RECONNECTING; - /* Drop the lock while doing the call. */ + /* + * Drop the lock while doing the TCON. + * On success, sets ss_tid, ss_vcgenid, + * and ss_flags |= SMBS_CONNECTED; + */ SMB_SS_UNLOCK(ssp); - error = smb_smb_treeconnect(ssp, &vcp->vc_scred); + error = smb_smb_treeconnect(ssp, scred); SMB_SS_LOCK(ssp); - if (!error) - ssp->ss_flags |= SMBS_CONNECTED; ssp->ss_flags &= ~SMBS_RECONNECTING; /* They can all go ahead! */ if (ssp->ss_conn_waiters) cv_broadcast(&ssp->ss_conn_done); - return (error); -} - -const char * -smb_share_getpass(struct smb_share *ssp) -{ - struct smb_vc *vcp; - - if (ssp->ss_pass) - return (ssp->ss_pass); - vcp = SSTOVC(ssp); - if (vcp->vc_pass) - return (vcp->vc_pass); - return (smb_emptypass); -} - -int -smb_share_count(void) -{ - struct smb_connobj *covc, *coss; - struct smb_vc *vcp; - zoneid_t zoneid = getzoneid(); - int nshares = 0; - - SMB_CO_LOCK(&smb_vclist); - SLIST_FOREACH(covc, &smb_vclist.co_children, co_next) { - vcp = CPTOVC(covc); - - /* VCs in other zones are invisibile. */ - if (vcp->vc_zoneid != zoneid) - continue; - - SMB_VC_LOCK(vcp); - - /* var, head, next_field */ - SLIST_FOREACH(coss, &(VCTOCP(vcp)->co_children), co_next) { - nshares++; - } - - SMB_VC_UNLOCK(vcp); - } - SMB_CO_UNLOCK(&smb_vclist); +out: + SMB_SS_UNLOCK(ssp); - return (nshares); + return (error); } /* diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/smb_conn.h b/usr/src/uts/common/fs/smbclnt/netsmb/smb_conn.h index 4662acd111..df85a77d11 100644 --- a/usr/src/uts/common/fs/smbclnt/netsmb/smb_conn.h +++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_conn.h @@ -33,28 +33,24 @@ */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _SMB_CONN_H #define _SMB_CONN_H +#include <sys/dditypes.h> #include <sys/t_lock.h> #include <sys/queue.h> /* for SLIST below */ #include <sys/uio.h> #include <netsmb/smb_dev.h> -#ifndef _KERNEL -#error "Not _KERNEL?" -#endif - /* * Credentials of user/process for processing in the connection procedures */ typedef struct smb_cred { - pid_t vc_pid; - cred_t *vc_ucred; + struct cred *scr_cred; } smb_cred_t; /* @@ -67,25 +63,17 @@ typedef struct smb_cred { * Many of these were duplicates of SMBVOPT_ flags * and we now keep those too instead of merging * them into vc_flags. - * - * Careful here: In smb_smb_negotiate, we clear ALL OF - * vc_flags except: SMBV_GONE, SMBV_RECONNECTING */ -#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 */ /* * Note: the common "obj" level uses this GONE flag by * the name SMBO_GONE. Keep this alias as a reminder. */ -#define SMBV_GONE SMBO_GONE +#define SMBV_GONE SMBO_GONE /* * bits in smb_share ss_flags (a.k.a. ss_co.co_flags) @@ -98,78 +86,13 @@ typedef struct smb_cred { * ^ This partition can't handle dates before 1980. It's probably a FAT * partition but could be some other ancient FS type */ -#define SMBS_RESUMEKEYS 0x0010 /* must use resume keys */ +#define SMBS_RESUMEKEYS 0x0020 /* must use resume keys */ +#define SMBS_LONGNAMES 0x0040 /* share can use long names */ /* * Note: the common "obj" level uses this GONE flag by * the name SMBO_GONE. Keep this alias as a reminder. */ -#define SMBS_GONE SMBO_GONE - -/* - * Negotiated protocol parameters - */ -struct smb_sopt { - int sv_proto; - int16_t sv_tz; /* offset in min relative to UTC */ - uint32_t sv_maxtx; /* maximum transmit buf size */ - uchar_t sv_sm; /* security mode */ - uint16_t sv_maxmux; /* max number of outstanding rq's */ - uint16_t sv_maxvcs; /* max number of VCs */ - uint16_t sv_rawmode; - uint32_t sv_maxraw; /* maximum raw-buffer size */ - uint32_t sv_skey; /* session key */ - uint32_t sv_caps; /* capabilites SMB_CAP_ */ -}; -typedef struct smb_sopt smb_sopt_t; - -/* - * network IO daemon states - * really connection states. - */ -enum smbiod_state { - SMBIOD_ST_NOTCONN, /* no connect request was made */ - SMBIOD_ST_RECONNECT, /* a [re]connect attempt is in progress */ - SMBIOD_ST_TRANACTIVE, /* transport level is up */ - SMBIOD_ST_NEGOACTIVE, /* completed negotiation */ - SMBIOD_ST_SSNSETUP, /* started (a) session setup */ - SMBIOD_ST_VCACTIVE, /* session established */ - SMBIOD_ST_DEAD /* connection broken, transport is down */ -}; - - -/* - * Info structures - */ -#define SMB_INFO_NONE 0 -#define SMB_INFO_VC 2 -#define SMB_INFO_SHARE 3 - -struct smb_vc_info { - int itype; - int usecount; - uid_t uid; /* user id of connection */ - gid_t gid; /* group of connection */ - mode_t mode; /* access mode */ - int flags; - enum smbiod_state iodstate; - struct smb_sopt sopt; - char srvname[SMB_MAXSRVNAMELEN+1]; - char vcname[128]; -}; -typedef struct smb_vc_info smb_vc_info_t; - -struct smb_share_info { - int itype; - int usecount; - ushort_t tid; /* TID */ - int type; /* share type */ - uid_t uid; /* user id of connection */ - gid_t gid; /* group of connection */ - mode_t mode; /* access mode */ - int flags; - char sname[128]; -}; -typedef struct smb_share_info smb_share_info_t; +#define SMBS_GONE SMBO_GONE struct smb_rq; /* This declares struct smb_rqhead */ @@ -221,88 +144,76 @@ struct smb_connobj { typedef struct smb_connobj smb_connobj_t; /* - * Virtual Circuit (session) to a server. - * This is the most (over)complicated part of SMB protocol. - * For the user security level (usl), each session with different remote - * user name has its own VC. - * It is unclear however, should share security level (ssl) allow additional - * VCs, because user name is not used and can be the same. On other hand, - * multiple VCs allows us to create separate sessions to server on a per - * user basis. + * "Level" in the connection object hierarchy */ +#define SMBL_SM 0 +#define SMBL_VC 1 +#define SMBL_SHARE 2 +/* + * Virtual Circuit to a server (really connection + session). + * Yes, calling this a "Virtual Circuit" is confusining, + * because it has nothing to do with the SMB notion of a + * "Virtual Circuit". + */ typedef struct smb_vc { - struct smb_connobj vc_co; - enum smbiod_state vc_state; - kcondvar_t vc_statechg; - ksema_t vc_sendlock; - - zoneid_t vc_zoneid; - char *vc_srvname; - struct sockaddr *vc_paddr; /* server addr */ - struct sockaddr *vc_laddr; /* local addr, if any */ - char *vc_domain; /* domain that defines username */ - char *vc_username; - char *vc_pass; /* password for usl case */ - uchar_t vc_lmhash[SMB_PWH_MAX]; - uchar_t vc_nthash[SMB_PWH_MAX]; - uint8_t *vc_mackey; /* MAC key */ - int vc_mackeylen; /* length of MAC key */ - uint32_t vc_seqno; /* my next sequence number - */ - /* - serialized by iod_rqlock */ - uint_t vc_timo; /* default request timeout */ - int vc_maxvcs; /* maximum number of VC per conn */ - - void *vc_tolower; /* local charset */ - void *vc_toupper; /* local charset */ - void *vc_toserver; /* local charset to server one */ - void *vc_tolocal; /* server charset to local one */ - int vc_number; /* number of this VC from client side */ - int vc_genid; /* "generation ID" of this VC */ - uid_t vc_uid; /* user id of connection */ - gid_t vc_grp; /* group of connection */ - mode_t vc_mode; /* access mode */ - uint16_t vc_smbuid; /* auth. session ID from server */ - - uint8_t vc_hflags; /* or'ed with flags in the smb header */ - uint16_t vc_hflags2; /* or'ed with flags in the smb header */ - void *vc_tdata; /* transport control block */ - struct smb_tran_desc *vc_tdesc; - int vc_chlen; /* actual challenge length */ - uchar_t vc_challenge[SMB_MAXCHALLENGELEN]; - uint16_t vc_mid; /* multiplex id */ - int vc_vopt; /* local options SMBVOPT_ */ - struct smb_sopt vc_sopt; /* server options */ - struct smb_cred vc_scred; /* used in reconnect procedure */ - int vc_txmax; /* max tx/rx packet size */ - int vc_rxmax; /* max readx data size */ - int vc_wxmax; /* max writex data size */ - - /* Authentication tokens */ - size_t vc_intoklen; - caddr_t vc_intok; - size_t vc_outtoklen; - caddr_t vc_outtok; - size_t vc_negtoklen; - caddr_t vc_negtok; + struct smb_connobj vc_co; /* keep first! See CPTOVC */ + enum smbiod_state vc_state; + kcondvar_t vc_statechg; - /* - * These members used to be in struct smbiod, - * which has been eliminated. - */ - krwlock_t iod_rqlock; /* iod_rqlist */ + zoneid_t vc_zoneid; + uid_t vc_owner; /* Unix owner */ + int vc_genid; /* "generation" ID */ + + int vc_mackeylen; /* length of MAC key */ + uint8_t *vc_mackey; /* MAC key */ + + ksema_t vc_sendlock; + struct smb_tran_desc *vc_tdesc; /* transport ops. vector */ + void *vc_tdata; /* transport control block */ + + kcondvar_t iod_idle; /* IOD thread idle CV */ + krwlock_t iod_rqlock; /* iod_rqlist */ struct smb_rqhead iod_rqlist; /* list of outstanding reqs */ struct _kthread *iod_thr; /* the IOD (reader) thread */ - kcondvar_t iod_exit; /* IOD thread termination */ int iod_flags; /* see SMBIOD_* below */ int iod_newrq; /* send needed (iod_rqlock) */ int iod_muxfull; /* maxmux limit reached */ - uint_t iod_rqwaiting; /* count of waiting requests */ + + /* This is copied in/out when IOD enters/returns */ + smbioc_ssn_work_t vc_work; + + /* session identity, etc. */ + smbioc_ossn_t vc_ssn; } smb_vc_t; #define vc_lock vc_co.co_lock #define vc_flags vc_co.co_flags -#define vc_maxmux vc_sopt.sv_maxmux + +/* defines for members in vc_ssn */ +#define vc_owner vc_ssn.ssn_owner +#define vc_srvname vc_ssn.ssn_srvname +#define vc_srvaddr vc_ssn.ssn_id.id_srvaddr +#define vc_domain vc_ssn.ssn_id.id_domain +#define vc_username vc_ssn.ssn_id.id_user +#define vc_vopt vc_ssn.ssn_vopt + +/* defines for members in vc_work */ +#define vc_sopt vc_work.wk_sopt +#define vc_maxmux vc_work.wk_sopt.sv_maxmux +#define vc_tran_fd vc_work.wk_iods.is_tran_fd +#define vc_hflags vc_work.wk_iods.is_hflags +#define vc_hflags2 vc_work.wk_iods.is_hflags2 +#define vc_smbuid vc_work.wk_iods.is_smbuid +#define vc_next_mid vc_work.wk_iods.is_next_mid +#define vc_txmax vc_work.wk_iods.is_txmax +#define vc_rwmax vc_work.wk_iods.is_rwmax +#define vc_rxmax vc_work.wk_iods.is_rxmax +#define vc_wxmax vc_work.wk_iods.is_wxmax +#define vc_ssn_key vc_work.wk_iods.is_ssn_key +#define vc_next_seq vc_work.wk_iods.is_next_seq +#define vc_u_mackey vc_work.wk_iods.is_u_mackey +#define vc_u_maclen vc_work.wk_iods.is_u_maclen #define SMB_VC_LOCK(vcp) mutex_enter(&(vcp)->vc_lock) #define SMB_VC_UNLOCK(vcp) mutex_exit(&(vcp)->vc_lock) @@ -319,104 +230,121 @@ typedef struct smb_vc { */ typedef struct smb_share { - struct smb_connobj ss_co; + struct smb_connobj ss_co; /* keep first! See CPTOSS */ kcondvar_t ss_conn_done; /* wait for reconnect */ int ss_conn_waiters; - char *ss_name; - char *ss_pass; /* share password, can be null */ - char *ss_fsname; - void *ss_mount; /* used for smb up/down */ - uint16_t ss_tid; /* TID */ - int ss_type; /* share type */ - mode_t ss_mode; /* access mode */ int ss_vcgenid; /* check VC generation ID */ - uint32_t ss_maxfilenamelen; - int ss_sopt; /* local options SMBSOPT_ */ + uint16_t ss_tid; /* TID */ + uint16_t ss_options; /* option support bits */ + smbioc_oshare_t ss_ioc; } smb_share_t; #define ss_lock ss_co.co_lock #define ss_flags ss_co.co_flags +#define ss_name ss_ioc.sh_name +#define ss_pwlen ss_ioc.sh_pwlen +#define ss_pass ss_ioc.sh_pass +#define ss_type_req ss_ioc.sh_type_req +#define ss_type_ret ss_ioc.sh_type_ret + #define SMB_SS_LOCK(ssp) mutex_enter(&(ssp)->ss_lock) #define SMB_SS_UNLOCK(ssp) mutex_exit(&(ssp)->ss_lock) -#define CPTOVC(cp) ((struct smb_vc *)(cp)) +#define CPTOVC(cp) ((struct smb_vc *)((void *)(cp))) #define VCTOCP(vcp) (&(vcp)->vc_co) -#define CPTOSS(cp) ((struct smb_share *)(cp)) +#define CPTOSS(cp) ((struct smb_share *)((void *)(cp))) #define SSTOVC(ssp) CPTOVC(((ssp)->ss_co.co_parent)) #define SSTOCP(ssp) (&(ssp)->ss_co) /* - * This is used internally to pass all the info about - * some VC that an ioctl caller is looking for. - */ -struct smb_vcspec { - char *srvname; - struct sockaddr *sap; - struct sockaddr *lap; - int optflags; - char *domain; - char *username; - char *pass; - uid_t owner; - gid_t group; - mode_t mode; - mode_t rights; - char *localcs; - char *servercs; - size_t toklen; - caddr_t tok; -}; -typedef struct smb_vcspec smb_vcspec_t; - -/* - * This is used internally to pass all the info about - * some share that an ioctl caller is looking for. - */ -struct smb_sharespec { - char *name; - char *pass; - mode_t mode; - mode_t rights; - uid_t owner; - gid_t group; - int stype; - int optflags; -}; -typedef struct smb_sharespec smb_sharespec_t; - - -/* * Call-back operations vector, so the netsmb module * can notify smbfs about events affecting mounts. * Installed in netsmb after smbfs loads. */ -/* #define NEED_SMBFS_CALLBACKS 1 */ -#ifdef NEED_SMBFS_CALLBACKS typedef struct smb_fscb { - void (*fscb_dead)(smb_share_t *); + /* Called when the VC has disconnected. */ + void (*fscb_disconn)(smb_share_t *); + /* Called when the VC has reconnected. */ + void (*fscb_connect)(smb_share_t *); + /* Called when the server becomes unresponsive. */ void (*fscb_down)(smb_share_t *); + /* Called when the server is responding again. */ void (*fscb_up)(smb_share_t *); } smb_fscb_t; /* Install the above vector, or pass NULL to clear it. */ int smb_fscb_set(smb_fscb_t *); -#endif /* NEED_SMBFS_CALLBACKS */ + +/* + * The driver per open instance object. + * Mostly used in: smb_dev.c, smb_usr.c + */ +typedef struct smb_dev { + int sd_opened; /* Opened or not */ + int sd_level; /* Future use */ + struct smb_vc *sd_vc; /* Reference to VC */ + struct smb_share *sd_share; /* Reference to share if any */ + int sd_vcgenid; /* Generation of share or VC */ + int sd_poll; /* Future use */ + int sd_seq; /* Kind of minor number/instance no */ + int sd_flags; /* State of connection */ +#define NSMBFL_OPEN 0x0001 +#define NSMBFL_IOD 0x0002 + zoneid_t zoneid; /* Zone id */ + dev_info_t *smb_dip; /* ptr to dev_info node */ + void *sd_devfs; /* Dont know how to use this. but */ + struct cred *smb_cred; /* per dev credentails. Future use */ +} smb_dev_t; + +extern const uint32_t nsmb_version; + +/* + * smb_dev.c + */ +int smb_dev2share(int fd, struct smb_share **sspp); + + +/* + * smb_usr.c + */ +int smb_usr_get_flags2(smb_dev_t *sdp, intptr_t arg, int flags); +int smb_usr_get_ssnkey(smb_dev_t *sdp, intptr_t arg, int flags); + +int smb_usr_simplerq(smb_dev_t *sdp, intptr_t arg, int flags, cred_t *cr); +int smb_usr_t2request(smb_dev_t *sdp, intptr_t arg, int flags, cred_t *cr); +int smb_usr_rw(smb_dev_t *sdp, int cmd, intptr_t arg, int flags, cred_t *cr); + +int smb_usr_get_ssn(smb_dev_t *, int, intptr_t, int, cred_t *); +int smb_usr_drop_ssn(smb_dev_t *sdp, int cmd); + +int smb_usr_get_tree(smb_dev_t *, int, intptr_t, int, cred_t *); +int smb_usr_drop_tree(smb_dev_t *sdp, int cmd); + +int smb_usr_iod_work(smb_dev_t *sdp, intptr_t arg, int flags, cred_t *cr); +int smb_usr_iod_ioctl(smb_dev_t *sdp, int cmd, intptr_t arg, int flags); + /* * IOD functions */ -int smb_iod_create(struct smb_vc *vcp); -int smb_iod_destroy(struct smb_vc *vcp); -int smb_iod_connect(struct smb_vc *vcp); -int smb_iod_disconnect(struct smb_vc *vcp); +int smb_iod_create(smb_vc_t *vcp); +int smb_iod_destroy(smb_vc_t *vcp); +int smb_iod_connect(smb_vc_t *vcp); +int smb_iod_disconnect(smb_vc_t *vcp); int smb_iod_addrq(struct smb_rq *rqp); int smb_iod_multirq(struct smb_rq *rqp); int smb_iod_waitrq(struct smb_rq *rqp); int smb_iod_removerq(struct smb_rq *rqp); -void smb_iod_shutdown_share(struct smb_share *ssp); -void smb_iod_notify_down(struct smb_vc *vcp); -void smb_iod_notify_up(struct smb_vc *vcp); +void smb_iod_shutdown_share(smb_share_t *ssp); + +void smb_iod_sendall(smb_vc_t *); +int smb_iod_recvall(smb_vc_t *); + +int smb_iod_vc_work(smb_vc_t *, cred_t *); +int smb_iod_vc_idle(smb_vc_t *); +int smb_iod_vc_rcfail(smb_vc_t *); +int smb_iod_reconnect(smb_vc_t *); /* * Session level functions @@ -425,63 +353,45 @@ int smb_sm_init(void); int smb_sm_idle(void); void smb_sm_done(void); -int smb_sm_findvc(struct smb_vcspec *vcspec, - struct smb_cred *scred, struct smb_vc **vcpp); -int smb_sm_negotiate(struct smb_vcspec *vcspec, - struct smb_cred *scred, struct smb_vc **vcpp); -int smb_sm_ssnsetup(struct smb_vcspec *vcspec, - struct smb_cred *scred, struct smb_vc *vcp); -int smb_sm_tcon(struct smb_sharespec *shspec, struct smb_cred *scred, - struct smb_vc *vcp, struct smb_share **sspp); - /* * VC level functions */ -int smb_vc_setup(struct smb_vcspec *vcspec, struct smb_cred *scred, - struct smb_vc *vcp, int is_ss); -int smb_vc_create(struct smb_vcspec *vcspec, - struct smb_cred *scred, struct smb_vc **vcpp); -int smb_vc_negotiate(struct smb_vc *vcp, struct smb_cred *scred); -int smb_vc_ssnsetup(struct smb_vc *vcp, struct smb_cred *scred); -void smb_vc_hold(struct smb_vc *vcp); -void smb_vc_rele(struct smb_vc *vcp); -void smb_vc_kill(struct smb_vc *vcp); -int smb_vc_lookupshare(struct smb_vc *vcp, struct smb_sharespec *shspec, - struct smb_cred *scred, struct smb_share **sspp); -const char *smb_vc_getpass(struct smb_vc *vcp); -uint16_t smb_vc_nextmid(struct smb_vc *vcp); -void *smb_vc_getipaddr(struct smb_vc *vcp, int *ipvers); +void smb_vc_hold(smb_vc_t *vcp); +void smb_vc_rele(smb_vc_t *vcp); +void smb_vc_kill(smb_vc_t *vcp); + +int smb_vc_findcreate(smbioc_ossn_t *, smb_cred_t *, smb_vc_t **); +int smb_vc_create(smbioc_ossn_t *ossn, smb_cred_t *scred, smb_vc_t **vcpp); + +const char *smb_vc_getpass(smb_vc_t *vcp); +uint16_t smb_vc_nextmid(smb_vc_t *vcp); +void *smb_vc_getipaddr(smb_vc_t *vcp, int *ipvers); + +typedef void (*walk_share_func_t)(smb_share_t *); +void smb_vc_walkshares(struct smb_vc *, walk_share_func_t); /* * share level functions */ -int smb_share_create(struct smb_vc *vcp, struct smb_sharespec *shspec, - struct smb_cred *scred, struct smb_share **sspp); -void smb_share_hold(struct smb_share *ssp); -void smb_share_rele(struct smb_share *ssp); -void smb_share_kill(struct smb_share *ssp); +int smb_share_findcreate(smbioc_tcon_t *, smb_vc_t *, + smb_share_t **, smb_cred_t *); + +void smb_share_hold(smb_share_t *ssp); +void smb_share_rele(smb_share_t *ssp); +void smb_share_kill(smb_share_t *ssp); -void smb_share_invalidate(struct smb_share *ssp); -int smb_share_tcon(struct smb_share *ssp); -int smb_share_valid(struct smb_share *ssp); -const char *smb_share_getpass(struct smb_share *ssp); -int smb_share_count(void); +void smb_share_invalidate(smb_share_t *ssp); +int smb_share_tcon(smb_share_t *, smb_cred_t *); /* * SMB protocol level functions */ -int smb_smb_negotiate(struct smb_vc *vcp, struct smb_cred *scred); -int smb_smb_ssnsetup(struct smb_vc *vcp, struct smb_cred *scred); -int smb_smb_ssnclose(struct smb_vc *vcp, struct smb_cred *scred); -int smb_smb_treeconnect(struct smb_share *ssp, struct smb_cred *scred); -int smb_smb_treedisconnect(struct smb_share *ssp, struct smb_cred *scred); -int smb_smb_echo(struct smb_vc *vcp, struct smb_cred *scred, int timo); -#ifdef APPLE -int smb_smb_checkdir(struct smb_share *ssp, void *dnp, - char *name, int nmlen, struct smb_cred *scred); -#endif -int smb_rwuio(struct smb_share *ssp, uint16_t fid, uio_rw_t rw, - uio_t *uiop, struct smb_cred *scred, int timo); +int smb_smb_echo(smb_vc_t *vcp, smb_cred_t *scred, int timo); +int smb_smb_treeconnect(smb_share_t *ssp, smb_cred_t *scred); +int smb_smb_treedisconnect(smb_share_t *ssp, smb_cred_t *scred); + +int smb_rwuio(smb_share_t *ssp, uint16_t fid, uio_rw_t rw, + uio_t *uiop, smb_cred_t *scred, int timo); #endif /* _SMB_CONN_H */ diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/smb_crypt.c b/usr/src/uts/common/fs/smbclnt/netsmb/smb_crypt.c deleted file mode 100644 index 10d77644de..0000000000 --- a/usr/src/uts/common/fs/smbclnt/netsmb/smb_crypt.c +++ /dev/null @@ -1,662 +0,0 @@ -/* - * 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 2008 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. - */ - -#include <sys/param.h> -#include <sys/systm.h> -#include <sys/conf.h> -#include <sys/proc.h> -#include <sys/fcntl.h> -#include <sys/socket.h> -#include <sys/md4.h> -#include <sys/md5.h> -#include <sys/des.h> -#include <sys/kmem.h> -#include <sys/crypto/api.h> -#include <sys/crypto/common.h> -#include <sys/cmn_err.h> -#include <sys/stream.h> -#include <sys/strsun.h> -#include <sys/sdt.h> - -#include <netsmb/smb_osdep.h> -#include <netsmb/smb.h> -#include <netsmb/smb_conn.h> -#include <netsmb/smb_subr.h> -#include <netsmb/smb_dev.h> -#include <netsmb/smb_rq.h> - -#ifdef DEBUG -/* - * Set this to a small number to debug sequence numbers - * that seem to get out of step. - */ -int nsmb_signing_fudge = 0; -#endif - -/* Mechanism definitions */ -static crypto_mechanism_t crypto_mech_md5 = { CRYPTO_MECH_INVALID }; -static crypto_mechanism_t crypto_mech_des = { CRYPTO_MECH_INVALID }; - -void -smb_crypto_mech_init(void) -{ - crypto_mech_des.cm_type = crypto_mech2id(SUN_CKM_DES_ECB); - crypto_mech_md5.cm_type = crypto_mech2id(SUN_CKM_MD5); -} - -static void -smb_E(const uchar_t *key, const uchar_t *data, uchar_t *dest) -{ - int rv; - uchar_t kk[8]; - crypto_data_t d1, d2; - crypto_key_t keyt; - - - bzero(&d1, sizeof (crypto_data_t)); - bzero(&d2, sizeof (crypto_data_t)); - /* - * 'Key' here is the username - 7-bytes. Convert that to - * to a 8-byte string. - */ - kk[0] = key[0] & 0xfe; - kk[1] = key[0] << 7 | (key[1] >> 1 & 0xfe); - kk[2] = key[1] << 6 | (key[2] >> 2 & 0xfe); - kk[3] = key[2] << 5 | (key[3] >> 3 & 0xfe); - kk[4] = key[3] << 4 | (key[4] >> 4 & 0xfe); - kk[5] = key[4] << 3 | (key[5] >> 5 & 0xfe); - kk[6] = key[5] << 2 | (key[6] >> 6 & 0xfe); - kk[7] = key[6] << 1; - - keyt.ck_format = CRYPTO_KEY_RAW; - keyt.ck_length = 8 * 8; - keyt.ck_data = (void *)kk; - - d1.cd_format = CRYPTO_DATA_RAW; - d1.cd_length = 8; - d1.cd_offset = 0; - d1.cd_raw.iov_len = 8; - d1.cd_raw.iov_base = (void *)data; - - d2.cd_format = CRYPTO_DATA_RAW; - d2.cd_length = 8; - d2.cd_offset = 0; - d2.cd_raw.iov_len = 8; - d2.cd_raw.iov_base = (void *)dest; - - /* Checked this in callers */ - ASSERT(crypto_mech_des.cm_type != CRYPTO_MECH_INVALID); - - rv = crypto_encrypt(&crypto_mech_des, &d1, &keyt, NULL, &d2, NULL); - if (rv != CRYPTO_SUCCESS) - SMBSDEBUG("crypto_encrypt failed.\n"); -} - -/* - * Compute the LM hash, which is used to compute the LM response. - */ -void -smb_oldlm_hash(const char *apwd, uchar_t *lmhash) -{ - static const uchar_t N8[] = "KGS!@#$%"; - uchar_t P14[14+1]; - - /* In case we error out. */ - bzero(lmhash, 21); - - /* Note ASSERT in smb_E */ - if (crypto_mech_des.cm_type == CRYPTO_MECH_INVALID) { - SMBSDEBUG("crypto_mech_des invalid\n"); - return; - } - - /* Convert apwd to upper case, zero extend. */ - bzero(P14, sizeof (P14)); - smb_toupper(apwd, (char *)P14, 14); - - /* - * lmhash = concat(Ex(P14, N8), zeros(5)); - */ - smb_E(P14, N8, lmhash); - smb_E(P14 + 7, N8, lmhash + 8); -} - -/* - * Compute an LM or NTLM response given the LM or NTLM hash and a - * challenge. Note: This now replaces smb_ntlmresponse which - * used to compute a different hash and then do the same - * response computation as found here. Now that the hash - * is computed by the caller, this is used for both. - */ -int -smb_lmresponse(const uchar_t *hash, const uchar_t *C8, uchar_t *RN) -{ - - /* In case we error out. */ - bzero(RN, 24); - - /* Note ASSERT in smb_E */ - if (crypto_mech_des.cm_type == CRYPTO_MECH_INVALID) { - SMBSDEBUG("crypto_mech_des invalid\n"); - return (ENOTSUP); - } - - smb_E(hash, C8, RN); - smb_E(hash + 7, C8, RN + 8); - smb_E(hash + 14, C8, RN + 16); - - return (0); -} - -/* - * Compute the NTLMv1 hash, which is used to compute both NTLMv1 and - * NTLMv2 responses. - */ -void -smb_ntlmv1hash(const char *apwd, uchar_t *v1hash) -{ - u_int16_t *unipwd; - MD4_CTX *ctxp; - size_t alen, unilen; - - alen = strlen(apwd); - unipwd = kmem_alloc(alen * 2, KM_SLEEP); - /* - * v1hash = concat(MD4(U(apwd)), zeros(5)); - */ - unilen = smb_strtouni(unipwd, apwd, alen, UCONV_IGNORE_NULL); - - ctxp = kmem_alloc(sizeof (MD4_CTX), KM_SLEEP); - MD4Init(ctxp); - MD4Update(ctxp, unipwd, unilen); - bzero(v1hash, 21); - MD4Final(v1hash, ctxp); - - kmem_free(ctxp, sizeof (MD4_CTX)); - kmem_free(unipwd, alen * 2); -} - -/* - * Note: smb_ntlmresponse() is gone. - * Use: smb_lmresponse() instead. - */ - -static void -HMACT64(const uchar_t *key, size_t key_len, const uchar_t *data, - size_t data_len, uchar_t *digest) -{ - 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, - * the destination machine or domain name, and a challenge. - */ -void -smb_ntlmv2hash(const uchar_t *v1hash, const char *user, - const char *destination, uchar_t *v2hash) -{ - u_int16_t *uniuser, *unidest; - size_t uniuserlen, unidestlen; - size_t uniuser_sz, unidest_sz; - int len; - size_t datalen; - uchar_t *data; - - /* - * v2hash = HMACT64(v1hash, 16, concat(upcase(user), upcase(dest)) - * where "dest" is the domain or server name ("target name") - * We assume that user and destination are supplied to us as - * upper-case UTF-8. - */ - len = strlen((char *)user); - uniuser_sz = (len + 1) * sizeof (u_int16_t); - uniuser = kmem_alloc(uniuser_sz, KM_SLEEP); - uniuserlen = smb_strtouni(uniuser, (char *)user, len, - UCONV_IGNORE_NULL); - - len = strlen((char *)destination); - unidest_sz = (len + 1) * sizeof (u_int16_t); - unidest = kmem_alloc(unidest_sz, KM_SLEEP); - unidestlen = smb_strtouni(unidest, (char *)destination, len, - UCONV_IGNORE_NULL); - - datalen = uniuserlen + unidestlen; - data = kmem_alloc(datalen, KM_SLEEP); - bcopy(uniuser, data, uniuserlen); - bcopy(unidest, data + uniuserlen, unidestlen); - kmem_free(uniuser, uniuser_sz); - kmem_free(unidest, unidest_sz); - - HMACT64(v1hash, 16, data, datalen, v2hash); - kmem_free(data, datalen); -} - -/* - * Compute an NTLMv2 response given the 16 byte NTLMv2 hash, - * a challenge, and the blob. - */ -int -smb_ntlmv2response(const uchar_t *v2hash, const uchar_t *C8, - const uchar_t *blob, size_t bloblen, uchar_t **RN, size_t *RNlen) -{ - size_t datalen; - uchar_t *data; - size_t v2resplen; - uchar_t *v2resp; - - datalen = 8 + bloblen; - data = kmem_alloc(datalen, KM_SLEEP); - bcopy(C8, data, 8); - bcopy(blob, data + 8, bloblen); - v2resplen = 16 + bloblen; - v2resp = kmem_alloc(v2resplen, KM_SLEEP); - HMACT64(v2hash, 16, data, datalen, v2resp); - kmem_free(data, datalen); - bcopy(blob, v2resp + 16, bloblen); - *RN = v2resp; - *RNlen = v2resplen; - - return (0); -} - -/* - * Calculate NTLMv2 message authentication code (MAC) key for - * this VC and store it in vc_mackey (allocated here). - * - * The MAC key is the concatenation of the 16 byte session key - * and the NT response. - * - * XXX: Should factor out computation of the session key - * from both this and the next function, and then use a - * common function to compute the MAC key (which then - * can do simple concatenation). Later. - */ -int -smb_calcv2mackey(struct smb_vc *vcp, const uchar_t *v2hash, - const uchar_t *ntresp, size_t resplen) -{ - uchar_t sesskey[16]; - - if (vcp->vc_mackey != NULL) { - SMBSDEBUG("Already have MAC key!\n"); - return (0); - } - - /* session key uses only 1st 16 bytes of ntresp */ - HMACT64(v2hash, 16, ntresp, (size_t)16, sesskey); - - vcp->vc_mackeylen = 16 + resplen; - vcp->vc_mackey = kmem_alloc(vcp->vc_mackeylen, KM_SLEEP); - /* Free in: smb_vc_free, smb_smb_negotiate */ - - bcopy(sesskey, vcp->vc_mackey, 16); - bcopy(ntresp, vcp->vc_mackey + 16, (int)resplen); - -#ifdef DTRACE_PROBE - DTRACE_PROBE2(smb_mac_key, (char *), vcp->vc_mackey, - int, vcp->vc_mackeylen); -#endif - - return (0); -} - -/* - * Calculate message authentication code (MAC) key for virtual circuit. - * The MAC key is the concatenation of the 16 byte session key - * and the 24 byte challenge response. - */ -/*ARGSUSED*/ -int -smb_calcmackey(struct smb_vc *vcp, const uchar_t *v2hash, - const uchar_t *ntresp, size_t resplen) -{ - MD4_CTX md4; - - if (vcp->vc_mackey != NULL) { - SMBSDEBUG("Already have MAC key!\n"); - return (0); - } - - vcp->vc_mackeylen = 16 + 24; - vcp->vc_mackey = kmem_alloc(vcp->vc_mackeylen, KM_SLEEP); - /* Free in: smb_vc_free, smb_smb_negotiate */ - - /* - * Calculate session key: - */ - MD4Init(&md4); - MD4Update(&md4, vcp->vc_nthash, 16); - MD4Final(vcp->vc_mackey, &md4); - - /* Response to challenge. */ - bcopy(ntresp, vcp->vc_mackey + 16, 24); - -#ifdef DTRACE_PROBE - DTRACE_PROBE2(smb_mac_key, (char *), vcp->vc_mackey, - int, vcp->vc_mackeylen); -#endif - - return (0); -} - -#define SMBSIGLEN 8 /* SMB signature length */ -#define SMBSIGOFF 14 /* SMB signature offset */ - -/* - * Compute HMAC-MD5 of packet data, using the stored MAC key. - * - * See similar code for the server side: - * uts/common/fs/smbsrv/smb_signing.c : smb_sign_calc - */ -static int -smb_compute_MAC(struct smb_vc *vcp, mblk_t *mp, - uint32_t seqno, uchar_t *signature) -{ - crypto_context_t crypto_ctx; - crypto_data_t key; - crypto_data_t data; - crypto_data_t digest; - uchar_t mac[16]; - int status; - /* - * 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; - - ASSERT(mp != NULL); - ASSERT(MBLKL(mp) >= SMB_HDRLEN); - ASSERT(vcp->vc_mackey != NULL); - - /* - * Make an aligned copy of the SMB header - * and fill in the sequence number. - */ - bcopy(mp->b_rptr, smbhdr.r.raw, SMB_HDRLEN); - smbhdr.s.sig[0] = htolel(seqno); - smbhdr.s.sig[1] = 0; - - /* - * Compute the MAC: MD5(concat(Key, message)) - */ - if (crypto_mech_md5.cm_type == CRYPTO_MECH_INVALID) { - SMBSDEBUG("crypto_mech_md5 invalid\n"); - return (CRYPTO_MECHANISM_INVALID); - } - status = crypto_digest_init(&crypto_mech_md5, &crypto_ctx, 0); - if (status != CRYPTO_SUCCESS) - return (status); - - /* Digest the MAC Key */ - key.cd_format = CRYPTO_DATA_RAW; - key.cd_offset = 0; - key.cd_length = vcp->vc_mackeylen; - key.cd_miscdata = 0; - key.cd_raw.iov_base = (char *)vcp->vc_mackey; - key.cd_raw.iov_len = vcp->vc_mackeylen; - status = crypto_digest_update(crypto_ctx, &key, 0); - if (status != CRYPTO_SUCCESS) - return (status); - - /* Digest the (copied) SMB header */ - data.cd_format = CRYPTO_DATA_RAW; - data.cd_offset = 0; - data.cd_length = SMB_HDRLEN; - data.cd_miscdata = 0; - data.cd_raw.iov_base = (char *)smbhdr.r.raw; - data.cd_raw.iov_len = SMB_HDRLEN; - status = crypto_digest_update(crypto_ctx, &data, 0); - if (status != CRYPTO_SUCCESS) - return (status); - - /* Digest rest of the SMB message. */ - data.cd_format = CRYPTO_DATA_MBLK; - data.cd_offset = SMB_HDRLEN; - data.cd_length = msgdsize(mp) - SMB_HDRLEN; - data.cd_miscdata = 0; - data.cd_mp = mp; - status = crypto_digest_update(crypto_ctx, &data, 0); - if (status != CRYPTO_SUCCESS) - return (status); - - /* Final */ - digest.cd_format = CRYPTO_DATA_RAW; - digest.cd_offset = 0; - digest.cd_length = sizeof (mac); - digest.cd_miscdata = 0; - digest.cd_raw.iov_base = (char *)mac; - digest.cd_raw.iov_len = sizeof (mac); - status = crypto_digest_final(crypto_ctx, &digest, 0); - if (status != CRYPTO_SUCCESS) - return (status); - - /* - * Finally, store the signature. - * (first 8 bytes of the mac) - */ - if (signature) - bcopy(mac, signature, SMBSIGLEN); - - return (0); -} - -/* - * Sign a request with HMAC-MD5. - */ -int -smb_rq_sign(struct smb_rq *rqp) -{ - struct smb_vc *vcp = rqp->sr_vc; - mblk_t *mp = rqp->sr_rq.mb_top; - uint8_t *sigloc; - int status; - - /* - * Our mblk allocation ensures this, - * but just in case... - */ - if (MBLKL(mp) < SMB_HDRLEN) { - if (!pullupmsg(mp, SMB_HDRLEN)) - return (0); - } - sigloc = mp->b_rptr + SMBSIGOFF; - - if (vcp->vc_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. - */ - status = smb_compute_MAC(vcp, mp, rqp->sr_seqno, sigloc); - if (status != CRYPTO_SUCCESS) { - SMBSDEBUG("Crypto error %d", status); - bzero(sigloc, SMBSIGLEN); - return (ENOTSUP); - } - return (0); -} - -/* - * Verify reply signature. - */ -int -smb_rq_verify(struct smb_rq *rqp) -{ - struct smb_vc *vcp = rqp->sr_vc; - mblk_t *mp = rqp->sr_rp.md_top; - uint8_t sigbuf[SMBSIGLEN]; - uint8_t *sigloc; - int status; - int fudge; - - /* - * Note vc_mackey and vc_mackeylen gets initialized by - * smb_smb_ssnsetup. - */ - if (vcp->vc_mackey == NULL) { - SMBSDEBUG("no mac key\n"); - return (0); - } - - /* - * Let caller deal with empty reply or short messages by - * returning zero. Caller will fail later, in parsing. - */ - if (mp == NULL) { - SMBSDEBUG("empty reply\n"); - return (0); - } - if (MBLKL(mp) < SMB_HDRLEN) { - if (!pullupmsg(mp, SMB_HDRLEN)) - return (0); - } - sigloc = mp->b_rptr + SMBSIGOFF; - - SMBSDEBUG("sr_rseqno = 0x%x\n", rqp->sr_rseqno); - - status = smb_compute_MAC(vcp, mp, rqp->sr_rseqno, sigbuf); - if (status != CRYPTO_SUCCESS) { - SMBSDEBUG("Crypto error %d", status); - /* - * 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); - - SMBSDEBUG("BAD signature, MID=0x%x\n", rqp->sr_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(vcp, mp, rqp->sr_rseqno + fudge, sigbuf); - if (bcmp(sigbuf, sigloc, SMBSIGLEN) == 0) - break; - smb_compute_MAC(vcp, mp, rqp->sr_rseqno - fudge, sigbuf); - if (bcmp(sigbuf, sigloc, SMBSIGLEN) == 0) { - fudge = -fudge; - break; - } - } - if (fudge <= nsmb_signing_fudge) { - SMBSDEBUG("sr_rseqno=%d, but %d would have worked\n", - rqp->sr_rseqno, rqp->sr_rseqno + fudge); - } -#endif - return (EBADRPC); -} diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/smb_dev.c b/usr/src/uts/common/fs/smbclnt/netsmb/smb_dev.c index 29795281f4..8ec7daef1c 100644 --- a/usr/src/uts/common/fs/smbclnt/netsmb/smb_dev.c +++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_dev.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. */ @@ -114,6 +114,8 @@ static int nsmb_open(dev_t *devp, int flag, int otyp, cred_t *credp); static int nsmb_close(dev_t dev, int flag, int otyp, cred_t *credp); static int nsmb_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, int *rvalp); +static int nsmb_close2(smb_dev_t *sdp, cred_t *cr); + /* smbfs cb_ops */ static struct cb_ops nsmb_cbops = { nsmb_open, /* open */ @@ -165,7 +167,7 @@ static struct dev_ops nsmb_ops = { static struct modldrv nsmb_modldrv = { &mod_driverops, /* Driver module */ - "SMBFS network driver v" NSMB_VER_STR, + "SMBFS network driver", &nsmb_ops /* Driver ops */ }; @@ -350,26 +352,11 @@ nsmb_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) /*ARGSUSED*/ static int -nsmb_ioctl(dev_t dev, - int cmd, - intptr_t arg, - int mode, - cred_t *credp, - int *rvalp) +nsmb_ioctl(dev_t dev, int cmd, intptr_t arg, int flags, /* model.h */ + cred_t *cr, int *rvalp) { smb_dev_t *sdp; - struct smb_vc *vcp = NULL; - struct smb_share *ssp = NULL; - struct smb_cred scred; - int err, error; - uid_t uid; - - /* Free any+all of these at end of switch. */ - smbioc_lookup_t *sioc = NULL; - smbioc_rq_t *srq = NULL; - smbioc_rw_t *rwrq = NULL; - smbioc_t2rq_t *strq = NULL; - smbioc_pk_t *pk = NULL; + int err; sdp = ddi_get_soft_state(statep, getminor(dev)); if (sdp == NULL) { @@ -386,413 +373,85 @@ nsmb_ioctl(dev_t dev, */ if (sdp->zoneid != getzoneid()) return (EIO); - if (cmd != SMBIOC_TDIS && - zone_status_get(curproc->p_zone) >= ZONE_IS_SHUTTING_DOWN) - return (EIO); + /* + * We have a zone_shutdown call back that kills all the VCs + * in a zone that's shutting down. That action will cause + * all of these ioctls to fail on such VCs, so no need to + * check the zone status here on every ioctl call. + */ - error = 0; - smb_credinit(&scred, curproc, credp); + err = 0; switch (cmd) { - case SMBIOC_GETVERS: - ddi_copyout(&nsmb_version, (void *)arg, - sizeof (nsmb_version), mode); - break; - - case SMBIOC_REQUEST: - if (sdp->sd_share == NULL) { - error = ENOTCONN; - break; - } - srq = kmem_alloc(sizeof (*srq), KM_SLEEP); - if (ddi_copyin((void *) arg, srq, - sizeof (*srq), mode)) { - error = EFAULT; - break; - } - error = smb_usr_simplerequest(sdp->sd_share, - srq, &scred); - ddi_copyout(srq, (void *)arg, - SMBIOC_RQ_COPYOUT_SIZE, mode); - break; - - case SMBIOC_T2RQ: - if (sdp->sd_share == NULL) { - error = ENOTCONN; - break; - } - strq = kmem_alloc(sizeof (*strq), KM_SLEEP); - if (ddi_copyin((void *)arg, strq, - sizeof (*strq), mode)) { - error = EFAULT; - break; - } - error = smb_usr_t2request(sdp->sd_share, strq, &scred); - ddi_copyout(strq, (void *)arg, - SMBIOC_T2RQ_COPYOUT_SIZE, mode); - break; - - case SMBIOC_READ: - case SMBIOC_WRITE: - if ((ssp = sdp->sd_share) == NULL) { - error = ENOTCONN; - break; - } - rwrq = kmem_alloc(sizeof (*rwrq), KM_SLEEP); - if (ddi_copyin((void *)arg, rwrq, - sizeof (*rwrq), mode)) { - error = EFAULT; - break; - } - error = smb_usr_rw(ssp, rwrq, cmd, &scred); - ddi_copyout(rwrq, (void *)arg, - SMBIOC_RW_COPYOUT_SIZE, mode); - break; - - case SMBIOC_FINDVC: - /* Should be no VC and no share */ - if (sdp->sd_vc || sdp->sd_share) { - error = EISCONN; - break; - } - sioc = kmem_alloc(sizeof (*sioc), KM_SLEEP); - if (ddi_copyin((void *)arg, sioc, - sizeof (*sioc), mode)) { - error = EFAULT; - break; - } - vcp = NULL; - ssp = NULL; - error = smb_usr_findvc(sioc, &scred, &vcp); - if (error) - break; - if (vcp) { - /* - * The VC has a hold from _findvc - * which we keep until nsmb_close(). - */ - sdp->sd_level = SMBL_VC; - sdp->sd_vc = vcp; - } - (void) ddi_copyout(sioc, (void *)arg, - SMBIOC_LOOK_COPYOUT_SIZE, mode); - - break; - - case SMBIOC_NEGOTIATE: - /* Should be no VC (and no share) */ - if (sdp->sd_vc || sdp->sd_share) { - error = EISCONN; - break; - } - sioc = kmem_alloc(sizeof (*sioc), KM_SLEEP); - if (ddi_copyin((void *)arg, sioc, - sizeof (*sioc), mode)) { - error = EFAULT; - break; - } - vcp = NULL; - ssp = NULL; - error = smb_usr_negotiate(sioc, &scred, &vcp); - if (error) - break; - if (vcp) { - /* - * The VC has a hold from _negotiate - * which we keep until nsmb_close(). - */ - sdp->sd_level = SMBL_VC; - sdp->sd_vc = vcp; - /* - * If we just created this VC, and - * this minor is doing the setup, - * keep track of that fact here. - */ - if (vcp->vc_state < SMBIOD_ST_VCACTIVE) - sdp->sd_flags |= NSMBFL_NEWVC; - - } - /* - * Copyout the "out token" (security blob). - * - * This code used to be near the end of - * smb_usr_negotiate(). Moved the copyout - * calls here so we know the "mode" - */ - if (vcp->vc_outtok) { - /* - * Note: will copyout sioc below - * including sioc.vc_outtoklen, - * so we no longer put the length - * at the start of the outtok data. - */ - sioc->ioc_ssn.ioc_outtoklen = - vcp->vc_outtoklen; - err = ddi_copyout( - vcp->vc_outtok, - sioc->ioc_ssn.ioc_outtok, - vcp->vc_outtoklen, mode); - if (err) { - error = EFAULT; - break; - } - /* - * Save this blob in vc_negtok. - * We need it in case we have to - * reconnect. - * - * Set vc_negtok = vc_outtok - * but free vc_negtok first. - */ - if (vcp->vc_negtok) { - kmem_free( - vcp->vc_negtok, - vcp->vc_negtoklen); - vcp->vc_negtok = NULL; - vcp->vc_negtoklen = 0; - } - vcp->vc_negtok = vcp->vc_outtok; - vcp->vc_negtoklen = vcp->vc_outtoklen; - vcp->vc_outtok = NULL; - vcp->vc_outtoklen = 0; - } - /* - * Added copyout here of (almost) - * the whole struct, even though - * the lib only needs _outtoklen. - * We may put other things in this - * struct that user-land needs. - */ - err = ddi_copyout(sioc, (void *)arg, - SMBIOC_LOOK_COPYOUT_SIZE, mode); - if (err) - error = EFAULT; - break; - - case SMBIOC_SSNSETUP: - /* Must have a VC, but no share. */ - if (sdp->sd_share) { - error = EISCONN; - break; - } - if (!sdp->sd_vc) { - error = ENOTCONN; - break; - } - sioc = kmem_alloc(sizeof (*sioc), KM_SLEEP); - if (ddi_copyin((void *)arg, sioc, - sizeof (*sioc), mode)) { - error = EFAULT; - break; - } - vcp = sdp->sd_vc; - ssp = NULL; - error = smb_usr_ssnsetup(sioc, &scred, vcp); - if (error) - break; - /* - * If this minor has finished ssn setup, - * turn off the NEWVC flag, otherwise we - * will kill this VC when we close. - */ - if (vcp->vc_state == SMBIOD_ST_VCACTIVE) - sdp->sd_flags &= ~NSMBFL_NEWVC; - /* - * Copyout the "out token" (security blob). - * - * This code used to be near the end of - * smb_usr_ssnsetup(). Moved the copyout - * calls here so we know the "mode" - */ - if (vcp->vc_outtok) { - /* - * Note: will copyout sioc below - * including sioc.vc_outtoklen, - * so we no longer put the length - * at the start of the outtok data. - */ - sioc->ioc_ssn.ioc_outtoklen = - vcp->vc_outtoklen; - err = ddi_copyout( - vcp->vc_outtok, - sioc->ioc_ssn.ioc_outtok, - vcp->vc_outtoklen, mode); - if (err) { - error = EFAULT; - break; - } - /* - * Done with vc_outtok. Similar, - * but NOT the same as after the - * smb_usr_negotiate call above. - */ - kmem_free( - vcp->vc_outtok, - vcp->vc_outtoklen); - vcp->vc_outtok = NULL; - vcp->vc_outtoklen = 0; - } - /* Added copyout here... (see above) */ - err = ddi_copyout(sioc, (void *)arg, - SMBIOC_LOOK_COPYOUT_SIZE, mode); - if (err) - error = EFAULT; - break; - - case SMBIOC_TCON: - /* Must have a VC, but no share. */ - if (sdp->sd_share) { - error = EISCONN; - break; - } - if (!sdp->sd_vc) { - error = ENOTCONN; - break; - } - sioc = kmem_alloc(sizeof (*sioc), KM_SLEEP); - if (ddi_copyin((void *)arg, sioc, - sizeof (*sioc), mode)) { - error = EFAULT; - break; - } - vcp = sdp->sd_vc; - ssp = NULL; - error = smb_usr_tcon(sioc, &scred, vcp, &ssp); - if (error) - break; - if (ssp) { - /* - * The share has a hold from _tcon - * which we keep until nsmb_close() - * or the SMBIOC_TDIS below. - */ - sdp->sd_share = ssp; - sdp->sd_level = SMBL_SHARE; - } - /* No need for copyout here. */ - break; + case SMBIOC_GETVERS: + ddi_copyout(&nsmb_version, (void *)arg, + sizeof (nsmb_version), flags); + break; - case SMBIOC_TDIS: - if (sdp->sd_share == NULL) { - error = ENOTCONN; - break; - } - smb_share_rele(sdp->sd_share); - sdp->sd_share = NULL; - sdp->sd_level = SMBL_VC; - break; - case SMBIOC_FLAGS2: - if (sdp->sd_share == NULL) { - error = ENOTCONN; - break; - } - if (!sdp->sd_vc) { - error = ENOTCONN; - break; - } - vcp = sdp->sd_vc; - /* - * Return the flags2 value. - */ - ddi_copyout(&vcp->vc_hflags2, (void *)arg, - sizeof (u_int16_t), mode); - break; + case SMBIOC_FLAGS2: + err = smb_usr_get_flags2(sdp, arg, flags); + break; - case SMBIOC_PK_ADD: - pk = kmem_alloc(sizeof (*pk), KM_SLEEP); - if (ddi_copyin((void *)arg, pk, - sizeof (*pk), mode)) { - error = EFAULT; - break; - } - error = smb_pkey_add(pk, credp); - break; + case SMBIOC_GETSSNKEY: + err = smb_usr_get_ssnkey(sdp, arg, flags); + break; - case SMBIOC_PK_DEL: - pk = kmem_alloc(sizeof (*pk), KM_SLEEP); - if (ddi_copyin((void *)arg, pk, - sizeof (*pk), mode)) { - error = EFAULT; - break; - } - error = smb_pkey_del(pk, credp); - break; + case SMBIOC_REQUEST: + err = smb_usr_simplerq(sdp, arg, flags, cr); + break; - case SMBIOC_PK_CHK: - pk = kmem_alloc(sizeof (*pk), KM_SLEEP); - if (ddi_copyin((void *)arg, pk, - sizeof (*pk), mode)) { - error = EFAULT; - break; - } - error = smb_pkey_check(pk, credp); - /* - * Note: Intentionally DO NOT copyout - * the pasword here. It can only be - * retrieved by internal calls. This - * ioctl only tells the caller if the - * keychain entry exists. - */ - break; + case SMBIOC_T2RQ: + err = smb_usr_t2request(sdp, arg, flags, cr); + break; - case SMBIOC_PK_DEL_OWNER: - uid = crgetruid(credp); - error = smb_pkey_deluid(uid, credp); - break; + case SMBIOC_READ: + case SMBIOC_WRITE: + err = smb_usr_rw(sdp, cmd, arg, flags, cr); + break; - case SMBIOC_PK_DEL_EVERYONE: - uid = (uid_t)-1; - error = smb_pkey_deluid(uid, credp); - break; + case SMBIOC_SSN_CREATE: + case SMBIOC_SSN_FIND: + err = smb_usr_get_ssn(sdp, cmd, arg, flags, cr); + break; - default: - error = ENODEV; - } + case SMBIOC_SSN_KILL: + case SMBIOC_SSN_RELE: + err = smb_usr_drop_ssn(sdp, cmd); + break; - /* - * Let's just do all the kmem_free stuff HERE, - * instead of at every switch break. - */ + case SMBIOC_TREE_CONNECT: + case SMBIOC_TREE_FIND: + err = smb_usr_get_tree(sdp, cmd, arg, flags, cr); + break; - /* SMBIOC_REQUEST */ - if (srq) - kmem_free(srq, sizeof (*srq)); + case SMBIOC_TREE_KILL: + case SMBIOC_TREE_RELE: + err = smb_usr_drop_tree(sdp, cmd); + break; - /* SMBIOC_T2RQ */ - if (strq) - kmem_free(strq, sizeof (*strq)); + case SMBIOC_IOD_WORK: + err = smb_usr_iod_work(sdp, arg, flags, cr); + break; - /* SMBIOC_READ */ - /* SMBIOC_WRITE */ - if (rwrq) - kmem_free(rwrq, sizeof (*rwrq)); + case SMBIOC_IOD_IDLE: + case SMBIOC_IOD_RCFAIL: + err = smb_usr_iod_ioctl(sdp, cmd, arg, flags); + break; - /* SMBIOC_FINDVC */ - /* SMBIOC_NEGOTIATE */ - /* SMBIOC_SSNSETUP */ - /* SMBIOC_TCON */ - if (sioc) { - /* - * This data structure may contain - * cleartext passwords, so zap it. - */ - bzero(sioc, sizeof (*sioc)); - kmem_free(sioc, sizeof (*sioc)); - } + case SMBIOC_PK_ADD: + case SMBIOC_PK_DEL: + case SMBIOC_PK_CHK: + case SMBIOC_PK_DEL_OWNER: + case SMBIOC_PK_DEL_EVERYONE: + err = smb_pkey_ioctl(cmd, arg, flags, cr); + break; - /* SMBIOC_PK_... */ - if (pk) { - /* - * This data structure may contain - * cleartext passwords, so zap it. - */ - bzero(pk, sizeof (*pk)); - kmem_free(pk, sizeof (*pk)); + default: + err = ENOTTY; + break; } - smb_credrele(&scred); - - return (error); + return (err); } /*ARGSUSED*/ @@ -879,11 +538,9 @@ nsmb_open(dev_t *dev, int flags, int otyp, cred_t *cr) static int nsmb_close(dev_t dev, int flags, int otyp, cred_t *cr) { - struct smb_vc *vcp; - struct smb_share *ssp; - struct smb_cred scred; minor_t inst = getminor(dev); smb_dev_t *sdp; + int err; mutex_enter(&dev_lck); /* @@ -892,79 +549,90 @@ nsmb_close(dev_t dev, int flags, int otyp, cred_t *cr) * 3. Can close the minor number. * 4. Deallocate any resources allocated in open() call. */ - smb_credinit(&scred, curproc, cr); sdp = ddi_get_soft_state(statep, inst); + if (sdp != NULL) + err = nsmb_close2(sdp, cr); + else + err = ENXIO; /* - * time to call ddi_get_soft_state() + * Free the instance */ + ddi_soft_state_free(statep, inst); + mutex_exit(&dev_lck); + return (err); +} + +static int +nsmb_close2(smb_dev_t *sdp, cred_t *cr) +{ + struct smb_vc *vcp; + struct smb_share *ssp; + struct smb_cred scred; + + smb_credinit(&scred, cr); ssp = sdp->sd_share; if (ssp != NULL) smb_share_rele(ssp); vcp = sdp->sd_vc; if (vcp != NULL) { /* - * If this dev minor was doing session setup - * and failed to authenticate (or whatever) - * then we need to put the VC in a state that - * allows later commands to try again. + * If this dev minor was opened by smbiod, + * mark this VC as "dead" because it now + * will have no IOD to service it. */ - if (sdp->sd_flags & NSMBFL_NEWVC) + if (sdp->sd_flags & NSMBFL_IOD) smb_iod_disconnect(vcp); smb_vc_rele(vcp); } - smb_credrele(&scred); - /* - * Free the instance - */ - ddi_soft_state_free(statep, inst); - mutex_exit(&dev_lck); + smb_credrele(&scred); return (0); } int smb_dev2share(int fd, struct smb_share **sspp) { - register vnode_t *vp; + file_t *fp = NULL; + vnode_t *vp; smb_dev_t *sdp; - struct smb_share *ssp; + smb_share_t *ssp; dev_t dev; - file_t *fp; + int err; if ((fp = getf(fd)) == NULL) - return (set_errno(EBADF)); + return (EBADF); + vp = fp->f_vnode; dev = vp->v_rdev; - if (dev == NULL) { - releasef(fd); - return (EBADF); + if (dev == 0 || dev == NODEV || + getmajor(dev) != nsmb_major) { + err = EBADF; + goto out; } + sdp = ddi_get_soft_state(statep, getminor(dev)); if (sdp == NULL) { - releasef(fd); - return (DDI_FAILURE); + err = EINVAL; + goto out; } + ssp = sdp->sd_share; if (ssp == NULL) { - releasef(fd); - return (ENOTCONN); + err = ENOTCONN; + goto out; } + /* - * The share is already locked and referenced by the TCON ioctl - * We NULL to hand off share to caller (mount) - * This allows further ioctls against connection, for instance - * another tree connect and mount, in the automounter case - * - * We're effectively giving our reference to the mount. - * - * XXX: I'm not sure I like this. I'd rather see the ioctl - * caller do something explicit to give up this reference, - * (i.e. SMBIOC_TDIS above) and increment the hold here. + * Our caller gains a ref. to this share. */ - sdp->sd_share = NULL; - releasef(fd); *sspp = ssp; - return (0); + smb_share_hold(ssp); + err = 0; + +out: + if (fp) + releasef(fd); + return (err); } diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/smb_iod.c b/usr/src/uts/common/fs/smbclnt/netsmb/smb_iod.c index b1c9c1d342..8f6e95de23 100644 --- a/usr/src/uts/common/fs/smbclnt/netsmb/smb_iod.c +++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_iod.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,6 +47,7 @@ #include <sys/atomic.h> #include <sys/proc.h> #include <sys/thread.h> +#include <sys/file.h> #include <sys/kmem.h> #include <sys/unistd.h> #include <sys/mount.h> @@ -76,7 +77,8 @@ #include <netsmb/smb_tran.h> #include <netsmb/smb_trantcp.h> -#ifdef NEED_SMBFS_CALLBACKS +int smb_iod_send_echo(smb_vc_t *); + /* * This is set/cleared when smbfs loads/unloads * No locks should be necessary, because smbfs @@ -89,28 +91,28 @@ smb_fscb_set(smb_fscb_t *cb) fscb = cb; return (0); } -#endif /* NEED_SMBFS_CALLBACKS */ -static void smb_iod_sendall(struct smb_vc *); -static void smb_iod_recvall(struct smb_vc *); -static void smb_iod_main(struct smb_vc *); +static void +smb_iod_share_disconnected(smb_share_t *ssp) +{ + smb_share_invalidate(ssp); -#define SMBIOD_SLEEP_TIMO 2 -#define SMBIOD_PING_TIMO 60 /* seconds */ + /* smbfs_dead() */ + if (fscb && fscb->fscb_disconn) { + fscb->fscb_disconn(ssp); + } +} /* - * After this many seconds we want an unresponded-to request to trigger - * some sort of UE (dialogue). If the connection hasn't responded at all - * in this many seconds then the dialogue is of the "connection isn't - * responding would you like to force unmount" variety. If the connection - * has been responding (to other requests that is) then we need a dialogue - * of the "operation is still pending do you want to cancel it" variety. - * At present this latter dialogue does not exist so we have no UE and - * just keep waiting for the slow operation. + * State changes are important and infrequent. + * Make them easily observable via dtrace. */ -#define SMBUETIMEOUT 8 /* seconds */ - +void +smb_iod_newstate(struct smb_vc *vcp, int state) +{ + vcp->vc_state = state; +} /* Lock Held version of the next function. */ static inline void @@ -153,130 +155,45 @@ smb_iod_invrq(struct smb_vc *vcp) rw_exit(&vcp->iod_rqlock); } -#ifdef SMBTP_UPCALL -static void -smb_iod_sockwakeup(struct smb_vc *vcp) -{ - /* note: called from socket upcall... */ -} -#endif - /* - * Called after we fail to send or recv. - * Called with no locks held. + * Called by smb_vc_rele, smb_vc_kill, and by the driver + * close entry point if the IOD closes its dev handle. + * + * Forcibly kill the connection and IOD. */ -static void -smb_iod_dead(struct smb_vc *vcp) +int +smb_iod_disconnect(struct smb_vc *vcp) { + /* + * Inform everyone of the state change. + */ SMB_VC_LOCK(vcp); - vcp->vc_state = SMBIOD_ST_DEAD; - cv_broadcast(&vcp->vc_statechg); - -#ifdef NEED_SMBFS_CALLBACKS - if (fscb != NULL) { - struct smb_connobj *co; - /* - * Walk the share list, notify... - * Was: smbfs_dead(...share->ss_mount); - * XXX: Ok to hold vc_lock here? - * XXX: More to do here? - */ - SLIST_FOREACH(co, &(VCTOCP(vcp)->co_children), co_next) { - /* smbfs_dead() */ - fscb->fscb_dead(CPTOSS(co)); - } + if (vcp->vc_state != SMBIOD_ST_DEAD) { + smb_iod_newstate(vcp, SMBIOD_ST_DEAD); + cv_broadcast(&vcp->vc_statechg); } -#endif /* NEED_SMBFS_CALLBACKS */ - SMB_VC_UNLOCK(vcp); - smb_iod_invrq(vcp); -} - -int -smb_iod_connect(struct smb_vc *vcp) -{ - struct proc *p = curproc; - int error; - - if (vcp->vc_state != SMBIOD_ST_RECONNECT) - return (EINVAL); - - if (vcp->vc_laddr) { - error = SMB_TRAN_BIND(vcp, vcp->vc_laddr, p); - if (error) - goto errout; - } - -#ifdef SMBTP_SELECTID - SMB_TRAN_SETPARAM(vcp, SMBTP_SELECTID, vcp); -#endif -#ifdef SMBTP_UPCALL - SMB_TRAN_SETPARAM(vcp, SMBTP_UPCALL, (void *)smb_iod_sockwakeup); -#endif - - error = SMB_TRAN_CONNECT(vcp, vcp->vc_paddr, p); - if (error) { - SMBIODEBUG("connection to %s error %d\n", - vcp->vc_srvname, error); - goto errout; - } - - /* Success! */ - return (0); - -errout: - - return (error); -} - -/* - * Called by smb_vc_rele, smb_vc_kill - * Make the connection go away, and - * the IOD (reader) thread too! - */ -int -smb_iod_disconnect(struct smb_vc *vcp) -{ - /* * Let's be safe here and avoid doing any * call across the network while trying to * shut things down. If we just disconnect, * the server will take care of the logoff. */ -#if 0 - if (vcp->vc_state == SMBIOD_ST_VCACTIVE) { - smb_smb_ssnclose(vcp, &vcp->vc_scred); - vcp->vc_state = SMBIOD_ST_TRANACTIVE; - } - vcp->vc_smbuid = SMB_UID_UNKNOWN; -#endif - - /* - * Used to call smb_iod_closetran here, - * which did both disconnect and close. - * We now do the close in smb_vc_free, - * so we always have a valid vc_tdata. - * Now just send the disconnect here. - * Extra disconnect calls are ignored. - */ - SMB_TRAN_DISCONNECT(vcp, curproc); + SMB_TRAN_DISCONNECT(vcp); /* - * If we have an IOD, let it handle the - * state change when it receives the ACK - * from the disconnect we just sent. - * Otherwise set the state here, i.e. - * after failing session setup. + * If we have an IOD, it should immediately notice + * that its connection has closed. But in case + * it doesn't, let's also send it a signal. + * (but don't shoot our own foot!) + * Note: the iod calls smb_iod_invrq on its way out. */ - SMB_VC_LOCK(vcp); - if (vcp->vc_state != SMBIOD_ST_VCACTIVE) { - vcp->vc_state = SMBIOD_ST_DEAD; - cv_broadcast(&vcp->vc_statechg); + if (vcp->iod_thr != NULL && + vcp->iod_thr != curthread) { + tsignal(vcp->iod_thr, SIGKILL); } - SMB_VC_UNLOCK(vcp); return (0); } @@ -290,9 +207,7 @@ smb_iod_disconnect(struct smb_vc *vcp) static int smb_iod_sendrq(struct smb_rq *rqp) { - struct proc *p = curproc; struct smb_vc *vcp = rqp->sr_vc; - struct smb_share *ssp = rqp->sr_share; mblk_t *m; int error; @@ -301,66 +216,36 @@ smb_iod_sendrq(struct smb_rq *rqp) ASSERT(RW_READ_HELD(&vcp->iod_rqlock)); /* - * Note: requests with sr_flags & SMBR_INTERNAL - * need to pass here with these states: - * SMBIOD_ST_TRANACTIVE: smb_negotiate - * SMBIOD_ST_NEGOACTIVE: smb_ssnsetup + * Note: Anything special for SMBR_INTERNAL here? */ - SMBIODEBUG("vc_state = %d\n", vcp->vc_state); - switch (vcp->vc_state) { - case SMBIOD_ST_NOTCONN: - smb_iod_rqprocessed(rqp, ENOTCONN, 0); - return (0); - case SMBIOD_ST_DEAD: - /* This is what keeps the iod itself from sending more */ - smb_iod_rqprocessed(rqp, ENOTCONN, 0); - return (0); - case SMBIOD_ST_RECONNECT: - return (0); - default: - break; + if (vcp->vc_state != SMBIOD_ST_VCACTIVE) { + SMBIODEBUG("bad vc_state=%d\n", vcp->vc_state); + return (ENOTCONN); } + + /* + * On the first send, set the MID and (maybe) + * the signing sequence numbers. The increments + * here are serialized by vc_sendlock + */ if (rqp->sr_sendcnt == 0) { - *rqp->sr_rquid = htoles(vcp->vc_smbuid); - /* - * XXX: Odd place for all this... - * Would expect these values in vc_smbuid - * and/or the request before we get here. - * I think most of this mess is due to having - * the initial UID set to SMB_UID_UKNOWN when - * it should have been initialized to zero! - * REVIST this later. XXX -gwr - * - * This is checking for the case where - * "vc_smbuid" was set to 0 in "smb_smb_ssnsetup()"; - * that happens for requests that occur - * after that's done but before we get back the final - * session setup reply, where the latter is what - * gives us the UID. (There can be an arbitrary # of - * session setup packet exchanges to complete - * "extended security" authentication.) - * - * However, if the server gave us a UID of 0 in a - * Session Setup andX reply, and we then do a - * Tree Connect andX and get back a TID, we should - * use that TID, not 0, in subsequent references to - * that tree (e.g., in NetShareEnum RAP requests). - * - * So, for now, we forcibly zero out the TID only if we're - * doing extended security, as that's the only time - * that "vc_smbuid" should be explicitly zeroed. - * - * note we must and do use SMB_TID_UNKNOWN for SMB_COM_ECHO - */ - if (!vcp->vc_smbuid && - (vcp->vc_hflags2 & SMB_FLAGS2_EXT_SEC)) - *rqp->sr_rqtid = htoles(0); - else - *rqp->sr_rqtid = - htoles(ssp ? ssp->ss_tid : SMB_TID_UNKNOWN); - mb_fixhdr(&rqp->sr_rq); + rqp->sr_mid = vcp->vc_next_mid++; + + if (rqp->sr_rqflags2 & SMB_FLAGS2_SECURITY_SIGNATURE) { + /* + * We're signing requests and verifying + * signatures on responses. Set the + * sequence numbers of the request and + * response here, used in smb_rq_verify. + */ + rqp->sr_seqno = vcp->vc_next_seq++; + rqp->sr_rseqno = vcp->vc_next_seq++; + } + + /* Fill in UID, TID, MID, etc. */ + smb_rq_fillhdr(rqp); /* * Sign the message now that we're finally done @@ -394,8 +279,13 @@ smb_iod_sendrq(struct smb_rq *rqp) #endif m_dumpm(m); - error = rqp->sr_lerror = m ? SMB_TRAN_SEND(vcp, m, p) : ENOBUFS; - m = 0; /* consumed by SEND */ + if (m != NULL) { + error = SMB_TRAN_SEND(vcp, m); + m = 0; /* consumed by SEND */ + } else + error = ENOBUFS; + + rqp->sr_lerror = error; if (error == 0) { SMBRQ_LOCK(rqp); rqp->sr_flags |= SMBR_SENT; @@ -430,14 +320,13 @@ smb_iod_sendrq(struct smb_rq *rqp) static int smb_iod_recv1(struct smb_vc *vcp, mblk_t **mpp) { - struct proc *p = curproc; mblk_t *m; uchar_t *hp; int error; top: m = NULL; - error = SMB_TRAN_RECV(vcp, &m, p); + error = SMB_TRAN_RECV(vcp, &m); if (error == EAGAIN) goto top; if (error) @@ -469,38 +358,50 @@ top: * while in state SMBIOD_ST_VCACTIVE. The loop now * simply blocks in the socket recv until either a * message arrives, or a disconnect. + * + * Any non-zero error means the IOD should terminate. */ -static void +int smb_iod_recvall(struct smb_vc *vcp) { struct smb_rq *rqp; mblk_t *m; uchar_t *hp; ushort_t mid; - int error; + int error = 0; int etime_count = 0; /* for "server not responding", etc. */ for (;;) { + /* + * Check whether someone "killed" this VC, + * or is asking the IOD to terminate. + */ if (vcp->vc_state != SMBIOD_ST_VCACTIVE) { SMBIODEBUG("bad vc_state=%d\n", vcp->vc_state); - error = EIO; + error = 0; break; } if (vcp->iod_flags & SMBIOD_SHUTDOWN) { SMBIODEBUG("SHUTDOWN set\n"); - error = EIO; + /* This IOD thread will terminate. */ + SMB_VC_LOCK(vcp); + smb_iod_newstate(vcp, SMBIOD_ST_DEAD); + cv_broadcast(&vcp->vc_statechg); + SMB_VC_UNLOCK(vcp); + error = EINTR; break; } m = NULL; error = smb_iod_recv1(vcp, &m); - if ((error == ETIME) && vcp->iod_rqwaiting) { + if (error == ETIME && + vcp->iod_rqlist.tqh_first != NULL) { /* - * Nothing received for 15 seconds, - * and we have requests waiting. + * Nothing received for 15 seconds and + * we have requests in the queue. */ etime_count++; @@ -509,7 +410,10 @@ smb_iod_recvall(struct smb_vc *vcp) * and print the warning message. */ if (etime_count == 1) { - smb_iod_notify_down(vcp); + /* Was: smb_iod_notify_down(vcp); */ + if (fscb && fscb->fscb_down) + smb_vc_walkshares(vcp, + fscb->fscb_down); zprintf(vcp->vc_zoneid, "SMB server %s not responding\n", vcp->vc_srvname); @@ -517,38 +421,27 @@ smb_iod_recvall(struct smb_vc *vcp) /* * At 30 sec. try sending an echo, and then - * once a minute thereafter. It's tricky to - * do a send from the IOD thread because - * we don't want to block here. - * - * Using tmo=SMBNOREPLYWAIT in the request - * so smb_rq_reply will skip smb_iod_waitrq. - * The smb_smb_echo call uses SMBR_INTERNAL - * to avoid calling smb_iod_sendall(). + * once a minute thereafter. */ if ((etime_count & 3) == 2) { - smb_smb_echo(vcp, &vcp->vc_scred, - SMBNOREPLYWAIT); + (void) smb_iod_send_echo(vcp); } continue; - } /* ETIME && iod_rqwaiting */ + } /* ETIME && requests in queue */ if (error == ETIME) { /* * If the IOD thread holds the last reference - * to this VC, disconnect, release, terminate. - * Usually can avoid the lock/unlock here. - * Note, in-line: _vc_kill ... _vc_gone + * to this VC, let the IOD thread terminate. */ if (vcp->vc_co.co_usecount > 1) continue; SMB_VC_LOCK(vcp); - if (vcp->vc_co.co_usecount == 1 && - (vcp->vc_flags & SMBV_GONE) == 0) { - vcp->vc_flags |= SMBV_GONE; + if (vcp->vc_co.co_usecount == 1) { + smb_iod_newstate(vcp, SMBIOD_ST_DEAD); SMB_VC_UNLOCK(vcp); - smb_iod_disconnect(vcp); + error = 0; break; } SMB_VC_UNLOCK(vcp); @@ -557,9 +450,22 @@ smb_iod_recvall(struct smb_vc *vcp) if (error) { /* + * The recv. above returned some error + * we can't continue from i.e. ENOTCONN. * It's dangerous to continue here. * (possible infinite loop!) + * + * If we have requests enqueued, next + * state is reconnecting, else idle. */ + int state; + SMB_VC_LOCK(vcp); + state = (vcp->iod_rqlist.tqh_first != NULL) ? + SMBIOD_ST_RECONNECT : SMBIOD_ST_IDLE; + smb_iod_newstate(vcp, state); + cv_broadcast(&vcp->vc_statechg); + SMB_VC_UNLOCK(vcp); + error = 0; break; } @@ -572,7 +478,9 @@ smb_iod_recvall(struct smb_vc *vcp) zprintf(vcp->vc_zoneid, "SMB server %s OK\n", vcp->vc_srvname); - smb_iod_notify_up(vcp); + /* Was: smb_iod_notify_up(vcp); */ + if (fscb && fscb->fscb_up) + smb_vc_walkshares(vcp, fscb->fscb_up); } /* @@ -582,7 +490,7 @@ smb_iod_recvall(struct smb_vc *vcp) */ hp = mtod(m, uchar_t *); /*LINTED*/ - mid = SMB_HDRMID(hp); + mid = letohs(SMB_HDRMID(hp)); SMBIODEBUG("mid %04x\n", (uint_t)mid); rw_enter(&vcp->iod_rqlock, RW_READER); @@ -625,70 +533,31 @@ smb_iod_recvall(struct smb_vc *vcp) rw_exit(&vcp->iod_rqlock); } -#ifdef APPLE - /* - * check for interrupts - * On Solaris, handle in smb_iod_waitrq - */ - rw_enter(&vcp->iod_rqlock, RW_READER); - TAILQ_FOREACH(rqp, &vcp->iod_rqlist, sr_link) { - if (smb_sigintr(rqp->sr_cred->scr_vfsctx)) - smb_iod_rqprocessed(rqp, EINTR, 0); - } - rw_exit(&vcp->iod_rqlock); -#endif + + return (error); } /* - * Looks like we don't need these callbacks, - * but keep the code for now (for Apple). + * The IOD receiver thread has requests pending and + * has not received anything in a while. Try to + * send an SMB echo request. It's tricky to do a + * send from the IOD thread because we can't block. + * + * Using tmo=SMBNOREPLYWAIT in the request + * so smb_rq_reply will skip smb_iod_waitrq. + * The smb_smb_echo call uses SMBR_INTERNAL + * to avoid calling smb_iod_sendall(). */ -/*ARGSUSED*/ -void -smb_iod_notify_down(struct smb_vc *vcp) -{ -#ifdef NEED_SMBFS_CALLBACKS - struct smb_connobj *co; - - if (fscb == NULL) - return; - - /* - * Walk the share list, notify... - * Was: smbfs_down(...share->ss_mount); - * XXX: Ok to hold vc_lock here? - */ - SMB_VC_LOCK(vcp); - SLIST_FOREACH(co, &(VCTOCP(vcp)->co_children), co_next) { - /* smbfs_down() */ - fscb->fscb_down(CPTOSS(co)); - } - SMB_VC_UNLOCK(vcp); -#endif /* NEED_SMBFS_CALLBACKS */ -} - -/*ARGSUSED*/ -void -smb_iod_notify_up(struct smb_vc *vcp) +int +smb_iod_send_echo(smb_vc_t *vcp) { -#ifdef NEED_SMBFS_CALLBACKS - struct smb_connobj *co; - - if (fscb == NULL) - return; + smb_cred_t scred; + int err; - /* - * Walk the share list, notify... - * Was: smbfs_up(...share->ss_mount); - * XXX: Ok to hold vc_lock here? - */ - SMB_VC_LOCK(vcp); - SLIST_FOREACH(co, &(VCTOCP(vcp)->co_children), co_next) { - /* smbfs_up() */ - fscb->fscb_up(CPTOSS(co)); - } - SMB_VC_UNLOCK(vcp); -#endif /* NEED_SMBFS_CALLBACKS */ + smb_credinit(&scred, NULL); + err = smb_smb_echo(vcp, &scred, SMBNOREPLYWAIT); + smb_credrele(&scred); + return (err); } /* @@ -706,33 +575,35 @@ smb_iod_addrq(struct smb_rq *rqp) struct smb_vc *vcp = rqp->sr_vc; int error, save_newrq; - SMBIODEBUG("entry, mid=%d\n", rqp->sr_mid); - ASSERT(rqp->sr_cred); - /* This helps a little with debugging. */ + /* + * State should be correct after the check in + * smb_rq_enqueue(), but we dropped locks... + */ + if (vcp->vc_state != SMBIOD_ST_VCACTIVE) { + SMBIODEBUG("bad vc_state=%d\n", vcp->vc_state); + return (ENOTCONN); + } + + /* + * Requests from the IOD itself are marked _INTERNAL, + * and get some special treatment to avoid blocking + * the reader thread (so we don't deadlock). + * The request is not yet on the queue, so we can + * modify it's state here without locks. + * Only thing using this now is ECHO. + */ rqp->sr_owner = curthread; + if (rqp->sr_owner == vcp->iod_thr) { + rqp->sr_flags |= SMBR_INTERNAL; - if (rqp->sr_flags & SMBR_INTERNAL) { /* - * This is some kind of internal request, - * i.e. negotiate, session setup, echo... - * Allow vc_state < SMBIOD_ST_VCACTIVE, and - * always send directly from this thread. - * May be called by the IOD thread (echo). + * This is a request from the IOD thread. + * Always send directly from this thread. * Note lock order: iod_rqlist, vc_sendlock */ rw_enter(&vcp->iod_rqlock, RW_WRITER); - if (rqp->sr_rqflags2 & SMB_FLAGS2_SECURITY_SIGNATURE) { - /* - * We're signing requests and verifying - * signatures on responses. Set the - * sequence numbers of the request and - * response here, used in smb_rq_verify. - */ - rqp->sr_seqno = vcp->vc_seqno++; - rqp->sr_rseqno = vcp->vc_seqno++; - } TAILQ_INSERT_HEAD(&vcp->iod_rqlist, rqp, sr_link); rw_downgrade(&vcp->iod_rqlock); @@ -741,52 +612,31 @@ smb_iod_addrq(struct smb_rq *rqp) * so take that here, but carefully: * Never block the IOD thread here. */ - if (curthread == vcp->iod_thr) { - if (sema_tryp(&vcp->vc_sendlock) == 0) { - SMBIODEBUG("sendlock busy\n"); - error = EAGAIN; - } else { - /* Have vc_sendlock */ - error = smb_iod_sendrq(rqp); - sema_v(&vcp->vc_sendlock); - } + if (sema_tryp(&vcp->vc_sendlock) == 0) { + SMBIODEBUG("sendlock busy\n"); + error = EAGAIN; } else { - sema_p(&vcp->vc_sendlock); + /* Have vc_sendlock */ error = smb_iod_sendrq(rqp); sema_v(&vcp->vc_sendlock); } rw_exit(&vcp->iod_rqlock); + + /* + * In the non-error case, _removerq + * is done by either smb_rq_reply + * or smb_iod_waitrq. + */ if (error) smb_iod_removerq(rqp); return (error); } - /* - * Normal request from the driver or smbfs. - * State should be correct after the check in - * smb_rq_enqueue(), but we dropped locks... - */ - if (vcp->vc_state != SMBIOD_ST_VCACTIVE) { - SMBIODEBUG("bad vc_state=%d\n", vcp->vc_state); - return (ENOTCONN); - } - rw_enter(&vcp->iod_rqlock, RW_WRITER); - if (rqp->sr_rqflags2 & SMB_FLAGS2_SECURITY_SIGNATURE) { - /* - * We're signing requests and verifying - * signatures on responses. Set the - * sequence numbers of the request and - * response here, used in smb_rq_verify. - */ - rqp->sr_seqno = vcp->vc_seqno++; - rqp->sr_rseqno = vcp->vc_seqno++; - } TAILQ_INSERT_TAIL(&vcp->iod_rqlist, rqp, sr_link); - /* iod_rqlock/WRITER protects iod_newrq */ save_newrq = vcp->iod_newrq; vcp->iod_newrq++; @@ -855,8 +705,6 @@ smb_iod_removerq(struct smb_rq *rqp) { struct smb_vc *vcp = rqp->sr_vc; - SMBIODEBUG("entry, mid=%d\n", rqp->sr_mid); - rw_enter(&vcp->iod_rqlock, RW_WRITER); #ifdef QUEUEDEBUG /* @@ -873,111 +721,10 @@ smb_iod_removerq(struct smb_rq *rqp) } -/* - * Internal version of smb_iod_waitrq. - * - * This is used when there is no reader thread, - * so we have to do the recv here. The request - * must have the SMBR_INTERNAL flag set. - */ -static int -smb_iod_waitrq_internal(struct smb_rq *rqp) -{ - struct smb_vc *vcp = rqp->sr_vc; - mblk_t *m; - uchar_t *hp; - int error; - uint16_t mid; - uint8_t cmd; - - /* Make sure it's an internal request. */ - if ((rqp->sr_flags & SMBR_INTERNAL) == 0) { - SMBIODEBUG("not internal\n"); - return (EINVAL); - } - - /* Only simple requests allowed. */ - if (rqp->sr_flags & SMBR_MULTIPACKET) { - SMBIODEBUG("multipacket\n"); - return (EINVAL); - } - - /* Should not already have a response. */ - if (rqp->sr_rp.md_top) { - DEBUG_ENTER("smb_iod_waitrq again?\n"); - return (0); - } - - /* - * The message recv loop. Terminates when we - * receive the message we're looking for. - * Drop others, with complaints. - * Scaled-down version of smb_iod_recvall - */ - for (;;) { - m = NULL; - error = smb_iod_recv1(vcp, &m); - if (error) { - /* - * It's dangerous to continue here. - * (possible infinite loop!) - */ -#if 0 - if (SMB_TRAN_FATAL(vcp, error)) { - return (error); - } - continue; -#endif - return (error); - } - - hp = mtod(m, uchar_t *); - cmd = SMB_HDRCMD(hp); - /*LINTED*/ - mid = SMB_HDRMID(hp); - - SMBIODEBUG("cmd 0x%02x mid %04x\n", - (uint_t)cmd, (uint_t)mid); - m_dumpm(m); - - /* - * Normally, the MID will match. - * For internal requests, also - * match on the cmd to be safe. - */ - if (mid == rqp->sr_mid) - break; - if (cmd == rqp->sr_cmd) { - SMBIODEBUG("cmd match but not mid!\n"); - break; - } - - SMBIODEBUG("drop nomatch\n"); - m_freem(m); - } - - /* - * Have the response we were waiting for. - * Simplified version of the code from - * smb_iod_recvall - */ - SMBRQ_LOCK(rqp); - if (rqp->sr_rp.md_top == NULL) { - md_initm(&rqp->sr_rp, m); - } else { - SMBIODEBUG("drop duplicate\n"); - m_freem(m); - } - SMBRQ_UNLOCK(rqp); - - return (0); -} - /* * Wait for a request to complete. * - * For internal requests, see smb_iod_waitrq_internal. * For normal requests, we need to deal with * ioc_muxcnt dropping below vc_maxmux by * making arrangements to send more... @@ -989,23 +736,18 @@ smb_iod_waitrq(struct smb_rq *rqp) clock_t tr, tmo1, tmo2; int error, rc; - SMBIODEBUG("entry, cmd=0x%02x mid=0x%04x\n", - (uint_t)rqp->sr_cmd, (uint_t)rqp->sr_mid); - if (rqp->sr_flags & SMBR_INTERNAL) { ASSERT((rqp->sr_flags & SMBR_MULTIPACKET) == 0); - error = smb_iod_waitrq_internal(rqp); smb_iod_removerq(rqp); - return (error); + return (EAGAIN); } /* * Make sure this is NOT the IOD thread, - * or the wait below will always timeout. + * or the wait below will stop the reader. */ ASSERT(curthread != vcp->iod_thr); - atomic_inc_uint(&vcp->iod_rqwaiting); SMBRQ_LOCK(rqp); /* @@ -1031,7 +773,7 @@ smb_iod_waitrq(struct smb_rq *rqp) rc = cv_wait_sig(&rqp->sr_cond, &rqp->sr_lock); rqp->sr_flags &= ~SMBR_SENDWAIT; if (rc == 0) { - SMBIODEBUG("EINTR in sendwait, mid=%u\n", rqp->sr_mid); + SMBIODEBUG("EINTR in sendwait, rqp=%p\n", rqp); error = EINTR; goto out; } @@ -1120,7 +862,6 @@ smb_iod_waitrq(struct smb_rq *rqp) out: SMBRQ_UNLOCK(rqp); - atomic_dec_uint(&vcp->iod_rqwaiting); /* * MULTIPACKET request must stay in the list. @@ -1168,8 +909,8 @@ smb_iod_shutdown_share(struct smb_share *ssp) * Send all requests that need sending. * Called from _addrq, _multirq, _waitrq */ -static void -smb_iod_sendall(struct smb_vc *vcp) +void +smb_iod_sendall(smb_vc_t *vcp) { struct smb_rq *rqp; int error, save_newrq, muxcnt; @@ -1208,7 +949,7 @@ smb_iod_sendall(struct smb_vc *vcp) error = muxcnt = 0; TAILQ_FOREACH(rqp, &vcp->iod_rqlist, sr_link) { - if (vcp->vc_state == SMBIOD_ST_DEAD) { + if (vcp->vc_state != SMBIOD_ST_VCACTIVE) { error = ENOTCONN; /* stop everything! */ break; } @@ -1236,167 +977,204 @@ smb_iod_sendall(struct smb_vc *vcp) sema_v(&vcp->vc_sendlock); rw_exit(&vcp->iod_rqlock); - - if (error == ENOTCONN) - smb_iod_dead(vcp); - } - -/* - * "main" function for smbiod daemon thread - */ -void -smb_iod_main(struct smb_vc *vcp) +int +smb_iod_vc_work(struct smb_vc *vcp, cred_t *cr) { - kthread_t *thr = curthread; + struct file *fp = NULL; + int err = 0; - SMBIODEBUG("entry\n"); + /* + * This is called by the one-and-only + * IOD thread for this VC. + */ + ASSERT(vcp->iod_thr == curthread); - SMBIODEBUG("Running, thr=0x%p\n", thr); + /* + * Get the network transport file pointer, + * and "loan" it to our transport module. + */ + if ((fp = getf(vcp->vc_tran_fd)) == NULL) { + err = EBADF; + goto out; + } + if ((err = SMB_TRAN_LOAN_FP(vcp, fp, cr)) != 0) + goto out; /* - * Prevent race with thread that created us. - * After we get this lock iod_thr is set. + * In case of reconnect, tell any enqueued requests + * then can GO! */ SMB_VC_LOCK(vcp); - ASSERT(thr == vcp->iod_thr); - - /* Redundant with iod_thr, but may help debugging. */ - vcp->iod_flags |= SMBIOD_RUNNING; + vcp->vc_genid++; /* possibly new connection */ + smb_iod_newstate(vcp, SMBIOD_ST_VCACTIVE); + cv_broadcast(&vcp->vc_statechg); SMB_VC_UNLOCK(vcp); /* - * OK, this is a new reader thread. - * In case of reconnect, tell any - * old requests they can restart. + * The above cv_broadcast should be sufficient to + * get requests going again. + * + * If we have a callback function, run it. + * Was: smb_iod_notify_connected() */ - smb_iod_invrq(vcp); + if (fscb && fscb->fscb_connect) + smb_vc_walkshares(vcp, fscb->fscb_connect); /* * Run the "reader" loop. */ - smb_iod_recvall(vcp); + err = smb_iod_recvall(vcp); /* - * The reader loop function returns only when - * there's been a fatal error on the connection. + * The reader loop returned, so we must have a + * new state. (disconnected or reconnecting) + * + * Notify shares of the disconnect. + * Was: smb_iod_notify_disconnect() */ - smb_iod_dead(vcp); + smb_vc_walkshares(vcp, smb_iod_share_disconnected); /* - * The reader thread is going away. Clear iod_thr, - * and wake up anybody waiting for us to quit. + * The reader loop function returns only when + * there's been an error on the connection, or + * this VC has no more references. It also + * updates the state before it returns. + * + * Tell any requests to give up or restart. */ - SMB_VC_LOCK(vcp); - vcp->iod_flags &= ~SMBIOD_RUNNING; - vcp->iod_thr = NULL; - cv_broadcast(&vcp->iod_exit); - SMB_VC_UNLOCK(vcp); + smb_iod_invrq(vcp); + +out: + /* Recall the file descriptor loan. */ + (void) SMB_TRAN_LOAN_FP(vcp, NULL, cr); + if (fp != NULL) { + releasef(vcp->vc_tran_fd); + } + + return (err); +} + +/* + * Wait around for someone to ask to use this VC. + * If the VC has only the IOD reference, then + * wait only a minute or so, then drop it. + */ +int +smb_iod_vc_idle(struct smb_vc *vcp) +{ + clock_t tr, tmo; + int err = 0; /* - * This hold was taken in smb_iod_create() - * when this thread was created. + * This is called by the one-and-only + * IOD thread for this VC. */ - smb_vc_rele(vcp); + ASSERT(vcp->iod_thr == curthread); + + SMB_VC_LOCK(vcp); + while (vcp->vc_state == SMBIOD_ST_IDLE) { + tmo = lbolt + SEC_TO_TICK(15); + tr = cv_timedwait_sig(&vcp->iod_idle, &vcp->vc_lock, tmo); + if (tr == 0) { + err = EINTR; + break; + } + if (tr < 0) { + /* timeout */ + if (vcp->vc_co.co_usecount == 1) { + /* Let this IOD terminate. */ + smb_iod_newstate(vcp, SMBIOD_ST_DEAD); + /* nobody to cv_broadcast */ + break; + } + } + } + SMB_VC_UNLOCK(vcp); - SMBIODEBUG("Exiting, p=0x%p\n", curproc); - zthread_exit(); + return (err); } /* - * Create the reader thread. - * - * This happens when we are just about to - * enter vc_state = SMBIOD_ST_VCACTIVE; - * See smb_sm_ssnsetup() + * After a failed reconnect attempt, smbiod will + * call this to make current requests error out. */ int -smb_iod_create(struct smb_vc *vcp) +smb_iod_vc_rcfail(struct smb_vc *vcp) { - kthread_t *thr = NULL; - int error; + clock_t tr, tmo; + int err = 0; /* - * Take a hold on the VC for the IOD thread. - * This hold will be released when the IOD - * thread terminates. (or on error below) + * This is called by the one-and-only + * IOD thread for this VC. */ - smb_vc_hold(vcp); + ASSERT(vcp->iod_thr == curthread); + + if (vcp->vc_state != SMBIOD_ST_RECONNECT) + return (EINVAL); SMB_VC_LOCK(vcp); - if (vcp->iod_thr != NULL) { - SMBIODEBUG("aready have an IOD?"); - error = EIO; - goto out; - } + smb_iod_newstate(vcp, SMBIOD_ST_RCFAILED); + cv_broadcast(&vcp->vc_statechg); /* - * Darwin code used: IOCreateThread(...) - * In Solaris, we use... + * Short wait here for two reasons: + * (1) Give requests a chance to error out. + * (2) Prevent immediate retry. */ - thr = zthread_create( - NULL, /* stack */ - 0, /* stack size (default) */ - smb_iod_main, /* entry func... */ - vcp, /* ... and arg */ - 0, /* len (of what?) */ - minclsyspri); /* priority */ - if (thr == NULL) { - SMBERROR("can't start smbiod\n"); - error = ENOMEM; - goto out; - } + tmo = lbolt + SEC_TO_TICK(5); + tr = cv_timedwait_sig(&vcp->iod_idle, &vcp->vc_lock, tmo); + if (tr == 0) + err = EINTR; - /* Success! */ - error = 0; - vcp->iod_thr = thr; + smb_iod_newstate(vcp, SMBIOD_ST_IDLE); + cv_broadcast(&vcp->vc_statechg); -out: SMB_VC_UNLOCK(vcp); - if (error) - smb_vc_rele(vcp); - - return (error); + return (err); } /* - * Called from smb_vc_free to do any - * cleanup of our IOD (reader) thread. + * Ask the IOD to reconnect (if not already underway) + * then wait for the reconnect to finish. */ int -smb_iod_destroy(struct smb_vc *vcp) +smb_iod_reconnect(struct smb_vc *vcp) { - clock_t tmo; + int err = 0, rv; - /* - * Let's try to make sure the IOD thread - * goes away, by waiting for it to exit. - * Normally, it's gone by now. - * - * Only wait for a second, because we're in the - * teardown path and don't want to get stuck here. - * Should not take long, or things are hosed... - */ SMB_VC_LOCK(vcp); - if (vcp->iod_thr) { - vcp->iod_flags |= SMBIOD_SHUTDOWN; - tmo = lbolt + hz; - tmo = cv_timedwait(&vcp->iod_exit, &vcp->vc_lock, tmo); - if (tmo == -1) { - SMBERROR("IOD thread for %s did not exit?\n", - vcp->vc_srvname); +again: + switch (vcp->vc_state) { + + case SMBIOD_ST_IDLE: + smb_iod_newstate(vcp, SMBIOD_ST_RECONNECT); + cv_signal(&vcp->iod_idle); + /* FALLTHROUGH */ + + case SMBIOD_ST_RECONNECT: + rv = cv_wait_sig(&vcp->vc_statechg, &vcp->vc_lock); + if (rv == 0) { + err = EINTR; + break; } + goto again; + + case SMBIOD_ST_VCACTIVE: + err = 0; /* success! */ + break; + + case SMBIOD_ST_RCFAILED: + case SMBIOD_ST_DEAD: + default: + err = ENOTCONN; + break; } - if (vcp->iod_thr) { - /* This should not happen. */ - SMBIODEBUG("IOD thread did not exit!\n"); - /* Try harder? */ - tsignal(vcp->iod_thr, SIGKILL); - } - SMB_VC_UNLOCK(vcp); - return (0); + SMB_VC_UNLOCK(vcp); + return (err); } diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/smb_pass.c b/usr/src/uts/common/fs/smbclnt/netsmb/smb_pass.c index c78c0e61b1..4079482538 100644 --- a/usr/src/uts/common/fs/smbclnt/netsmb/smb_pass.c +++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_pass.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" - /* * Password Keychain storage mechanism. */ @@ -57,6 +55,7 @@ #include <sys/mkdev.h> #include <sys/avl.h> #include <sys/avl_impl.h> +#include <sys/u8_textprep.h> #include <netsmb/smb_osdep.h> @@ -97,6 +96,9 @@ avl_tree_t smb_ptd; /* AVL password tree descriptor */ unsigned int smb_list_len = 0; /* No. of elements in the tree. */ kmutex_t smb_ptd_lock; /* Mutex lock for controlled access */ +int smb_pkey_check(smbioc_pk_t *pk, cred_t *cr); +int smb_pkey_deluid(uid_t ioc_uid, cred_t *cr); + /* * This routine is called by AVL tree calls when they want to find a * node, find the next position in the tree to add or for deletion. @@ -108,7 +110,7 @@ smb_pkey_cmp(const void *a, const void *b) { const smb_passid_t *pa = (smb_passid_t *)a; const smb_passid_t *pb = (smb_passid_t *)b; - int duser, dsrv; + int duser, dsrv, error; ASSERT(MUTEX_HELD(&smb_ptd_lock)); @@ -128,12 +130,14 @@ smb_pkey_cmp(const void *a, const void *b) return (-1); if (pa->zoneid > pb->zoneid) return (+1); - dsrv = strcasecmp(pa->srvdom, pb->srvdom); + dsrv = u8_strcmp(pa->srvdom, pb->srvdom, 0, + U8_STRCMP_CI_LOWER, U8_UNICODE_LATEST, &error); if (dsrv < 0) return (-1); if (dsrv > 0) return (+1); - duser = strcasecmp(pa->username, pb->username); + duser = u8_strcmp(pa->username, pb->username, 0, + U8_STRCMP_CI_LOWER, U8_UNICODE_LATEST, &error); if (duser < 0) return (-1); if (duser > 0) @@ -157,11 +161,12 @@ smb_pkey_init() /* * Destroy the full AVL tree. + * Called just before unload. */ void smb_pkey_fini() { - smb_pkey_deluid((uid_t)-1, CRED()); + smb_pkey_deluid((uid_t)-1, kcred); avl_destroy(&smb_ptd); mutex_destroy(&smb_ptd_lock); } @@ -187,8 +192,8 @@ smb_node_delete(smb_passid_t *tmp) { ASSERT(MUTEX_HELD(&smb_ptd_lock)); avl_remove(&smb_ptd, tmp); - smb_strfree(tmp->srvdom); - smb_strfree(tmp->username); + strfree(tmp->srvdom); + strfree(tmp->username); kmem_free(tmp, sizeof (*tmp)); return (0); } @@ -287,10 +292,10 @@ smb_pkey_add(smbioc_pk_t *pk, cred_t *cr) cpid = kmem_zalloc(sizeof (smb_passid_t), KM_SLEEP); cpid->uid = uid; cpid->zoneid = getzoneid(); - cpid->srvdom = smb_strdup(pk->pk_dom); - cpid->username = smb_strdup(pk->pk_usr); - smb_oldlm_hash(pk->pk_pass, cpid->lmhash); - smb_ntlmv1hash(pk->pk_pass, cpid->nthash); + cpid->srvdom = strdup(pk->pk_dom); + cpid->username = strdup(pk->pk_usr); + bcopy(pk->pk_lmhash, cpid->lmhash, SMBIOC_HASH_SZ); + bcopy(pk->pk_nthash, cpid->nthash, SMBIOC_HASH_SZ); /* * XXX: Instead of calling smb_pkey_check here, @@ -309,8 +314,8 @@ smb_pkey_add(smbioc_pk_t *pk, cred_t *cr) if (tmp == NULL) { avl_insert(t, cpid, where); } else { - smb_strfree(cpid->srvdom); - smb_strfree(cpid->username); + strfree(cpid->srvdom); + strfree(cpid->username); kmem_free(cpid, sizeof (smb_passid_t)); } mutex_exit(&smb_ptd_lock); @@ -320,7 +325,7 @@ smb_pkey_add(smbioc_pk_t *pk, cred_t *cr) /* * Determine if a node with uid,zoneid, uname & dname exists in the tree - * given the information. Does NOT return the stored password. + * given the information, and if found, return the hashes. */ int smb_pkey_check(smbioc_pk_t *pk, cred_t *cr) @@ -346,46 +351,74 @@ smb_pkey_check(smbioc_pk_t *pk, cred_t *cr) mutex_enter(&smb_ptd_lock); tmp = (smb_passid_t *)avl_find(t, cpid, &where); - if (tmp != NULL) - error = 0; mutex_exit(&smb_ptd_lock); + if (tmp != NULL) { + bcopy(tmp->lmhash, pk->pk_lmhash, SMBIOC_HASH_SZ); + bcopy(tmp->nthash, pk->pk_nthash, SMBIOC_HASH_SZ); + error = 0; + } + kmem_free(cpid, sizeof (smb_passid_t)); return (error); } -/* - * Interface function between the keychain mechanism and SMB password - * handling during Session Setup. Internal form of smb_pkey_check(). - * Copies the password hashes into the VC. - */ + int -smb_pkey_getpwh(struct smb_vc *vcp, cred_t *cr) +smb_pkey_ioctl(int cmd, intptr_t arg, int flags, cred_t *cr) { - avl_tree_t *t = &smb_ptd; - avl_index_t where; - smb_passid_t *tmp, *cpid; - int error = ENOENT; + smbioc_pk_t *pk; + uid_t uid; + int err = 0; + + pk = kmem_alloc(sizeof (*pk), KM_SLEEP); + + switch (cmd) { + case SMBIOC_PK_ADD: + case SMBIOC_PK_DEL: + case SMBIOC_PK_CHK: + if (ddi_copyin((void *)arg, pk, + sizeof (*pk), flags)) { + err = EFAULT; + goto out; + } + /* Make strlen (etc) on these safe. */ + pk->pk_dom[SMBIOC_MAX_NAME-1] = '\0'; + pk->pk_usr[SMBIOC_MAX_NAME-1] = '\0'; + break; + } - cpid = kmem_alloc(sizeof (smb_passid_t), KM_SLEEP); - cpid->uid = crgetruid(cr); - cpid->zoneid = getzoneid(); - cpid->username = vcp->vc_username; + switch (cmd) { + case SMBIOC_PK_ADD: + err = smb_pkey_add(pk, cr); + break; - if (vcp->vc_vopt & SMBVOPT_KC_DOMAIN) - cpid->srvdom = vcp->vc_domain; - else - cpid->srvdom = vcp->vc_srvname; + case SMBIOC_PK_DEL: + err = smb_pkey_del(pk, cr); + break; - mutex_enter(&smb_ptd_lock); - tmp = (smb_passid_t *)avl_find(t, cpid, &where); - if (tmp != NULL) { - bcopy(tmp->lmhash, vcp->vc_lmhash, SMB_PWH_MAX); - bcopy(tmp->nthash, vcp->vc_nthash, SMB_PWH_MAX); - error = 0; + case SMBIOC_PK_CHK: + err = smb_pkey_check(pk, cr); + /* This is just a hash now. */ + (void) ddi_copyout(pk, (void *)arg, + sizeof (*pk), flags); + break; + + case SMBIOC_PK_DEL_OWNER: + uid = crgetruid(cr); + err = smb_pkey_deluid(uid, cr); + break; + + case SMBIOC_PK_DEL_EVERYONE: + uid = (uid_t)-1; + err = smb_pkey_deluid(uid, cr); + break; + + default: + err = ENODEV; } - mutex_exit(&smb_ptd_lock); - kmem_free(cpid, sizeof (smb_passid_t)); - return (error); +out: + kmem_free(pk, sizeof (*pk)); + return (err); } diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/smb_pass.h b/usr/src/uts/common/fs/smbclnt/netsmb/smb_pass.h index d4147798ba..f4ebf9a573 100644 --- a/usr/src/uts/common/fs/smbclnt/netsmb/smb_pass.h +++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_pass.h @@ -20,15 +20,13 @@ */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _SMB_PASS_H #define _SMB_PASS_H -#pragma ident "%Z%%M% %I% %E% SMI" - /* * Password keychains interface */ @@ -46,21 +44,14 @@ typedef struct smb_passid { zoneid_t zoneid; /* Future Use */ char *srvdom; /* Windows Domain (or server) */ char *username; /* Windows User name */ - uchar_t lmhash[SMB_PWH_MAX]; - uchar_t nthash[SMB_PWH_MAX]; + uchar_t lmhash[SMBIOC_HASH_SZ]; + uchar_t nthash[SMBIOC_HASH_SZ]; } smb_passid_t; /* Called from smb_dev.c */ void smb_pkey_init(void); void smb_pkey_fini(void); int smb_pkey_idle(void); -int smb_pkey_add(smbioc_pk_t *pk, cred_t *cr); -int smb_pkey_del(smbioc_pk_t *pk, cred_t *cr); -int smb_pkey_check(smbioc_pk_t *pk, cred_t *cr); -int smb_pkey_deluid(uid_t uid, cred_t *cr); - -/* Called from smb_usr.c */ -struct smb_vc; -int smb_pkey_getpwh(struct smb_vc *vcp, cred_t *cr); +int smb_pkey_ioctl(int, intptr_t, int, cred_t *); #endif /* _SMB_PASS_H */ diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/smb_rq.c b/usr/src/uts/common/fs/smbclnt/netsmb/smb_rq.c index e73b159e1b..5199127d47 100644 --- a/usr/src/uts/common/fs/smbclnt/netsmb/smb_rq.c +++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_rq.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. */ @@ -44,6 +44,7 @@ #include <sys/lock.h> #include <sys/socket.h> #include <sys/mount.h> +#include <sys/sunddi.h> #include <sys/cmn_err.h> #include <sys/sdt.h> @@ -55,6 +56,18 @@ #include <netsmb/smb_tran.h> #include <netsmb/smb_rq.h> +/* + * How long to wait before restarting a request (after reconnect) + */ +#define SMB_RCNDELAY 2 /* seconds */ + +/* + * leave this zero - we can't ssecond guess server side effects of + * duplicate ops, this isn't nfs! + */ +#define SMBMAXRESTARTS 0 + + static int smb_rq_reply(struct smb_rq *rqp); static int smb_rq_enqueue(struct smb_rq *rqp); static int smb_rq_getenv(struct smb_connobj *layer, @@ -64,6 +77,27 @@ static int smb_t2_reply(struct smb_t2rq *t2p); static int smb_nt_reply(struct smb_ntrq *ntp); +/* + * Done with a request object. Free its contents. + * If it was allocated (SMBR_ALLOCED) free it too. + * Some of these are stack locals, not allocated. + * + * No locks here - this is the last ref. + */ +void +smb_rq_done(struct smb_rq *rqp) +{ + + /* + * No smb_vc_rele() here - see smb_rq_init() + */ + mb_done(&rqp->sr_rq); + md_done(&rqp->sr_rp); + mutex_destroy(&rqp->sr_lock); + cv_destroy(&rqp->sr_cond); + if (rqp->sr_flags & SMBR_ALLOCED) + kmem_free(rqp, sizeof (*rqp)); +} int smb_rq_alloc(struct smb_connobj *layer, uchar_t cmd, struct smb_cred *scred, @@ -85,9 +119,8 @@ smb_rq_alloc(struct smb_connobj *layer, uchar_t cmd, struct smb_cred *scred, return (0); } - int -smb_rq_init(struct smb_rq *rqp, struct smb_connobj *layer, uchar_t cmd, +smb_rq_init(struct smb_rq *rqp, struct smb_connobj *co, uchar_t cmd, struct smb_cred *scred) { int error; @@ -96,78 +129,106 @@ smb_rq_init(struct smb_rq *rqp, struct smb_connobj *layer, uchar_t cmd, mutex_init(&rqp->sr_lock, NULL, MUTEX_DRIVER, NULL); cv_init(&rqp->sr_cond, NULL, CV_DEFAULT, NULL); - error = smb_rq_getenv(layer, &rqp->sr_vc, &rqp->sr_share); + error = smb_rq_getenv(co, &rqp->sr_vc, &rqp->sr_share); if (error) return (error); + /* + * We copied a VC pointer (vcp) into rqp->sr_vc, + * but we do NOT do a smb_vc_hold here. Instead, + * the caller is responsible for the hold on the + * share or the VC as needed. For smbfs callers, + * the hold is on the share, via the smbfs mount. + * For nsmb ioctl callers, the hold is done when + * the driver handle gets VC or share references. + * This design avoids frequent hold/rele activity + * when creating and completing requests. + */ + rqp->sr_rexmit = SMBMAXRESTARTS; - rqp->sr_cred = scred; /* XXX no ref hold */ - rqp->sr_mid = smb_vc_nextmid(rqp->sr_vc); + rqp->sr_cred = scred; /* Note: ref hold done by caller. */ + rqp->sr_pid = (uint16_t)ddi_get_pid(); error = smb_rq_new(rqp, cmd); - if (!error) { - rqp->sr_flags |= SMBR_VCREF; - smb_vc_hold(rqp->sr_vc); - } + return (error); } static int smb_rq_new(struct smb_rq *rqp, uchar_t cmd) { - struct smb_vc *vcp = rqp->sr_vc; struct mbchain *mbp = &rqp->sr_rq; + struct smb_vc *vcp = rqp->sr_vc; int error; - static char tzero[12]; - caddr_t ptr; - pid_t pid; ASSERT(rqp != NULL); - ASSERT(rqp->sr_cred != NULL); - pid = rqp->sr_cred->vc_pid; + rqp->sr_sendcnt = 0; rqp->sr_cmd = cmd; + mb_done(mbp); md_done(&rqp->sr_rp); error = mb_init(mbp); if (error) return (error); - mb_put_mem(mbp, SMB_SIGNATURE, SMB_SIGLEN, MB_MSYSTEM); - mb_put_uint8(mbp, cmd); - mb_put_uint32le(mbp, 0); - rqp->sr_rqflags = vcp->vc_hflags; - mb_put_uint8(mbp, rqp->sr_rqflags); + + /* + * Is this the right place to save the flags? + */ + rqp->sr_rqflags = vcp->vc_hflags; rqp->sr_rqflags2 = vcp->vc_hflags2; - mb_put_uint16le(mbp, rqp->sr_rqflags2); - mb_put_mem(mbp, tzero, 12, MB_MSYSTEM); - ptr = mb_reserve(mbp, sizeof (u_int16_t)); - /*LINTED*/ - ASSERT(ptr == (caddr_t)((u_int16_t *)ptr)); - /*LINTED*/ - rqp->sr_rqtid = (u_int16_t *)ptr; - mb_put_uint16le(mbp, (u_int16_t)(pid)); - ptr = mb_reserve(mbp, sizeof (u_int16_t)); - /*LINTED*/ - ASSERT(ptr == (caddr_t)((u_int16_t *)ptr)); - /*LINTED*/ - rqp->sr_rquid = (u_int16_t *)ptr; - mb_put_uint16le(mbp, rqp->sr_mid); + + /* + * The SMB header is filled in later by + * smb_rq_fillhdr (see below) + * Just reserve space here. + */ + mb_put_mem(mbp, NULL, SMB_HDRLEN, MB_MZERO); + return (0); } +/* + * Given a request with it's body already composed, + * rewind to the start and fill in the SMB header. + * This is called after the request is enqueued, + * so we have the final MID, seq num. etc. + */ void -smb_rq_done(struct smb_rq *rqp) +smb_rq_fillhdr(struct smb_rq *rqp) { - /* No locks. Last ref. here. */ - if (rqp->sr_flags & SMBR_VCREF) { - rqp->sr_flags &= ~SMBR_VCREF; - smb_vc_rele(rqp->sr_vc); - } - mb_done(&rqp->sr_rq); - md_done(&rqp->sr_rp); - mutex_destroy(&rqp->sr_lock); - cv_destroy(&rqp->sr_cond); - if (rqp->sr_flags & SMBR_ALLOCED) - kmem_free(rqp, sizeof (*rqp)); + struct mbchain mbtmp, *mbp = &mbtmp; + mblk_t *m; + + /* + * Fill in the SMB header using a dup of the first mblk, + * which points at the same data but has its own wptr, + * so we can rewind without trashing the message. + */ + m = dupb(rqp->sr_rq.mb_top); + m->b_wptr = m->b_rptr; /* rewind */ + mb_initm(mbp, m); + + mb_put_mem(mbp, SMB_SIGNATURE, 4, MB_MSYSTEM); + mb_put_uint8(mbp, rqp->sr_cmd); + mb_put_uint32le(mbp, 0); /* status */ + mb_put_uint8(mbp, rqp->sr_rqflags); + mb_put_uint16le(mbp, rqp->sr_rqflags2); + mb_put_uint16le(mbp, 0); /* pid-high */ + mb_put_mem(mbp, NULL, 8, MB_MZERO); /* MAC sig. (later) */ + mb_put_uint16le(mbp, 0); /* reserved */ + mb_put_uint16le(mbp, rqp->sr_rqtid); + mb_put_uint16le(mbp, rqp->sr_pid); + mb_put_uint16le(mbp, rqp->sr_rquid); + mb_put_uint16le(mbp, rqp->sr_mid); + + /* This will free the mblk from dupb. */ + mb_done(mbp); +} + +int +smb_rq_simple(struct smb_rq *rqp) +{ + return (smb_rq_simple_timed(rqp, smb_timo_default)); } /* @@ -199,7 +260,7 @@ smb_rq_simple_timed(struct smb_rq *rqp, int timeout) if (rqp->sr_rexmit <= 0) break; SMBRQ_LOCK(rqp); - if (rqp->sr_share && rqp->sr_share->ss_mount) { + if (rqp->sr_share) { cv_timedwait(&rqp->sr_cond, &(rqp)->sr_lock, lbolt + (hz * SMB_RCNDELAY)); @@ -216,12 +277,6 @@ smb_rq_simple_timed(struct smb_rq *rqp, int timeout) } -int -smb_rq_simple(struct smb_rq *rqp) -{ - return (smb_rq_simple_timed(rqp, smb_timo_default)); -} - static int smb_rq_enqueue(struct smb_rq *rqp) { @@ -230,56 +285,50 @@ smb_rq_enqueue(struct smb_rq *rqp) int error = 0; /* - * Unfortunate special case needed for - * tree disconnect, which needs sr_share - * but should skip the reconnect check. + * Normal requests may initiate a reconnect, + * and/or wait for state changes to finish. + * Some requests set the NORECONNECT flag + * to avoid all that (i.e. tree discon) */ - if (rqp->sr_cmd == SMB_COM_TREE_DISCONNECT) - ssp = NULL; - - /* - * If this is an "internal" request, bypass any - * wait for connection state changes, etc. - * This request is making those changes. - */ - if (rqp->sr_flags & SMBR_INTERNAL) { - ASSERT(ssp == NULL); - goto just_doit; + if (rqp->sr_flags & SMBR_NORECONNECT) { + if (vcp->vc_state != SMBIOD_ST_VCACTIVE) { + SMBSDEBUG("bad vc_state=%d\n", vcp->vc_state); + return (ENOTCONN); + } + if (ssp != NULL && + ((ssp->ss_flags & SMBS_CONNECTED) == 0)) + return (ENOTCONN); + goto ok_out; } /* - * Wait for VC reconnect to finish... - * XXX: Deal with reconnect later. - * Just bail out for now. - * - * MacOS might check vfs_isforce() here. + * If we're not connected, initiate a reconnect + * and/or wait for an existing one to finish. */ if (vcp->vc_state != SMBIOD_ST_VCACTIVE) { - SMBSDEBUG("bad vc_state=%d\n", vcp->vc_state); - return (ENOTCONN); + error = smb_iod_reconnect(vcp); + if (error != 0) + return (error); } /* - * If this request has a "share" object: - * 1: Deny access if share is _GONE (unmounted) - * 2: Wait for state changes in that object, - * Initiate share (re)connect if needed. - * XXX: Not really doing 2 yet. + * If this request has a "share" object + * that needs a tree connect, do it now. */ - if (ssp) { - if (ssp->ss_flags & SMBS_GONE) - return (ENOTCONN); - SMB_SS_LOCK(ssp); - if (!smb_share_valid(ssp)) { - error = smb_share_tcon(ssp); - } - SMB_SS_UNLOCK(ssp); + if (ssp != NULL && (ssp->ss_flags & SMBS_CONNECTED) == 0) { + error = smb_share_tcon(ssp, rqp->sr_cred); + if (error) + return (error); } - if (!error) { - just_doit: - error = smb_iod_addrq(rqp); - } + /* + * We now know what UID + TID to use. + * Store them in the request. + */ +ok_out: + rqp->sr_rquid = vcp->vc_smbuid; + rqp->sr_rqtid = ssp ? ssp->ss_tid : SMB_TID_UNKNOWN; + error = smb_iod_addrq(rqp); return (error); } @@ -358,23 +407,6 @@ smb_rq_intr(struct smb_rq *rqp) return (EINTR); return (0); -#ifdef APPLE - return (smb_sigintr(rqp->sr_cred->scr_vfsctx)); -#endif -} - -int -smb_rq_getrequest(struct smb_rq *rqp, struct mbchain **mbpp) -{ - *mbpp = &rqp->sr_rq; - return (0); -} - -int -smb_rq_getreply(struct smb_rq *rqp, struct mdchain **mbpp) -{ - *mbpp = &rqp->sr_rp; - return (0); } static int @@ -383,7 +415,7 @@ smb_rq_getenv(struct smb_connobj *co, { struct smb_vc *vcp = NULL; struct smb_share *ssp = NULL; - int error = 0; + int error = EINVAL; if (co->co_flags & SMBO_GONE) { SMBSDEBUG("zombie CO\n"); @@ -392,27 +424,28 @@ smb_rq_getenv(struct smb_connobj *co, } switch (co->co_level) { + case SMBL_SHARE: + ssp = CPTOSS(co); + if ((co->co_flags & SMBO_GONE) || + co->co_parent == NULL) { + SMBSDEBUG("zombie share %s\n", ssp->ss_name); + break; + } + /* instead of recursion... */ + co = co->co_parent; + /* FALLTHROUGH */ case SMBL_VC: vcp = CPTOVC(co); - if (co->co_parent == NULL) { + if ((co->co_flags & SMBO_GONE) || + co->co_parent == NULL) { SMBSDEBUG("zombie VC %s\n", vcp->vc_srvname); - error = EINVAL; break; } + error = 0; break; - case SMBL_SHARE: - ssp = CPTOSS(co); - if (co->co_parent == NULL) { - SMBSDEBUG("zombie share %s\n", ssp->ss_name); - error = EINVAL; - break; - } - error = smb_rq_getenv(co->co_parent, &vcp, NULL); - break; default: SMBSDEBUG("invalid level %d passed\n", co->co_level); - error = EINVAL; } out: @@ -433,7 +466,6 @@ static int smb_rq_reply(struct smb_rq *rqp) { struct mdchain *mdp = &rqp->sr_rp; - u_int32_t tdw; u_int8_t tb; int error, rperror = 0; @@ -457,7 +489,7 @@ smb_rq_reply(struct smb_rq *rqp) /* * Parse the SMB header */ - error = md_get_uint32(mdp, &tdw); + error = md_get_uint32le(mdp, NULL); if (error) return (error); error = md_get_uint8(mdp, &tb); @@ -493,9 +525,9 @@ smb_rq_reply(struct smb_rq *rqp) } else rqp->sr_flags &= ~SMBR_MOREDATA; - error = md_get_uint32(mdp, &tdw); - error = md_get_uint32(mdp, &tdw); - error = md_get_uint32(mdp, &tdw); + error = md_get_uint32le(mdp, NULL); + error = md_get_uint32le(mdp, NULL); + error = md_get_uint32le(mdp, NULL); error = md_get_uint16le(mdp, &rqp->sr_rptid); error = md_get_uint16le(mdp, &rqp->sr_rppid); @@ -528,8 +560,6 @@ smb_t2_alloc(struct smb_connobj *layer, ushort_t setup, struct smb_cred *scred, if (t2p == NULL) return (ENOMEM); error = smb_t2_init(t2p, layer, &setup, 1, scred); - mutex_init(&t2p->t2_lock, NULL, MUTEX_DRIVER, NULL); - cv_init(&t2p->t2_cond, NULL, CV_DEFAULT, NULL); t2p->t2_flags |= SMBT2_ALLOCED; if (error) { smb_t2_done(t2p); @@ -569,6 +599,9 @@ smb_t2_init(struct smb_t2rq *t2p, struct smb_connobj *source, ushort_t *setup, int error; bzero(t2p, sizeof (*t2p)); + mutex_init(&t2p->t2_lock, NULL, MUTEX_DRIVER, NULL); + cv_init(&t2p->t2_cond, NULL, CV_DEFAULT, NULL); + t2p->t2_source = source; t2p->t2_setupcount = (u_int16_t)setupcnt; t2p->t2_setupdata = t2p->t2_setup; @@ -733,7 +766,7 @@ smb_t2_reply(struct smb_t2rq *t2p) md_get_uint8(mdp, NULL); /* Reserved2 */ tmp = wc; while (tmp--) - md_get_uint16(mdp, NULL); + md_get_uint16le(mdp, NULL); if ((error2 = md_get_uint16le(mdp, &bc)) != 0) break; @@ -859,7 +892,7 @@ smb_nt_reply(struct smb_ntrq *ntp) md_get_uint8(mdp, &wc); /* SetupCount */ tmp = wc; while (tmp--) - md_get_uint16(mdp, NULL); + md_get_uint16le(mdp, NULL); if ((error2 = md_get_uint16le(mdp, &bc)) != 0) break; @@ -1375,7 +1408,7 @@ smb_t2_request(struct smb_t2rq *t2p) if (++i > SMBMAXRESTARTS) break; mutex_enter(&(t2p)->t2_lock); - if (t2p->t2_share && t2p->t2_share->ss_mount) { + if (t2p->t2_share) { cv_timedwait(&t2p->t2_cond, &(t2p)->t2_lock, lbolt + (hz * SMB_RCNDELAY)); } else { @@ -1408,7 +1441,7 @@ smb_nt_request(struct smb_ntrq *ntp) if (++i > SMBMAXRESTARTS) break; mutex_enter(&(ntp)->nt_lock); - if (ntp->nt_share && ntp->nt_share->ss_mount) { + if (ntp->nt_share) { cv_timedwait(&ntp->nt_cond, &(ntp)->nt_lock, lbolt + (hz * SMB_RCNDELAY)); diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/smb_rq.h b/usr/src/uts/common/fs/smbclnt/netsmb/smb_rq.h index 7359b42049..1aeefcedd7 100644 --- a/usr/src/uts/common/fs/smbclnt/netsmb/smb_rq.h +++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_rq.h @@ -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. */ @@ -50,11 +50,12 @@ #define SMBR_RESTART 0x0010 /* req should be repeated if possible */ #define SMBR_NORESTART 0x0020 /* request is not restartable */ #define SMBR_MULTIPACKET 0x0040 /* multiple pkts can be sent/received */ -#define SMBR_INTERNAL 0x0080 /* request is internal to netsmb */ +#define SMBR_INTERNAL 0x0080 /* request enqueued by the IOD! */ #define SMBR_NOINTR_SEND 0x0100 /* no interrupt in send wait */ #define SMBR_NOINTR_RECV 0x0200 /* no interrupt in recv wait */ #define SMBR_SENDWAIT 0x0400 /* waiting for send to complete */ -#define SMBR_VCREF 0x4000 /* took vc reference */ +#define SMBR_NORECONNECT 0x0800 /* do not reconnect for this */ +/* SMBR_VCREF 0x4000 * took vc reference (obsolete) */ #define SMBR_MOREDATA 0x8000 /* our buffer was too small */ #define SMBT2_ALLSENT 0x0001 /* all data and params are sent */ @@ -67,7 +68,6 @@ #define SMBRQ_LOCK(rqp) mutex_enter(&(rqp)->sr_lock) #define SMBRQ_UNLOCK(rqp) mutex_exit(&(rqp)->sr_lock) - enum smbrq_state { SMBRQ_NOTSENT, /* rq have data to send */ SMBRQ_SENT, /* send procedure completed */ @@ -85,13 +85,16 @@ struct smb_rq { struct smb_vc *sr_vc; struct smb_share *sr_share; struct _kthread *sr_owner; - ushort_t sr_mid; uint32_t sr_seqno; /* Seq. no. of request */ uint32_t sr_rseqno; /* Seq. no. of reply */ struct mbchain sr_rq; uchar_t sr_cmd; uint8_t sr_rqflags; uint16_t sr_rqflags2; + uint16_t sr_rqtid; + uint16_t sr_pid; + uint16_t sr_rquid; + uint16_t sr_mid; uchar_t *sr_wcount; uchar_t *sr_bcount; struct mdchain sr_rp; @@ -105,8 +108,6 @@ struct smb_rq { int sr_sendcnt; struct timespec sr_timesent; int sr_lerror; - uint16_t *sr_rqtid; - uint16_t *sr_rquid; uint8_t sr_errclass; uint16_t sr_serror; uint32_t sr_error; @@ -124,7 +125,7 @@ struct smb_t2rq { kcondvar_t t2_cond; uint16_t t2_setupcount; uint16_t *t2_setupdata; - uint16_t t2_setup[SMB_MAXSETUPWORDS]; + uint16_t t2_setup[SMBIOC_T2RQ_MAXSETUP]; uint8_t t2_maxscount; /* max setup words to return */ uint16_t t2_maxpcount; /* max param bytes to return */ uint16_t t2_maxdcount; /* max data bytes to return */ @@ -174,13 +175,18 @@ struct smb_ntrq { }; typedef struct smb_ntrq smb_ntrq_t; +#define smb_rq_getrequest(RQ, MBPP) \ + *(MBPP) = &(RQ)->sr_rq +#define smb_rq_getreply(RQ, MDPP) \ + *(MDPP) = &(RQ)->sr_rp + +void smb_rq_done(struct smb_rq *rqp); int smb_rq_alloc(struct smb_connobj *layer, uchar_t cmd, struct smb_cred *scred, struct smb_rq **rqpp); int smb_rq_init(struct smb_rq *rqp, struct smb_connobj *layer, uchar_t cmd, struct smb_cred *scred); -void smb_rq_done(struct smb_rq *rqp); -int smb_rq_getrequest(struct smb_rq *rqp, struct mbchain **mbpp); -int smb_rq_getreply(struct smb_rq *rqp, struct mdchain **mbpp); + +void smb_rq_fillhdr(struct smb_rq *rqp); void smb_rq_wstart(struct smb_rq *rqp); void smb_rq_wend(struct smb_rq *rqp); void smb_rq_bstart(struct smb_rq *rqp); diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/smb_sign.c b/usr/src/uts/common/fs/smbclnt/netsmb/smb_sign.c new file mode 100644 index 0000000000..91c450bb23 --- /dev/null +++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_sign.c @@ -0,0 +1,311 @@ +/* + * 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. + */ + +/* + * Support for SMB "signing" (message integrity) + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/conf.h> +#include <sys/proc.h> +#include <sys/fcntl.h> +#include <sys/socket.h> +#include <sys/md4.h> +#include <sys/md5.h> +#include <sys/des.h> +#include <sys/kmem.h> +#include <sys/crypto/api.h> +#include <sys/crypto/common.h> +#include <sys/cmn_err.h> +#include <sys/stream.h> +#include <sys/strsun.h> +#include <sys/sdt.h> + +#include <netsmb/smb_osdep.h> +#include <netsmb/smb.h> +#include <netsmb/smb_conn.h> +#include <netsmb/smb_subr.h> +#include <netsmb/smb_dev.h> +#include <netsmb/smb_rq.h> + +#ifdef DEBUG +/* + * Set this to a small number to debug sequence numbers + * that seem to get out of step. + */ +int nsmb_signing_fudge = 0; +#endif + +/* Mechanism definitions */ +static crypto_mechanism_t crypto_mech_md5 = { CRYPTO_MECH_INVALID }; + +void +smb_crypto_mech_init(void) +{ + crypto_mech_md5.cm_type = crypto_mech2id(SUN_CKM_MD5); +} + + + +#define SMBSIGLEN 8 /* SMB signature length */ +#define SMBSIGOFF 14 /* SMB signature offset */ + +/* + * Compute HMAC-MD5 of packet data, using the stored MAC key. + * + * See similar code for the server side: + * uts/common/fs/smbsrv/smb_signing.c : smb_sign_calc + */ +static int +smb_compute_MAC(struct smb_vc *vcp, mblk_t *mp, + uint32_t seqno, uchar_t *signature) +{ + crypto_context_t crypto_ctx; + crypto_data_t key; + crypto_data_t data; + crypto_data_t digest; + uchar_t mac[16]; + int status; + /* + * 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; + + ASSERT(mp != NULL); + ASSERT(MBLKL(mp) >= SMB_HDRLEN); + ASSERT(vcp->vc_mackey != NULL); + + /* + * Make an aligned copy of the SMB header + * and fill in the sequence number. + */ + bcopy(mp->b_rptr, smbhdr.r.raw, SMB_HDRLEN); + smbhdr.s.sig[0] = htolel(seqno); + smbhdr.s.sig[1] = 0; + + /* + * Compute the MAC: MD5(concat(Key, message)) + */ + if (crypto_mech_md5.cm_type == CRYPTO_MECH_INVALID) { + SMBSDEBUG("crypto_mech_md5 invalid\n"); + return (CRYPTO_MECHANISM_INVALID); + } + status = crypto_digest_init(&crypto_mech_md5, &crypto_ctx, 0); + if (status != CRYPTO_SUCCESS) + return (status); + + /* Digest the MAC Key */ + key.cd_format = CRYPTO_DATA_RAW; + key.cd_offset = 0; + key.cd_length = vcp->vc_mackeylen; + key.cd_miscdata = 0; + key.cd_raw.iov_base = (char *)vcp->vc_mackey; + key.cd_raw.iov_len = vcp->vc_mackeylen; + status = crypto_digest_update(crypto_ctx, &key, 0); + if (status != CRYPTO_SUCCESS) + return (status); + + /* Digest the (copied) SMB header */ + data.cd_format = CRYPTO_DATA_RAW; + data.cd_offset = 0; + data.cd_length = SMB_HDRLEN; + data.cd_miscdata = 0; + data.cd_raw.iov_base = (char *)smbhdr.r.raw; + data.cd_raw.iov_len = SMB_HDRLEN; + status = crypto_digest_update(crypto_ctx, &data, 0); + if (status != CRYPTO_SUCCESS) + return (status); + + /* Digest rest of the SMB message. */ + data.cd_format = CRYPTO_DATA_MBLK; + data.cd_offset = SMB_HDRLEN; + data.cd_length = msgdsize(mp) - SMB_HDRLEN; + data.cd_miscdata = 0; + data.cd_mp = mp; + status = crypto_digest_update(crypto_ctx, &data, 0); + if (status != CRYPTO_SUCCESS) + return (status); + + /* Final */ + digest.cd_format = CRYPTO_DATA_RAW; + digest.cd_offset = 0; + digest.cd_length = sizeof (mac); + digest.cd_miscdata = 0; + digest.cd_raw.iov_base = (char *)mac; + digest.cd_raw.iov_len = sizeof (mac); + status = crypto_digest_final(crypto_ctx, &digest, 0); + if (status != CRYPTO_SUCCESS) + return (status); + + /* + * Finally, store the signature. + * (first 8 bytes of the mac) + */ + if (signature) + bcopy(mac, signature, SMBSIGLEN); + + return (0); +} + +/* + * Sign a request with HMAC-MD5. + */ +int +smb_rq_sign(struct smb_rq *rqp) +{ + struct smb_vc *vcp = rqp->sr_vc; + mblk_t *mp = rqp->sr_rq.mb_top; + uint8_t *sigloc; + int status; + + /* + * Our mblk allocation ensures this, + * but just in case... + */ + if (MBLKL(mp) < SMB_HDRLEN) { + if (!pullupmsg(mp, SMB_HDRLEN)) + return (0); + } + sigloc = mp->b_rptr + SMBSIGOFF; + + if (vcp->vc_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. + */ + status = smb_compute_MAC(vcp, mp, rqp->sr_seqno, sigloc); + if (status != CRYPTO_SUCCESS) { + SMBSDEBUG("Crypto error %d", status); + bzero(sigloc, SMBSIGLEN); + return (ENOTSUP); + } + return (0); +} + +/* + * Verify reply signature. + */ +int +smb_rq_verify(struct smb_rq *rqp) +{ + struct smb_vc *vcp = rqp->sr_vc; + mblk_t *mp = rqp->sr_rp.md_top; + uint8_t sigbuf[SMBSIGLEN]; + uint8_t *sigloc; + int status; + int fudge; + + /* + * Note vc_mackey and vc_mackeylen gets filled in by + * smb_usr_iod_work as the connection comes in. + */ + if (vcp->vc_mackey == NULL) { + SMBSDEBUG("no mac key\n"); + return (0); + } + + /* + * Let caller deal with empty reply or short messages by + * returning zero. Caller will fail later, in parsing. + */ + if (mp == NULL) { + SMBSDEBUG("empty reply\n"); + return (0); + } + if (MBLKL(mp) < SMB_HDRLEN) { + if (!pullupmsg(mp, SMB_HDRLEN)) + return (0); + } + sigloc = mp->b_rptr + SMBSIGOFF; + + SMBSDEBUG("sr_rseqno = 0x%x\n", rqp->sr_rseqno); + + status = smb_compute_MAC(vcp, mp, rqp->sr_rseqno, sigbuf); + if (status != CRYPTO_SUCCESS) { + SMBSDEBUG("Crypto error %d", status); + /* + * 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); + + SMBSDEBUG("BAD signature, MID=0x%x\n", rqp->sr_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(vcp, mp, rqp->sr_rseqno + fudge, sigbuf); + if (bcmp(sigbuf, sigloc, SMBSIGLEN) == 0) + break; + smb_compute_MAC(vcp, mp, rqp->sr_rseqno - fudge, sigbuf); + if (bcmp(sigbuf, sigloc, SMBSIGLEN) == 0) { + fudge = -fudge; + break; + } + } + if (fudge <= nsmb_signing_fudge) { + SMBSDEBUG("sr_rseqno=%d, but %d would have worked\n", + rqp->sr_rseqno, rqp->sr_rseqno + fudge); + } +#endif + return (EBADRPC); +} diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/smb_smb.c b/usr/src/uts/common/fs/smbclnt/netsmb/smb_smb.c index 3412109a68..fce18f74ff 100644 --- a/usr/src/uts/common/fs/smbclnt/netsmb/smb_smb.c +++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_smb.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. */ @@ -105,19 +105,6 @@ struct smb_dialect { const char *d_name; }; -smb_unichar smb_unieol = 0; - -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) /* * Number of seconds between 1970 and 1601 year @@ -177,1346 +164,143 @@ extern int iconv_open(const char *to, const char *from, void **handle); extern int iconv_close(void *handle); #endif -int -smb_smb_negotiate(struct smb_vc *vcp, struct smb_cred *scred) -{ - struct smb_dialect *dp; - struct smb_sopt *sp = NULL; - struct smb_rq *rqp; - struct mbchain *mbp; - struct mdchain *mdp; - u_int8_t wc, stime[8], sblen; - u_int16_t dindex, tw, tw1, swlen, bc; - int error; - int unicode = 0; - char *servercs; - void *servercshandle = NULL; - void *localcshandle = NULL; - int negotiated_signing = 0; - u_int16_t toklen; - - /* - * We set various flags below to keep track of - * interesting things we learn from negotiation. - * Clear all the flags except these two, which - * are operational rather than protocol info. - */ - SMB_VC_LOCK(vcp); - vcp->vc_flags &= (SMBV_GONE | SMBV_RECONNECTING); - SMB_VC_UNLOCK(vcp); - - /* - * Now vc_hflags and vc_hflags2. Careful with this: - * Leave SMB_FLAGS2_UNICODE off so mb_put_dstring - * marshalls the dialect strings in plain ascii. - * We'll turn that on below, if appropriate. - * - * Note: These flags are marshalled into the request - * when we call smb_rq_alloc, so changing them after - * this point does not affect THIS request. - */ - vcp->vc_hflags = SMB_FLAGS_CASELESS; - vcp->vc_hflags2 = (SMB_FLAGS2_ERR_STATUS | - SMB_FLAGS2_KNOWS_LONG_NAMES); - - /* User-level may ask for extended security. */ - if (vcp->vc_vopt & SMBVOPT_EXT_SEC) - vcp->vc_hflags2 |= SMB_FLAGS2_EXT_SEC; - - /* Also clear any old key (for reconnect) */ - if (vcp->vc_mackey != NULL) { - kmem_free(vcp->vc_mackey, vcp->vc_mackeylen); - vcp->vc_mackey = NULL; - vcp->vc_mackeylen = 0; - vcp->vc_seqno = 0; - } - - sp = &vcp->vc_sopt; - bzero(sp, sizeof (struct smb_sopt)); - error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_NEGOTIATE, scred, &rqp); - if (error) - return (error); - smb_rq_getrequest(rqp, &mbp); - smb_rq_wstart(rqp); - smb_rq_wend(rqp); - smb_rq_bstart(rqp); - for (dp = smb_dialects; dp->d_id != -1; dp++) { - mb_put_uint8(mbp, SMB_DT_DIALECT); - smb_put_dstring(mbp, vcp, dp->d_name, SMB_CS_NONE); - } - smb_rq_bend(rqp); - - /* - * This request should not wait for - * connection state changes, etc. - */ - rqp->sr_flags |= SMBR_INTERNAL; - error = smb_rq_simple(rqp); - SMBSDEBUG("%d\n", error); - if (error) - goto bad; - - smb_rq_getreply(rqp, &mdp); - do { - error = md_get_uint8(mdp, &wc); - if (error) - break; - error = md_get_uint16le(mdp, &dindex); - if (error) - break; - error = EBADRPC; - if (dindex > SMB_DIALECT_MAX) { - SMBERROR( - "Don't know how to talk with server %s (%d)\n", - vcp->vc_srvname, dindex); - break; - } - dp = smb_dialects + dindex; - if (dindex < SMB_DIALECT_MAX) { - SMBERROR( - "Server %s negotiated old dialect (%s)\n", - vcp->vc_srvname, dp->d_name); - } - sp->sv_proto = dp->d_id; - SMBSDEBUG("Dialect %s (%d, %d)\n", dp->d_name, dindex, wc); - if (dp->d_id >= SMB_DIALECT_NTLM0_12) { - if (wc != 17) - break; - md_get_uint8(mdp, &sp->sv_sm); - md_get_uint16le(mdp, &sp->sv_maxmux); - md_get_uint16le(mdp, &sp->sv_maxvcs); - md_get_uint32le(mdp, &sp->sv_maxtx); - md_get_uint32le(mdp, &sp->sv_maxraw); - md_get_uint32le(mdp, &sp->sv_skey); - md_get_uint32le(mdp, &sp->sv_caps); - md_get_mem(mdp, (char *)stime, 8, MB_MSYSTEM); - md_get_uint16le(mdp, (u_int16_t *)&sp->sv_tz); - md_get_uint8(mdp, &sblen); - error = md_get_uint16le(mdp, &bc); - if (error) - break; - - /* 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 (sp->sv_sm & SMB_SM_SIGS_REQUIRE) { - /* - * Server requires signing. - */ - negotiated_signing = 1; - } else if (sp->sv_sm & SMB_SM_SIGS) { - /* - * Server enables signing (client's option). - * If enabled locally, do signing. - */ - if (vcp->vc_vopt & SMBVOPT_SIGNING_ENABLED) - negotiated_signing = 1; - /* else not signing. */ - } else { - /* - * Server does not support signing. - * If we "require" it, bail now. - */ - if (vcp->vc_vopt & SMBVOPT_SIGNING_REQUIRED) { - SMBERROR("Client requires signing " - "but server has it disabled.\n"); - error = EBADRPC; - break; - } - } - SMBSDEBUG("Security signatures: %d\n", - negotiated_signing); - if (negotiated_signing) { - SMB_VC_LOCK(vcp); - vcp->vc_flags |= SMBV_WILL_SIGN; - SMB_VC_UNLOCK(vcp); - } - - if (sp->sv_caps & SMB_CAP_UNICODE) { - SMB_VC_LOCK(vcp); - vcp->vc_flags |= SMBV_UNICODE; - SMB_VC_UNLOCK(vcp); - unicode = 1; - } - if (!(sp->sv_caps & SMB_CAP_STATUS32)) { - /* - * 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. - */ - vcp->vc_hflags2 &= ~SMB_FLAGS2_ERR_STATUS; - } - if (dp->d_id == SMB_DIALECT_NTLM0_12 && - sp->sv_maxtx < 4096 && - (sp->sv_caps & SMB_CAP_NT_SMBS) == 0) { - SMB_VC_LOCK(vcp); - vcp->vc_flags |= SMBV_WIN95; - SMB_VC_UNLOCK(vcp); - SMBSDEBUG("Win95 detected\n"); - } - - /* - * 3 cases here: - * - * 1) Extended security. - * Read bc bytes below for security blob. - * Note that we DON'T put the Caps flag in outtok. - * outtoklen = bc - * - * 2) No extended security, have challenge data and - * possibly a domain name (which might be zero - * bytes long, meaning "missing"). - * Copy challenge stuff to vcp->vc_ch (sblen bytes), - * then copy Cap flags and domain name (bc-sblen - * bytes) to outtok. - * outtoklen = bc-sblen+4, where the 4 is for the - * Caps flag. - * - * 3) No extended security, no challenge data, just - * possibly a domain name. - * Copy Capsflags and domain name (bc) to outtok. - * outtoklen = bc+4, where 4 is for the Caps flag - */ - - /* - * Sanity check: make sure the challenge length - * isn't bigger than the byte count. - */ - if (sblen > bc) { - error = EBADRPC; - break; - } - toklen = bc; - - if (sblen && sblen <= SMB_MAXCHALLENGELEN && - sp->sv_sm & SMB_SM_ENCRYPT) { - error = md_get_mem(mdp, - (char *)vcp->vc_challenge, - sblen, MB_MSYSTEM); - if (error) - break; - vcp->vc_chlen = sblen; - toklen -= sblen; - - SMB_VC_LOCK(vcp); - vcp->vc_flags |= SMBV_ENCRYPT; - SMB_VC_UNLOCK(vcp); - } - - /* - * For servers that don't support unicode - * there are 2 things we could do: - * 1) Pass the server Caps flags up to the - * user level so the logic up there will - * know whether the domain name is unicode - * (this is what I did). - * 2) Try to convert the non-unicode string - * to unicode. This doubles the length of - * the outtok buffer and would be guessing that - * the string was single-byte ascii, and that - * might be wrong. Why ask for trouble? - */ - - /* Warning: NetApp may omit the GUID */ - - if (!(sp->sv_caps & SMB_CAP_EXT_SECURITY)) { - /* - * No extended security. - * Stick domain name, if present, - * and caps in outtok. - */ - toklen = toklen + 4; /* space for Caps flags */ - vcp->vc_outtoklen = toklen; - vcp->vc_outtok = kmem_alloc(toklen, KM_SLEEP); - /* first store server capability bits */ - /*LINTED*/ - ASSERT(vcp->vc_outtok == - (caddr_t)(((u_int32_t *)vcp->vc_outtok))); - /*LINTED*/ - *(u_int32_t *)(vcp->vc_outtok) = sp->sv_caps; - - /* - * Then store the domain name if present; - * be sure to subtract 4 from the length - * for the Caps flag. - */ - if (toklen > 4) { - error = md_get_mem(mdp, - vcp->vc_outtok+4, toklen-4, - MB_MSYSTEM); - } - } else { - /* - * Extended security. - * Stick the rest of the buffer in outtok. - */ - vcp->vc_outtoklen = toklen; - vcp->vc_outtok = kmem_alloc(toklen, KM_SLEEP); - error = md_get_mem(mdp, vcp->vc_outtok, toklen, - MB_MSYSTEM); - } - break; - } - vcp->vc_hflags2 &= ~(SMB_FLAGS2_EXT_SEC|SMB_FLAGS2_DFS| - SMB_FLAGS2_ERR_STATUS|SMB_FLAGS2_UNICODE); - if (dp->d_id > SMB_DIALECT_CORE) { - md_get_uint16le(mdp, &tw); - sp->sv_sm = (uchar_t)tw; - md_get_uint16le(mdp, &tw); - sp->sv_maxtx = tw; - md_get_uint16le(mdp, &sp->sv_maxmux); - md_get_uint16le(mdp, &sp->sv_maxvcs); - md_get_uint16le(mdp, &tw); /* rawmode */ - md_get_uint32le(mdp, &sp->sv_skey); - if (wc == 13) { /* >= LANMAN1 */ - md_get_uint16(mdp, &tw); /* time */ - md_get_uint16(mdp, &tw1); /* date */ - md_get_uint16le(mdp, (u_int16_t *)&sp->sv_tz); - md_get_uint16le(mdp, &swlen); - if (swlen > SMB_MAXCHALLENGELEN) - break; - md_get_uint16(mdp, NULL); /* mbz */ - if (md_get_uint16le(mdp, &bc) != 0) - break; - if (bc < swlen) - break; - if (swlen && (sp->sv_sm & SMB_SM_ENCRYPT)) { - error = md_get_mem(mdp, - (char *)vcp->vc_challenge, - swlen, MB_MSYSTEM); - if (error) - break; - vcp->vc_chlen = swlen; - - SMB_VC_LOCK(vcp); - vcp->vc_flags |= SMBV_ENCRYPT; - SMB_VC_UNLOCK(vcp); - } - } - } else { /* an old CORE protocol */ - vcp->vc_hflags2 &= ~SMB_FLAGS2_KNOWS_LONG_NAMES; - sp->sv_maxmux = 1; - } - error = 0; - /*LINTED*/ - } while (0); - if (error == 0) { - uint32_t x; - - /* - * Maximum outstanding requests. - */ - if (vcp->vc_maxmux < 1) - vcp->vc_maxmux = 1; - - /* - * Max VCs between server and client. - * We only use one. - */ - vcp->vc_maxvcs = sp->sv_maxvcs; - if (vcp->vc_maxvcs < 1) - vcp->vc_maxvcs = 1; - - /* - * Maximum transfer size. - * Sanity checks: - * - * Spec. says lower limit is 1024. OK. - * - * 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_xxx, which we nearly always use. - */ - vcp->vc_txmax = sp->sv_maxtx; - if (vcp->vc_txmax < 1024) - vcp->vc_txmax = 1024; - if (vcp->vc_txmax > 0x8000) - vcp->vc_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 vc_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. - */ - x = (vcp->vc_txmax - 68) & 0xFE00; - vcp->vc_rxmax = x; - vcp->vc_wxmax = x; - - SMBSDEBUG("TZ = %d\n", sp->sv_tz); - SMBSDEBUG("CAPS = %x\n", sp->sv_caps); - - SMBSDEBUG("maxmux = %d\n", vcp->vc_maxmux); - SMBSDEBUG("maxvcs = %d\n", vcp->vc_maxvcs); - SMBSDEBUG("txmax = %d\n", vcp->vc_txmax); - SMBSDEBUG("rxmax = %d\n", vcp->vc_rxmax); - SMBSDEBUG("wxmax = %d\n", vcp->vc_wxmax); - } - - /* - * If the server supports Unicode, set up to use Unicode - * when talking to them. Othewise, use code page 437. - */ - if (unicode) - servercs = "ucs-2"; - else { - /* - * todo: if we can't determine the server's encoding, we - * need to try a best-guess here. - */ - servercs = "cp437"; - } -#if defined(NOICONVSUPPORT) || defined(lint) - /* - * REVISIT - */ - error = iconv_open(servercs, "utf-8", &servercshandle); - if (error != 0) - goto bad; - error = iconv_open("utf-8", servercs, &localcshandle); - if (error != 0) { - iconv_close(servercshandle); - goto bad; - } - if (vcp->vc_toserver) - iconv_close(vcp->vc_toserver); - if (vcp->vc_tolocal) - iconv_close(vcp->vc_tolocal); - vcp->vc_toserver = servercshandle; - vcp->vc_tolocal = localcshandle; -#endif - if (unicode) - vcp->vc_hflags2 |= SMB_FLAGS2_UNICODE; -bad: - smb_rq_done(rqp); - return (error); -} - -static void -get_ascii_password(struct smb_vc *vcp, int upper, char *pbuf) -{ - const char *pw = smb_vc_getpass(vcp); - if (upper) - smb_toupper(pw, pbuf, SMB_MAXPASSWORDLEN); - else - strncpy(pbuf, pw, SMB_MAXPASSWORDLEN); - pbuf[SMB_MAXPASSWORDLEN] = '\0'; -} - -#ifdef APPLE -static void -get_unicode_password(struct smb_vc *vcp, char *pbuf) -{ - strncpy(pbuf, smb_vc_getpass(vcp), SMB_MAXPASSWORDLEN); - pbuf[SMB_MAXPASSWORDLEN] = '\0'; -} -#endif - -/*ARGSUSED*/ -static uchar_t * -add_name_to_blob(uchar_t *blobnames, struct smb_vc *vcp, const uchar_t *name, - size_t namelen, int nametype, int uppercase) -{ - struct ntlmv2_namehdr namehdr; - char *namebuf; - u_int16_t *uninamebuf; - size_t uninamelen; - - if (name != NULL) { - uninamebuf = kmem_alloc(2 * namelen, KM_SLEEP); - if (uppercase) { - namebuf = kmem_alloc(namelen + 1, KM_SLEEP); - smb_toupper((const char *)name, namebuf, namelen); - namebuf[namelen] = '\0'; - uninamelen = smb_strtouni(uninamebuf, namebuf, namelen, - UCONV_IGNORE_NULL); - kmem_free(namebuf, namelen + 1); - } else { - uninamelen = smb_strtouni(uninamebuf, (char *)name, - namelen, UCONV_IGNORE_NULL); - } - } else { - uninamelen = 0; - uninamebuf = NULL; - } - namehdr.type = htoles(nametype); - namehdr.len = htoles(uninamelen); - bcopy(&namehdr, blobnames, sizeof (namehdr)); - blobnames += sizeof (namehdr); - if (uninamebuf != NULL) { - bcopy(uninamebuf, blobnames, uninamelen); - blobnames += uninamelen; - kmem_free(uninamebuf, namelen * 2); - } - return (blobnames); -} - -static uchar_t * -make_ntlmv2_blob(struct smb_vc *vcp, u_int64_t client_nonce, - size_t *bloblen, size_t *blob_allocsz) -{ - uchar_t *blob; - size_t blobsize; - size_t domainlen, srvlen; - struct ntlmv2_blobhdr *blobhdr; - struct timespec now; - u_int64_t timestamp; - uchar_t *blobnames; - ptrdiff_t diff; - - /* - * XXX - the information at - * - * http://davenport.sourceforge.net/ntlm.html#theNtlmv2Response - * - * says that the "target information" comes from the Type 2 message, - * but, as we're not doing NTLMSSP, we don't have that. - * - * Should we use the names from the NegProt response? Can we trust - * the NegProt response? (I've seen captures where the primary - * domain name has an extra byte in front of it.) - * - * For now, we don't trust it - we use vcp->vc_domain and - * vcp->vc_srvname, instead. We upper-case them and convert - * them to Unicode, as that's what's supposed to be in the blob. - */ - domainlen = strlen(vcp->vc_domain); - srvlen = strlen(vcp->vc_srvname); - blobsize = sizeof (struct ntlmv2_blobhdr) - + 3*sizeof (struct ntlmv2_namehdr) + 4 + 2*domainlen + 2*srvlen; - *blob_allocsz = blobsize; - blobhdr = kmem_zalloc(blobsize, KM_SLEEP); - blob = (uchar_t *)blobhdr; - blobhdr->header = htolel(0x00000101); - gethrestime(&now); - smb_time_local2NT(&now, 0, ×tamp); - blobhdr->timestamp = htoleq(timestamp); - blobhdr->client_nonce = client_nonce; - blobnames = blob + sizeof (struct ntlmv2_blobhdr); - blobnames = add_name_to_blob(blobnames, vcp, (uchar_t *)vcp->vc_domain, - domainlen, NAMETYPE_DOMAIN_NB, 1); - blobnames = add_name_to_blob(blobnames, vcp, (uchar_t *)vcp->vc_srvname, - srvlen, NAMETYPE_MACHINE_NB, 1); - blobnames = add_name_to_blob(blobnames, vcp, NULL, 0, NAMETYPE_EOL, 0); - diff = (intptr_t)blobnames - (intptr_t)blob; - ASSERT(diff == (ptrdiff_t)((size_t)diff)); - *bloblen = (size_t)diff; - return (blob); -} - /* - * When not doing Kerberos, we can try, in order: - * - * NTLMv2 - * NTLM (and maybe LM) - * - * if the server supports encrypted passwords, or - * - * plain-text with the ASCII password not upper-cased - * plain-text with the ASCII password upper-cased - * - * if it doesn't. + * Moved to user space helper: + * smb_smb_negotiate() + * smb_smb_ssnsetup() + * smb_smb_ssnclose() + * smb_share_typename() */ -typedef enum { - ClearUC, /* Cleartext p/w, upper case */ - ClearMC, /* Cleartext p/w, mixed case */ - NTLMv1, - NTLMv2, - ExtSec, /* Extended Security (Kerberos) */ - NullSes /* Null session (keep last) */ -} authtype_t; + int -smb_smb_ssnsetup(struct smb_vc *vcp, struct smb_cred *scred) +smb_smb_treeconnect(struct smb_share *ssp, struct smb_cred *scred) { - struct smb_rq *rqp; + struct smb_vc *vcp; + struct smb_rq *rqp = NULL; struct mbchain *mbp; struct mdchain *mdp; - u_int8_t wc; - int minauth; - smb_uniptr unipp = NULL, ntencpass = NULL; - char *pp = NULL, *up = NULL, *ucup = NULL; - char *ucdp = vcp->vc_domain; /* already upper case */ - char *encpass = NULL; - int error = 0; - size_t plen = 0, plen_alloc = 0; - size_t uniplen = 0, uniplen_alloc = 0; - size_t ucup_sl = 0; - authtype_t authtype; - size_t ntlmv2_bloblen, ntlmv2_blob_allocsz; - uchar_t *ntlmv2_blob; - u_int64_t client_nonce; - u_int32_t caps; - u_int16_t bl; /* BLOB length */ - u_int16_t bc; /* byte count */ - u_int16_t action; - u_int16_t rpflags2; - int declinedguest = 0; - uchar_t v2hash[16]; - static const char NativeOS[] = "Solaris"; - static const char LanMan[] = "NETSMB"; - /* - * Most of the "capability" bits we offer should 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; + char *pbuf, *unc_name = NULL; + int error, tlen, plen, unc_len; + uint16_t bcnt, options; + uint8_t wc; - caps = vcp->vc_sopt.sv_caps & caps_mask; - minauth = vcp->vc_vopt & SMBVOPT_MINAUTH; + vcp = SSTOVC(ssp); /* - * This function tries authentication types in a - * sequence going stronger to weaker, until it - * succeeds or runs into "minauth" and fails. - * - * Extended security is a special case because - * fall-back requires a return to user-level and - * a new connection, new SMB negotiate, etc. - * Null session is also special - no fall-back. + * Make this a "VC-level" request, so it will have + * rqp->sr_share == NULL, and smb_iod_sendrq() + * will send it with TID = SMB_TID_UNKNOWN * - * Otherwise if the server supports encryption, - * try NTLMv2, then NTLM, etc. - */ - if (vcp->vc_intok) - authtype = ExtSec; - else if (vcp->vc_username[0] == '\0') - authtype = NullSes; - else if ((vcp->vc_sopt.sv_sm & SMB_SM_USER) == 0) { - /* Share-level security. */ - authtype = NullSes; - } else { - /* Have SMB_SM_USER. Encryption? */ - if (vcp->vc_sopt.sv_sm & SMB_SM_ENCRYPT) { - if (nsmb_enable_ntlmv2) - authtype = NTLMv2; - else - authtype = NTLMv1; - } else { - /* - * This is normally disallowed - * by the minauth check below. - */ - authtype = ClearMC; - } - } - - /* - * If server does not support encryption, - * disable unicode too. (Spec. for this?) - */ - if ((vcp->vc_sopt.sv_sm & SMB_SM_ENCRYPT) == 0) { - if (vcp->vc_flags & SMBV_UNICODE) { - vcp->vc_hflags2 &= ~SMB_FLAGS2_UNICODE; - vcp->vc_toserver = 0; - } - } - -again: - SMBSDEBUG("authtype = %d\n", authtype); - - /* - * Now disallow auth. types that fall below - * the minimum strength configured. - * We hold no kmem here. - */ - switch (minauth) { - - case SMBVOPT_MINAUTH_NONE: - break; - - case SMBVOPT_MINAUTH_LM: - case SMBVOPT_MINAUTH_NTLM: - if (authtype < NTLMv1) { - error = EAUTH; - goto ssn_exit; - } - break; - - case SMBVOPT_MINAUTH_NTLMV2: - if (authtype < NTLMv2) { - error = EAUTH; - goto ssn_exit; - } - break; - - case SMBVOPT_MINAUTH_KERBEROS: - if (authtype < ExtSec) { - error = EAUTH; - goto ssn_exit; - } - break; - - default: - SMBSDEBUG("bad minauth 0x%x\n", minauth); - error = EAUTH; - goto ssn_exit; - } - - /* - * See comment in smb_iod_sendrq() - * about vc_smbuid initialization. - */ - vcp->vc_smbuid = SMB_UID_UNKNOWN; - - /* - * Within this switch, we may allocate either or both: - * encpass, ntencpass (len: plen_alloc, uniplen_alloc) - * and will free these below (see the label "bad") + * This also serves to bypass the wait for + * share state changes, which this call is + * trying to carry out. */ - switch (authtype) { - - case ExtSec: - /* - * With extended security, the whole blob is - * passed in from user-level (vc_intok) - */ - ASSERT(vcp->vc_intok != NULL); - caps |= SMB_CAP_EXT_SECURITY; - /* XXX Need Session Key */ - if (vcp->vc_intoklen > 65536 || - !(vcp->vc_hflags2 & SMB_FLAGS2_EXT_SEC) || - SMB_DIALECT(vcp) < SMB_DIALECT_NTLM0_12) { - /* We hold no kmem here. */ - error = EINVAL; - goto ssn_exit; - } - vcp->vc_smbuid = 0; - break; - - case NullSes: - pp = ""; - plen = 1; - unipp = &smb_unieol; - uniplen = sizeof (smb_unieol); - break; - - case NTLMv2: - /* - * Compute the LMv2 and NTLMv2 responses, - * derived from the challenge, the user name, - * the domain/workgroup into which we're - * logging, and the Unicode password. - */ - - /* - * Construct the client nonce by getting - * a bunch of random data. - */ - (void) random_get_pseudo_bytes((void *) - &client_nonce, sizeof (client_nonce)); - - /* - * Convert the user name to upper-case, as - * that's what's used when computing LMv2 - * and NTLMv2 responses. - */ - ucup_sl = strlen(vcp->vc_username); - ucup = kmem_alloc(ucup_sl + 1, KM_SLEEP); - smb_toupper((const char *)vcp->vc_username, - ucup, ucup_sl); - ucup[ucup_sl] = '\0'; - - /* - * Compute the NTLMv2 hash, which is - * derived from the NTLMv1 hash and - * the upper-case user + domain. - */ - smb_ntlmv2hash(vcp->vc_nthash, - ucup, ucdp, v2hash); - - /* - * Compute the LMv2 response, derived from - * the v2hash, the server challenge, and - * the client nonce (random bits). - * Note: kmem_alloc encpass (plen) - */ - smb_ntlmv2response(v2hash, - vcp->vc_challenge, - (uchar_t *)&client_nonce, 8, - (uchar_t **)&encpass, &plen); - plen_alloc = plen; - pp = encpass; - - /* - * Construct the blob. - * Note: kmem_alloc ntlmv2_blob - */ - ntlmv2_blob = make_ntlmv2_blob(vcp, - client_nonce, &ntlmv2_bloblen, - &ntlmv2_blob_allocsz); - - /* - * Compute the NTLMv2 response, derived - * from the server challenge, the - * user name, the domain/workgroup - * into which we're logging, the - * blob, and the v2 hash. - * Note: kmem_alloc ntencpass (uniplen) - */ - smb_ntlmv2response(v2hash, - vcp->vc_challenge, - ntlmv2_blob, ntlmv2_bloblen, - (uchar_t **)&ntencpass, &uniplen); - uniplen_alloc = uniplen; - unipp = ntencpass; - - /* - * If we negotiated signing, compute the MAC key - * and start signing messages, but only on the - * first non-null session login. - */ - if ((vcp->vc_flags & SMBV_WILL_SIGN) && - !(vcp->vc_hflags2 & SMB_FLAGS2_SECURITY_SIGNATURE)) { - vcp->vc_hflags2 |= SMB_FLAGS2_SECURITY_SIGNATURE; - smb_calcv2mackey(vcp, v2hash, - (uchar_t *)ntencpass, uniplen); - } - kmem_free(ucup, ucup_sl + 1); - kmem_free(ntlmv2_blob, ntlmv2_blob_allocsz); - break; - - case NTLMv1: - /* - * Compute the LM response, derived - * from the challenge and the ASCII - * password. (If minauth allows it.) - */ - plen_alloc = plen = 24; - encpass = kmem_zalloc(plen, KM_SLEEP); - if (minauth < SMBVOPT_MINAUTH_NTLM) { - smb_lmresponse(vcp->vc_lmhash, - vcp->vc_challenge, - (uchar_t *)encpass); - } - pp = encpass; - - /* - * Compute the NTLM response, derived from - * the challenge and the NT hash. - */ - uniplen_alloc = uniplen = 24; - ntencpass = kmem_alloc(uniplen, KM_SLEEP); - smb_lmresponse(vcp->vc_nthash, - vcp->vc_challenge, - (uchar_t *)ntencpass); - unipp = ntencpass; - - /* - * If we negotiated signing, compute the MAC key - * and start signing messages, but only on the - * first non-null session login. - */ - if ((vcp->vc_flags & SMBV_WILL_SIGN) && - !(vcp->vc_hflags2 & SMB_FLAGS2_SECURITY_SIGNATURE)) { - vcp->vc_hflags2 |= SMB_FLAGS2_SECURITY_SIGNATURE; - smb_calcmackey(vcp, vcp->vc_nthash, - (uchar_t *)ntencpass, uniplen); - } - break; - - case ClearMC: - case ClearUC: - /* - * We try w/o uppercasing first so Samba mixed case - * passwords work. If that fails, we come back and - * try uppercasing to satisfy OS/2 and Windows for - * Workgroups. - */ - plen_alloc = plen = SMB_MAXPASSWORDLEN + 1; - encpass = kmem_zalloc(plen, KM_SLEEP); - get_ascii_password(vcp, (authtype == ClearUC), encpass); - plen = strlen(encpass) + 1; - pp = encpass; - uniplen_alloc = uniplen = plen * 2; - ntencpass = kmem_alloc(uniplen, KM_SLEEP); - (void) smb_strtouni(ntencpass, smb_vc_getpass(vcp), 0, 0); - plen--; - /* - * The uniplen is zeroed because Samba cannot deal - * with this 2nd cleartext password. This Samba - * "bug" is actually a workaround for problems in - * Microsoft clients. - */ - uniplen = 0; /* -= 2 */ - unipp = ntencpass; - break; - - default: - ASSERT(0); - error = EAUTH; - goto ssn_exit; - - } /* switch authtype */ - - - error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_SESSION_SETUP_ANDX, + error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_TREE_CONNECT_ANDX, scred, &rqp); if (error) - goto bad; - - /* - * Build the request. - */ - smb_rq_wstart(rqp); - mbp = &rqp->sr_rq; - up = vcp->vc_username; - /* - * If userid is null we are attempting anonymous browse login - * so passwords must be zero length. - */ - if (*up == '\0') { - plen = uniplen = 0; - } - mb_put_uint8(mbp, 0xff); - mb_put_uint8(mbp, 0); - mb_put_uint16le(mbp, 0); - mb_put_uint16le(mbp, vcp->vc_sopt.sv_maxtx); - mb_put_uint16le(mbp, vcp->vc_sopt.sv_maxmux); - mb_put_uint16le(mbp, vcp->vc_number); - mb_put_uint32le(mbp, vcp->vc_sopt.sv_skey); - if ((SMB_DIALECT(vcp)) < SMB_DIALECT_NTLM0_12) { - mb_put_uint16le(mbp, plen); - mb_put_uint32le(mbp, 0); - smb_rq_wend(rqp); - smb_rq_bstart(rqp); - mb_put_mem(mbp, pp, plen, MB_MSYSTEM); - smb_put_dstring(mbp, vcp, up, SMB_CS_NONE); /* user */ - smb_put_dstring(mbp, vcp, ucdp, SMB_CS_NONE); /* domain */ - } else { - if (vcp->vc_intok) { - mb_put_uint16le(mbp, vcp->vc_intoklen); - mb_put_uint32le(mbp, 0); /* reserved */ - mb_put_uint32le(mbp, caps); /* my caps */ - smb_rq_wend(rqp); - smb_rq_bstart(rqp); - mb_put_mem(mbp, vcp->vc_intok, vcp->vc_intoklen, - MB_MSYSTEM); /* security blob */ - } else { - mb_put_uint16le(mbp, plen); - mb_put_uint16le(mbp, uniplen); - mb_put_uint32le(mbp, 0); /* reserved */ - mb_put_uint32le(mbp, caps); /* my caps */ - smb_rq_wend(rqp); - smb_rq_bstart(rqp); - mb_put_mem(mbp, pp, plen, MB_MSYSTEM); /* password */ - mb_put_mem(mbp, (caddr_t)unipp, uniplen, MB_MSYSTEM); - smb_put_dstring(mbp, vcp, up, SMB_CS_NONE); /* user */ - smb_put_dstring(mbp, vcp, ucdp, SMB_CS_NONE); /* dom */ - } - } - smb_put_dstring(mbp, vcp, NativeOS, SMB_CS_NONE); /* OS */ - smb_put_dstring(mbp, vcp, LanMan, SMB_CS_NONE); /* LAN Mgr */ - smb_rq_bend(rqp); - - /* - * This request should not wait for - * connection state changes, etc. - */ - rqp->sr_flags |= SMBR_INTERNAL; - error = smb_rq_simple_timed(rqp, SMBSSNSETUPTIMO); - SMBSDEBUG("%d\n", error); - if (error) { - if (rqp->sr_errclass == ERRDOS && - rqp->sr_serror == ERRnoaccess) - error = EAUTH; - if (!(rqp->sr_errclass == ERRDOS && - rqp->sr_serror == ERRmoredata)) - goto bad; - } - - /* - * Parse the reply - */ - rpflags2 = rqp->sr_rpflags2; - vcp->vc_smbuid = rqp->sr_rpuid; - smb_rq_getreply(rqp, &mdp); - error = md_get_uint8(mdp, &wc); - if (error) - goto bad; - error = EBADRPC; - if (vcp->vc_intok) { - if (wc != 4) - goto bad; - } else if (wc != 3) - goto bad; - md_get_uint8(mdp, NULL); /* secondary cmd */ - md_get_uint8(mdp, NULL); /* mbz */ - md_get_uint16le(mdp, NULL); /* andxoffset */ - md_get_uint16le(mdp, &action); /* action */ - if (vcp->vc_intok) - md_get_uint16le(mdp, &bl); /* ext security */ - md_get_uint16le(mdp, &bc); /* byte count */ - if (vcp->vc_intok) { - vcp->vc_outtoklen = bl; - vcp->vc_outtok = kmem_alloc(bl, KM_SLEEP); - error = md_get_mem(mdp, vcp->vc_outtok, bl, MB_MSYSTEM); - if (error) - goto bad; - } + return (error); /* - * Server OS, LANMGR, & Domain follow here. - * XXX: Should store these strings (later). - * - * Windows systems do not suport CAP_LARGE_... - * when signing is enabled, so adjust sv_caps. - * Match first 8 characters of server's OS - * with the UCS-2LE string: "Windows " + * Build the UNC name, i.e. "//server/share" + * but with backslashes of course. + * size math: three slashes, one null. */ - if (bc > 16) { - static const char WindowsU[16] = - "W\0i\0n\0d\0o\0w\0s\0 "; - char osbuf[16]; - - /* align(2) */ - if (((uintptr_t)mdp->md_pos) & 1) - md_get_uint8(mdp, NULL); - - bzero(osbuf, sizeof (osbuf)); - md_get_mem(mdp, osbuf, sizeof (osbuf), MB_MSYSTEM); - if (0 == bcmp(WindowsU, osbuf, sizeof (osbuf))) { - SMBSDEBUG("Server is Windows\n"); - if (vcp->vc_flags & SMBV_WILL_SIGN) { - SMBSDEBUG("disable CAP_LARGE_(r/w)\n"); - vcp->vc_sopt.sv_caps &= - ~(SMB_CAP_LARGE_READX - | SMB_CAP_LARGE_WRITEX); - } - } - } - - /* success! */ - error = 0; - -bad: + unc_len = 4 + strlen(vcp->vc_srvname) + strlen(ssp->ss_name); + unc_name = kmem_alloc(unc_len, KM_SLEEP); + snprintf(unc_name, unc_len, "\\\\%s\\%s", + vcp->vc_srvname, ssp->ss_name); /* - * When authentication fails and we're (possibly) doing - * fall-back to another method, we have to reset things. + * The password is now pre-computed in the + * user-space helper process. */ - if (error && vcp->vc_mackey) { - vcp->vc_hflags2 &= ~SMB_FLAGS2_SECURITY_SIGNATURE; - kmem_free(vcp->vc_mackey, vcp->vc_mackeylen); - vcp->vc_mackey = NULL; - vcp->vc_mackeylen = 0; - vcp->vc_seqno = 0; - } - - if (rqp) { - smb_rq_done(rqp); - rqp = NULL; - } - if (encpass) { - kmem_free(encpass, plen_alloc); - encpass = NULL; - } - if (ntencpass) { - kmem_free(ntencpass, uniplen_alloc); - ntencpass = NULL; - } + plen = ssp->ss_pwlen; + pbuf = ssp->ss_pass; /* - * Shall we try again with another auth type? - * Note: We hold no kmem here. + * Build the request. */ - switch (authtype) { - - case NullSes: - case ExtSec: - /* Error or not, we're done. (no fallback) */ - break; - - case NTLMv2: - /* - * We're doing user-level authentication (so we are actually - * sending authentication stuff over the wire), and we're - * not doing extended security, and the stuff we tried - * failed (or we we're trying to login a real user but - * got granted guest access instead.) - * - * See radar 4134676. This check works around the way a - * certain old server grants limited Guest access when we - * try NTLMv2, but works fine with NTLM. The fingerprint - * we are looking for is DOS error codes and no-Unicode. - * Note XP grants Guest access but uses Unicode and - * NT error codes. - */ - if (error == 0 && (action & SMB_ACT_GUEST) && - !(rpflags2 & SMB_FLAGS2_ERR_STATUS) && - !(rpflags2 & SMB_FLAGS2_UNICODE)) { - /* force fallback */ - declinedguest = 1; - error = EAUTH; - } - /* FALLTHROUGH */ - case NTLMv1: - case ClearMC: - if (error) { - authtype = authtype - 1; - goto again; - } - break; - - case ClearUC: - default: - /* no more fallbacks */ - break; - } - -ssn_exit: - if (error && declinedguest) - SMBERROR("we declined ntlmv2 guest access. errno will be %d\n", - error); - - return (error); -} - -int -smb_smb_ssnclose(struct smb_vc *vcp, struct smb_cred *scred) -{ - struct smb_rq *rqp; - struct mbchain *mbp; - int error; - - if (vcp->vc_smbuid == SMB_UID_UNKNOWN) - return (0); - - error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_LOGOFF_ANDX, scred, &rqp); - if (error) - return (error); mbp = &rqp->sr_rq; smb_rq_wstart(rqp); mb_put_uint8(mbp, 0xff); mb_put_uint8(mbp, 0); mb_put_uint16le(mbp, 0); + mb_put_uint16le(mbp, 0); /* Flags */ + mb_put_uint16le(mbp, plen); smb_rq_wend(rqp); smb_rq_bstart(rqp); - smb_rq_bend(rqp); - /* - * Run this with a relatively short timeout. - * We don't really care about the result, - * as we're just trying to play nice and - * "say goodbye" before we hangup. - * XXX: Add SMBLOGOFFTIMO somewhere? - */ - error = smb_rq_simple_timed(rqp, 5); - SMBSDEBUG("%d\n", error); - smb_rq_done(rqp); - return (error); -} - -static char smb_any_share[] = "?????"; - -static char * -smb_share_typename(int stype) -{ - char *pp; - - switch (stype) { - case STYPE_DISKTREE: - pp = "A:"; - break; - case STYPE_PRINTQ: - pp = smb_any_share; /* can't use LPT: here... */ - break; - case STYPE_DEVICE: - pp = "COMM"; - break; - case STYPE_IPC: - pp = "IPC"; - break; - default: - pp = smb_any_share; - break; - } - return (pp); -} -int -smb_smb_treeconnect(struct smb_share *ssp, struct smb_cred *scred) -{ - struct smb_vc *vcp; - struct smb_rq rq, *rqp = &rq; - struct mbchain *mbp; - char *pp, *pbuf, *encpass; - const char *pw; - uchar_t hash[SMB_PWH_MAX]; - int error, plen, caseopt; - int upper = 0; + /* Tree connect password, if any */ + error = mb_put_mem(mbp, pbuf, plen, MB_MSYSTEM); + if (error) + goto out; -again: - vcp = SSTOVC(ssp); + /* UNC resource name */ + error = smb_put_dstring(mbp, vcp, unc_name, SMB_CS_NONE); + if (error) + goto out; /* - * Make this a "VC-level" request, so it will have - * rqp->sr_share == NULL, and smb_iod_sendrq() - * will send it with TID = SMB_TID_UNKNOWN - * - * This also serves to bypass the wait for - * share state changes, which this call is - * trying to carry out. - * - * No longer need to set ssp->ss_tid - * here, but it's harmless enough. + * Put the type string (always ASCII), + * including the null. */ - ssp->ss_tid = SMB_TID_UNKNOWN; - error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_TREE_CONNECT_ANDX, - scred, &rqp); + tlen = strlen(ssp->ss_type_req) + 1; + error = mb_put_mem(mbp, ssp->ss_type_req, tlen, MB_MSYSTEM); if (error) - return (error); - caseopt = SMB_CS_NONE; - if (vcp->vc_sopt.sv_sm & SMB_SM_USER) { - plen = 1; - pp = ""; - pbuf = NULL; - encpass = NULL; - } else { - pbuf = kmem_alloc(SMB_MAXPASSWORDLEN + 1, KM_SLEEP); - encpass = kmem_alloc(24, KM_SLEEP); - pw = smb_share_getpass(ssp); - /* - * We try w/o uppercasing first so Samba mixed case - * passwords work. If that fails we come back and try - * uppercasing to satisfy OS/2 and Windows for Workgroups. - */ - if (upper++) { - smb_toupper(pw, pbuf, SMB_MAXPASSWORDLEN); - smb_oldlm_hash(pw, hash); - } else { - strncpy(pbuf, pw, SMB_MAXPASSWORDLEN); - smb_ntlmv1hash(pw, hash); - } - pbuf[SMB_MAXPASSWORDLEN] = '\0'; + goto out; -#ifdef NOICONVSUPPORT - /* - * We need to convert here to the server codeset. - * Initially we will send the same stuff and see what happens - * witout the conversion. REVISIT. - */ - iconv_convstr(vcp->vc_toserver, pbuf, pbuf, SMB_MAXPASSWORDLEN); -#endif - if (vcp->vc_sopt.sv_sm & SMB_SM_ENCRYPT) { - plen = 24; - smb_lmresponse(hash, - vcp->vc_challenge, - (uchar_t *)encpass); - pp = encpass; - } else { - plen = strlen(pbuf) + 1; - pp = pbuf; - } - } - mbp = &rqp->sr_rq; - smb_rq_wstart(rqp); - mb_put_uint8(mbp, 0xff); - mb_put_uint8(mbp, 0); - mb_put_uint16le(mbp, 0); - mb_put_uint16le(mbp, 0); /* Flags */ - mb_put_uint16le(mbp, plen); - smb_rq_wend(rqp); - smb_rq_bstart(rqp); - error = mb_put_mem(mbp, pp, plen, MB_MSYSTEM); - if (error) { - SMBSDEBUG("error %d from mb_put_mem for pp\n", error); - goto bad; - } - smb_put_dmem(mbp, vcp, "\\\\", 2, caseopt, NULL); - pp = vcp->vc_srvname; - error = smb_put_dmem(mbp, vcp, pp, strlen(pp), caseopt, NULL); - if (error) { - SMBSDEBUG("error %d from smb_put_dmem for srvname\n", error); - goto bad; - } - smb_put_dmem(mbp, vcp, "\\", 1, caseopt, NULL); - pp = ssp->ss_name; - error = smb_put_dstring(mbp, vcp, pp, caseopt); - if (error) { - SMBSDEBUG("error %d from smb_put_dstring for ss_name\n", error); - goto bad; - } - /* The type name is always ASCII */ - pp = smb_share_typename(ssp->ss_type); - error = mb_put_mem(mbp, pp, strlen(pp) + 1, MB_MSYSTEM); - if (error) { - SMBSDEBUG("error %d from mb_put_mem for ss_type\n", error); - goto bad; - } smb_rq_bend(rqp); + /* - * Don't want to risk missing a successful - * tree connect response. + * Run the request. + * + * Using NOINTR_RECV because we don't want to risk + * missing a successful tree connect response, + * which would "leak" Tree IDs. */ rqp->sr_flags |= SMBR_NOINTR_RECV; error = smb_rq_simple(rqp); SMBSDEBUG("%d\n", error); if (error) - goto bad; + goto out; + + /* + * Parse the TCON response + */ + smb_rq_getreply(rqp, &mdp); + md_get_uint8(mdp, &wc); + if (wc != 3) { + error = EBADRPC; + goto out; + } + md_get_uint16le(mdp, NULL); /* AndX cmd */ + md_get_uint16le(mdp, NULL); /* AndX off */ + md_get_uint16le(mdp, &options); /* option bits (DFS, search) */ + md_get_uint16le(mdp, &bcnt); /* byte count */ + + /* + * Get the returned share type string, + * i.e. "IPC" or whatever. + */ + tlen = sizeof (ssp->ss_type_ret); + bzero(ssp->ss_type_ret, tlen--); + if (tlen > bcnt) + tlen = bcnt; + md_get_mem(mdp, ssp->ss_type_ret, tlen, MB_MSYSTEM); /* Success! */ SMB_SS_LOCK(ssp); ssp->ss_tid = rqp->sr_rptid; ssp->ss_vcgenid = vcp->vc_genid; + ssp->ss_options = options; ssp->ss_flags |= SMBS_CONNECTED; SMB_SS_UNLOCK(ssp); -bad: - if (encpass) - kmem_free(encpass, 24); - if (pbuf) - kmem_free(pbuf, SMB_MAXPASSWORDLEN + 1); +out: + if (unc_name) + kmem_free(unc_name, unc_len); smb_rq_done(rqp); - if (error && upper == 1) - goto again; return (error); } @@ -1525,7 +309,6 @@ smb_smb_treedisconnect(struct smb_share *ssp, struct smb_cred *scred) { struct smb_vc *vcp; struct smb_rq *rqp; - struct mbchain *mbp; int error; if (ssp->ss_tid == SMB_TID_UNKNOWN) @@ -1543,10 +326,7 @@ smb_smb_treedisconnect(struct smb_share *ssp, struct smb_cred *scred) if (error) return (error); rqp->sr_share = ssp; /* by hand */ - mbp = &rqp->sr_rq; -#ifdef lint - mbp = mbp; -#endif + smb_rq_wstart(rqp); smb_rq_wend(rqp); smb_rq_bstart(rqp); @@ -1559,8 +339,9 @@ smb_smb_treedisconnect(struct smb_share *ssp, struct smb_cred *scred) * "leak" active tree IDs on interrupt or timeout. * The NOINTR_SEND flag makes this request immune to * interrupt or timeout until the send is done. + * Also, don't reconnect for this, of course! */ - rqp->sr_flags |= SMBR_NOINTR_SEND; + rqp->sr_flags |= (SMBR_NOINTR_SEND | SMBR_NORECONNECT); error = smb_rq_simple_timed(rqp, 5); SMBSDEBUG("%d\n", error); smb_rq_done(rqp); @@ -1632,10 +413,6 @@ smb_rwuio(struct smb_share *ssp, uint16_t fid, uio_rw_t rw, while (uiop->uio_resid > 0) { /* Lint: uio_resid may be 64-bits */ rlen = len = (uint32_t)min(maxlen, uiop->uio_resid); - - SMBSDEBUG("rw=%d, off %lld, len %d\n", - rw, uiop->uio_loffset, len); - error = (*iofun)(ssp, fid, &rlen, uiop, scred, timo); /* @@ -1688,7 +465,7 @@ smb_smb_readx(struct smb_share *ssp, uint16_t fid, uint32_t *lenp, mb_put_uint8(mbp, 0xff); /* no secondary command */ mb_put_uint8(mbp, 0); /* MBZ */ mb_put_uint16le(mbp, 0); /* offset to secondary */ - mb_put_mem(mbp, (caddr_t)&fid, sizeof (fid), MB_MSYSTEM); + mb_put_uint16le(mbp, fid); mb_put_uint32le(mbp, offlo); /* offset (low part) */ mb_put_uint16le(mbp, lenlo); /* MaxCount */ mb_put_uint16le(mbp, 1); /* MinCount */ @@ -1775,7 +552,7 @@ smb_smb_writex(struct smb_share *ssp, uint16_t fid, uint32_t *lenp, mb_put_uint8(mbp, 0xff); /* no secondary command */ mb_put_uint8(mbp, 0); /* MBZ */ mb_put_uint16le(mbp, 0); /* offset to secondary */ - mb_put_mem(mbp, (caddr_t)&fid, sizeof (fid), MB_MSYSTEM); + mb_put_uint16le(mbp, fid); mb_put_uint32le(mbp, offlo); /* offset (low part) */ mb_put_uint32le(mbp, 0); /* MBZ (timeout) */ mb_put_uint16le(mbp, 0); /* !write-thru */ @@ -1842,7 +619,7 @@ smb_smb_read(struct smb_share *ssp, uint16_t fid, uint32_t *lenp, return (error); smb_rq_getrequest(rqp, &mbp); smb_rq_wstart(rqp); - mb_put_mem(mbp, (caddr_t)&fid, sizeof (fid), MB_MSYSTEM); + mb_put_uint16le(mbp, fid); mb_put_uint16le(mbp, cnt); mb_put_uint32le(mbp, off32); mb_put_uint16le(mbp, todo); @@ -1915,7 +692,7 @@ smb_smb_write(struct smb_share *ssp, uint16_t fid, uint32_t *lenp, return (error); smb_rq_getrequest(rqp, &mbp); smb_rq_wstart(rqp); - mb_put_mem(mbp, (caddr_t)&fid, sizeof (fid), MB_MSYSTEM); + mb_put_uint16le(mbp, fid); mb_put_uint16le(mbp, cnt); mb_put_uint32le(mbp, off32); mb_put_uint16le(mbp, todo); @@ -1972,44 +749,9 @@ smb_smb_echo(struct smb_vc *vcp, struct smb_cred *scred, int timo) * this request must not wait for * connection state changes, etc. */ - rqp->sr_flags |= SMBR_INTERNAL; + rqp->sr_flags |= SMBR_NORECONNECT; error = smb_rq_simple_timed(rqp, timo); SMBSDEBUG("%d\n", error); smb_rq_done(rqp); return (error); } - -#ifdef APPLE -int -smb_smb_checkdir(struct smb_share *ssp, void *dnp, char *name, - int nmlen, struct smb_cred *scred) -{ - struct smb_rq *rqp; - struct mbchain *mbp; - int error; - - error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_CHECK_DIRECTORY, scred, &rqp); - if (error) - return (error); - - smb_rq_getrequest(rqp, &mbp); - smb_rq_wstart(rqp); - smb_rq_wend(rqp); - smb_rq_bstart(rqp); - mb_put_uint8(mbp, SMB_DT_ASCII); - /* - * All we need to do is marshall the path: "\\" - * (the root of the share) into this request. - * We essentially in-line smbfs_fullpath() here, - * except no mb_put_padbyte (already aligned). - */ - smb_put_dstring(mbp, SSTOVC(ssp), "\\", SMB_CS_NONE); - smb_rq_bend(rqp); - - error = smb_rq_simple(rqp); - SMBSDEBUG("%d\n", error); - smb_rq_done(rqp); - - return (error); -} -#endif /* APPLE */ diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/smb_subr.h b/usr/src/uts/common/fs/smbclnt/netsmb/smb_subr.h index 2ed6272a87..3edf4c4d9d 100644 --- a/usr/src/uts/common/fs/smbclnt/netsmb/smb_subr.h +++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_subr.h @@ -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. */ @@ -75,16 +75,6 @@ void m_dumpm(mblk_t *m); #endif /* DEBUG or lint */ -#define SMB_SIGMASK \ - (sigmask(SIGINT)|sigmask(SIGTERM)|sigmask(SIGKILL)| \ - sigmask(SIGHUP)|sigmask(SIGQUIT)) - -#define SMB_STRFREE(p) do { \ - if (p) \ - smb_strfree(p); \ - _NOTE(CONSTCOND) \ -} while (0) - typedef uint16_t smb_unichar; typedef smb_unichar *smb_uniptr; @@ -106,20 +96,8 @@ extern int smb_timo_append; #define EMOREDATA (0x7fff) -#ifdef APPLE -void smb_scred_init(struct smb_cred *scred, vfs_context_t vfsctx); -int smb_sigintr(vfs_context_t); -#endif -void smb_credinit(struct smb_cred *scred, struct proc *p, cred_t *cr); +void smb_credinit(struct smb_cred *scred, cred_t *cr); void smb_credrele(struct smb_cred *scred); -char *smb_strdup(const char *s); -void *smb_memdup(const void *umem, int len); -char *smb_strdupin(char *s, int maxlen); -void *smb_memdupin(void *umem, int len); -size_t smb_strtouni(uint16_t *dst, const char *src, size_t inlen, int flags); -void smb_strfree(char *s); -void smb_memfree(void *s); -void *smb_zmalloc(unsigned long size); void smb_oldlm_hash(const char *apwd, uchar_t *hash); void smb_ntlmv1hash(const char *apwd, uchar_t *hash); diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/smb_subrs.c b/usr/src/uts/common/fs/smbclnt/netsmb/smb_subrs.c index 79eba6d6ba..98963b5583 100644 --- a/usr/src/uts/common/fs/smbclnt/netsmb/smb_subrs.c +++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_subrs.c @@ -32,7 +32,10 @@ * $Id: smb_subr.c,v 1.27.108.1 2005/06/02 00:55:39 lindak 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/systm.h> @@ -90,150 +93,27 @@ smb_toupper(const char *inbuf, char *outbuf, size_t outlen) } void -smb_credinit(struct smb_cred *scred, struct proc *p, cred_t *icr) +smb_credinit(struct smb_cred *scred, cred_t *cr) { - scred->vc_pid = p->p_pidp->pid_id; - if (!icr) - icr = p->p_cred; + /* cr arg is optional */ + if (cr == NULL) + cr = ddi_get_cred(); if (is_system_labeled()) { - icr = crdup(icr); - (void) setpflags(NET_MAC_AWARE, 1, icr); + cr = crdup(cr); + (void) setpflags(NET_MAC_AWARE, 1, cr); } else { - crhold(icr); + crhold(cr); } - scred->vc_ucred = icr; + scred->scr_cred = cr; } void smb_credrele(struct smb_cred *scred) { - crfree(scred->vc_ucred); - scred->vc_ucred = NULL; -} - -#ifdef APPLE -/*ARGSUSED*/ -int -smb_sigintr(vfs_context_t vfsctx) -{ - /* - * I cannot find something to match vfs_context_issignal. - * It calls proc_pendingsignals() in Darwin code. - */ - if (vfsctx && vfs_context_issignal(vfsctx, SMB_SIGMASK)) - return (EINTR); - return (0); -} -#endif - -char * -smb_strdup(const char *s) -{ - char *p; - int len; - - len = s ? strlen(s) + 1 : 1; - p = kmem_alloc(len, KM_SLEEP); - if (s) - bcopy(s, p, len); - else - *p = 0; - return (p); -} - -/* - * duplicate string from a user space. - */ -char * -smb_strdupin(char *s, int maxlen) -{ - char *p, bt; - int len = 0; - - for (p = s; ; p++) { - if (copyin(p, &bt, 1)) - return (NULL); - len++; - if (maxlen && len > maxlen) - return (NULL); - if (bt == 0) - break; - } - p = kmem_alloc(len, KM_SLEEP); - copyin(s, p, len); - return (p); -} - -/* - * duplicate memory block from a user space. - */ -void * -smb_memdupin(void *umem, int len) -{ - char *p; - - if (len > 32 * 1024) - return (NULL); - p = kmem_alloc(len, KM_SLEEP); - if (copyin(umem, p, len) == 0) - return (p); - kmem_free(p, len); - return (NULL); -} - -/* - * duplicate memory block in the kernel space. - */ -void * -smb_memdup(const void *umem, int len) -{ - char *p; - - if (len > 32 * 1024) - return (NULL); - p = kmem_alloc(len, KM_SLEEP); - if (p == NULL) - return (NULL); - bcopy(umem, p, len); - return (p); -} - -void -smb_strfree(char *s) -{ - kmem_free(s, strlen(s) + 1); -} - -void -smb_memfree(void *s) -{ - kmem_free(s, strlen(s)); -} - -void * -smb_zmalloc(unsigned long size) -{ - void *p = kmem_zalloc(size, KM_SLEEP); - return (p); -} - -size_t -smb_strtouni(u_int16_t *dst, const char *src, size_t inlen, int flags) -{ - size_t outlen = 0; - - if (!inlen) - inlen = strlen(src); - - /* Force output format to little-endian. */ - flags &= ~UCONV_OUT_BIG_ENDIAN; - flags |= UCONV_OUT_LITTLE_ENDIAN; - - outlen = inlen * 2; - if (uconv_u8tou16((uchar_t *)src, &inlen, dst, &outlen, flags) != 0) { - outlen = 0; + if (scred->scr_cred != NULL) { + crfree(scred->scr_cred); + scred->scr_cred = NULL; } - return (outlen * 2); } /* @@ -290,7 +170,6 @@ m_dumpm(mblk_t *m) } #endif -/* all these need review XXX */ #ifndef EPROTO #define EPROTO ECONNABORTED #endif diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/smb_tran.h b/usr/src/uts/common/fs/smbclnt/netsmb/smb_tran.h index d82e3d862b..052759f333 100644 --- a/usr/src/uts/common/fs/smbclnt/netsmb/smb_tran.h +++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_tran.h @@ -32,11 +32,14 @@ * $Id: smb_tran.h,v 1.2 2001/12/21 02:41:30 conrad Exp $ */ +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + #ifndef _NETSMB_SMB_TRAN_H_ #define _NETSMB_SMB_TRAN_H_ -#pragma ident "%Z%%M% %I% %E% SMI" - #include <sys/socket.h> /* @@ -50,43 +53,36 @@ #define SMBTP_SNDSZ 1 /* R - int */ #define SMBTP_RCVSZ 2 /* R - int */ #define SMBTP_TIMEOUT 3 /* RW - struct timespec */ -#ifndef __sun -#define SMBTP_SELECTID 4 /* RW - (void *) */ -#define SMBTP_UPCALL 5 /* RW - (* void)(void *) */ -#endif struct smb_tran_ops; struct smb_tran_desc { sa_family_t tr_type; - int (*tr_create)(struct smb_vc *vcp, struct proc *p); - int (*tr_done)(struct smb_vc *vcp, struct proc *p); - int (*tr_bind)(struct smb_vc *vcp, struct sockaddr *sap, - struct proc *p); - int (*tr_connect)(struct smb_vc *vcp, struct sockaddr *sap, - struct proc *p); - int (*tr_disconnect)(struct smb_vc *vcp, struct proc *p); - int (*tr_send)(struct smb_vc *vcp, mblk_t *m0, struct proc *p); - int (*tr_recv)(struct smb_vc *vcp, mblk_t **mpp, struct proc *p); - int (*tr_poll)(struct smb_vc *vcp, int ticks, struct proc *p); + int (*tr_create)(struct smb_vc *vcp, cred_t *cr); + int (*tr_done)(struct smb_vc *vcp); + int (*tr_bind)(struct smb_vc *vcp, struct sockaddr *sap); + int (*tr_connect)(struct smb_vc *vcp, struct sockaddr *sap); + int (*tr_disconnect)(struct smb_vc *vcp); + int (*tr_send)(struct smb_vc *vcp, mblk_t *m); + int (*tr_recv)(struct smb_vc *vcp, mblk_t **mpp); + int (*tr_poll)(struct smb_vc *vcp, int ticks); + int (*tr_loan_fp)(struct smb_vc *, struct file *, cred_t *cr); int (*tr_getparam)(struct smb_vc *vcp, int param, void *data); int (*tr_setparam)(struct smb_vc *vcp, int param, void *data); int (*tr_fatal)(struct smb_vc *vcp, int error); -#ifdef notyet - int (*tr_cmpaddr)(void *addr1, void *addr2); -#endif LIST_ENTRY(smb_tran_desc) tr_link; }; typedef struct smb_tran_desc smb_tran_desc_t; -#define SMB_TRAN_CREATE(vcp, p) (vcp)->vc_tdesc->tr_create(vcp, p) -#define SMB_TRAN_DONE(vcp, p) (vcp)->vc_tdesc->tr_done(vcp, p) -#define SMB_TRAN_BIND(vcp, sap, p) (vcp)->vc_tdesc->tr_bind(vcp, sap, p) -#define SMB_TRAN_CONNECT(vcp, sap, p) (vcp)->vc_tdesc->tr_connect(vcp, sap, p) -#define SMB_TRAN_DISCONNECT(vcp, p) (vcp)->vc_tdesc->tr_disconnect(vcp, p) -#define SMB_TRAN_SEND(vcp, m0, p) (vcp)->vc_tdesc->tr_send(vcp, m0, p) -#define SMB_TRAN_RECV(vcp, m, p) (vcp)->vc_tdesc->tr_recv(vcp, m, p) -#define SMB_TRAN_POLL(vcp, t, p) (vcp)->vc_tdesc->tr_poll(vcp, t, p) +#define SMB_TRAN_CREATE(vcp, cr) (vcp)->vc_tdesc->tr_create(vcp, cr) +#define SMB_TRAN_DONE(vcp) (vcp)->vc_tdesc->tr_done(vcp) +#define SMB_TRAN_BIND(vcp, sap) (vcp)->vc_tdesc->tr_bind(vcp, sap) +#define SMB_TRAN_CONNECT(vcp, sap) (vcp)->vc_tdesc->tr_connect(vcp, sap) +#define SMB_TRAN_DISCONNECT(vcp) (vcp)->vc_tdesc->tr_disconnect(vcp) +#define SMB_TRAN_SEND(vcp, m) (vcp)->vc_tdesc->tr_send(vcp, m) +#define SMB_TRAN_RECV(vcp, m) (vcp)->vc_tdesc->tr_recv(vcp, m) +#define SMB_TRAN_POLL(vcp, t) (vcp)->vc_tdesc->tr_poll(vcp, t) +#define SMB_TRAN_LOAN_FP(vcp, f, cr) (vcp)->vc_tdesc->tr_loan_fp(vcp, f, cr) #define SMB_TRAN_GETPARAM(vcp, par, data) \ (vcp)->vc_tdesc->tr_getparam(vcp, par, data) #define SMB_TRAN_SETPARAM(vcp, par, data) \ diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/smb_trantcp.c b/usr/src/uts/common/fs/smbclnt/netsmb/smb_trantcp.c index d3036a7c98..1cd73649c4 100644 --- a/usr/src/uts/common/fs/smbclnt/netsmb/smb_trantcp.c +++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_trantcp.c @@ -84,123 +84,12 @@ static int smb_tcpsndbuf = 0x20000; static int smb_tcprcvbuf = 0x20000; -static dev_t smb_tcp_dev; - static int nbssn_recv(struct nbpcb *nbp, mblk_t **mpp, int *lenp, - uint8_t *rpcodep, struct proc *p); + uint8_t *rpcodep); static int nb_disconnect(struct nbpcb *nbp); /* - * Internal set sockopt for int-sized options. - * Is there a common Solaris function for this? - * Code from uts/common/rpc/clnt_cots.c - */ -static int -nb_setsockopt_int(TIUSER *tiptr, int level, int name, int val) -{ - int fmode; - mblk_t *mp; - struct opthdr *opt; - struct T_optmgmt_req *tor; - struct T_optmgmt_ack *toa; - int *valp; - int error, mlen; - - mlen = (sizeof (struct T_optmgmt_req) + - sizeof (struct opthdr) + sizeof (int)); - if (!(mp = allocb_cred_wait(mlen, STR_NOSIG, &error, CRED(), NOPID))) - return (error); - - mp->b_datap->db_type = M_PROTO; - /*LINTED*/ - tor = (struct T_optmgmt_req *)mp->b_wptr; - tor->PRIM_type = T_SVR4_OPTMGMT_REQ; - tor->MGMT_flags = T_NEGOTIATE; - tor->OPT_length = sizeof (struct opthdr) + sizeof (int); - tor->OPT_offset = sizeof (struct T_optmgmt_req); - mp->b_wptr += sizeof (struct T_optmgmt_req); - - /*LINTED*/ - opt = (struct opthdr *)mp->b_wptr; - opt->level = level; - opt->name = name; - opt->len = sizeof (int); - mp->b_wptr += sizeof (struct opthdr); - - /* LINTED */ - valp = (int *)mp->b_wptr; - *valp = val; - mp->b_wptr += sizeof (int); - - fmode = tiptr->fp->f_flag; - if ((error = tli_send(tiptr, mp, fmode)) != 0) - return (error); - - /* - * Wait for T_OPTMGMT_ACK - */ - mp = NULL; - fmode = 0; /* need to block */ - if ((error = tli_recv(tiptr, &mp, fmode)) != 0) - return (error); - /*LINTED*/ - toa = (struct T_optmgmt_ack *)mp->b_rptr; - if (toa->PRIM_type != T_OPTMGMT_ACK) - error = EPROTO; - freemsg(mp); - - return (error); -} - -static void -nb_setopts(struct nbpcb *nbp) -{ - int error; - TIUSER *tiptr = NULL; - - tiptr = nbp->nbp_tiptr; - if (tiptr == NULL) { - NBDEBUG("no tiptr!\n"); - return; - } - - /* - * Set various socket/TCP options. - * Failures here are not fatal - - * just log a complaint. - * - * We don't need these two: - * SO_RCVTIMEO, SO_SNDTIMEO - */ - - error = nb_setsockopt_int(tiptr, SOL_SOCKET, SO_SNDBUF, - nbp->nbp_sndbuf); - if (error) - NBDEBUG("can't set SO_SNDBUF"); - - error = nb_setsockopt_int(tiptr, SOL_SOCKET, SO_RCVBUF, - nbp->nbp_rcvbuf); - if (error) - NBDEBUG("can't set SO_RCVBUF"); - - error = nb_setsockopt_int(tiptr, SOL_SOCKET, SO_KEEPALIVE, 1); - if (error) - NBDEBUG("can't set SO_KEEPALIVE"); - - error = nb_setsockopt_int(tiptr, IPPROTO_TCP, TCP_NODELAY, 1); - if (error) - NBDEBUG("can't set TCP_NODELAY"); - - /* Set the connect timeout (in milliseconds). */ - error = nb_setsockopt_int(tiptr, IPPROTO_TCP, - TCP_CONN_ABORT_THRESHOLD, - nbp->nbp_timo.tv_sec * 1000); - if (error) - NBDEBUG("can't set connect timeout"); -} - -/* * Get mblks into *mpp until the data length is at least mlen. * Note that *mpp may already contain a fragment. * @@ -342,12 +231,14 @@ discon: static int nb_snddis(TIUSER *tiptr) { + cred_t *cr; mblk_t *mp; struct T_discon_req *dreq; int error, fmode, mlen; + cr = ddi_get_cred(); mlen = sizeof (struct T_discon_req); - if (!(mp = allocb_cred_wait(mlen, STR_NOSIG, &error, CRED(), NOPID))) + if (!(mp = allocb_cred_wait(mlen, STR_NOSIG, &error, cr, NOPID))) return (error); mp->b_datap->db_type = M_PROTO; @@ -367,14 +258,6 @@ nb_snddis(TIUSER *tiptr) return (error); } -#ifdef APPLE -static int -nb_intr(struct nbpcb *nbp, struct proc *p) -{ - return (0); -} -#endif - /* * Stuff the NetBIOS header into space already prepended. */ @@ -393,211 +276,6 @@ nb_sethdr(mblk_t *m, uint8_t type, uint32_t len) } /* - * Note: Moved name encoding into here. - */ -static int -nb_put_name(struct mbchain *mbp, struct sockaddr_nb *snb) -{ - int i, len; - uchar_t ch, *p; - - /* - * Do the NetBIOS "first-level encoding" here. - * (RFC1002 explains this wierdness...) - * See similar code in smbfs library: - * lib/libsmbfs/smb/nb_name.c - * - * Here is what we marshall: - * uint8_t NAME_LENGTH (always 32) - * uint8_t ENCODED_NAME[32] - * uint8_t SCOPE_LENGTH - * XXX Scope should follow here, then another null, - * if and when we support NetBIOS scopes. - */ - len = 1 + (2 * NB_NAMELEN) + 1; - - p = mb_reserve(mbp, len); - if (!p) - return (ENOSR); - - /* NAME_LENGTH */ - *p++ = (2 * NB_NAMELEN); - - /* ENCODED_NAME */ - for (i = 0; i < NB_NAMELEN; i++) { - ch = (uchar_t)snb->snb_name[i]; - *p++ = 'A' + ((ch >> 4) & 0xF); - *p++ = 'A' + ((ch) & 0xF); - } - - /* SCOPE_LENGTH */ - *p++ = 0; - - return (0); -} - -static int -nb_tcpopen(struct nbpcb *nbp, struct proc *p) -{ - TIUSER *tiptr; - int err, oflags = FREAD|FWRITE; - cred_t *cr = p->p_cred; - - if (!smb_tcp_dev) { - smb_tcp_dev = makedevice( - clone_major, ddi_name_to_major("tcp")); - } - - /* - * This magic arranges for our network endpoint - * to have the right "label" for operation in a - * "trusted extensions" environment. - */ - if (is_system_labeled()) { - cr = crdup(cr); - (void) setpflags(NET_MAC_AWARE, 1, cr); - } else { - crhold(cr); - } - err = t_kopen(NULL, smb_tcp_dev, oflags, &tiptr, cr); - crfree(cr); - if (err) - return (err); - - /* Note: I_PUSH "timod" is done by t_kopen */ - - /* Save the TPI handle we use everywhere. */ - nbp->nbp_tiptr = tiptr; - - /* - * Internal ktli calls need the "fmode" flags - * from the t_kopen call. XXX: Not sure if the - * flags have the right bits set, or if we - * always want the same block/non-block flags. - * XXX: Look into this... - */ - nbp->nbp_fmode = tiptr->fp->f_flag; - return (0); -} - -/*ARGSUSED*/ -static int -nb_connect_in(struct nbpcb *nbp, struct sockaddr_in *to, struct proc *p) -{ - int error; - TIUSER *tiptr = NULL; - struct t_call call; - - tiptr = nbp->nbp_tiptr; - if (tiptr == NULL) - return (EBADF); - if (nbp->nbp_flags & NBF_CONNECTED) - return (EISCONN); - - /* - * Setup (snd)call address (connect to). - * Just pass NULL for the (rcv)call. - */ - bzero(&call, sizeof (call)); - call.addr.len = sizeof (*to); - call.addr.buf = (char *)to; - /* call.opt - none */ - /* call.udata -- XXX: Should put NB session req here! */ - - /* Send the connect, wait... */ - error = t_kconnect(tiptr, &call, NULL); - if (error) { - NBDEBUG("nb_connect_in: connect %d error", error); - } else { - mutex_enter(&nbp->nbp_lock); - nbp->nbp_flags |= NBF_CONNECTED; - mutex_exit(&nbp->nbp_lock); - } - - return (error); -} - -static int -nbssn_rq_request(struct nbpcb *nbp, struct proc *p) -{ - struct mbchain mb, *mbp = &mb; - struct mdchain md, *mdp = &md; - mblk_t *m0; - struct sockaddr_in sin; - ushort_t port; - uint8_t rpcode; - int error, rplen; - - error = mb_init(mbp); - if (error) - return (error); - - /* - * Put a zero for the 4-byte NetBIOS header, - * then let nb_sethdr() overwrite it. - */ - mb_put_uint32le(mbp, 0); - nb_put_name(mbp, nbp->nbp_paddr); - nb_put_name(mbp, nbp->nbp_laddr); - nb_sethdr(mbp->mb_top, NB_SSN_REQUEST, mb_fixhdr(mbp) - 4); - - m0 = mb_detach(mbp); - error = tli_send(nbp->nbp_tiptr, m0, nbp->nbp_fmode); - m0 = NULL; /* Note: _always_ consumed by tli_send */ - mb_done(mbp); - if (error) - return (error); - - nbp->nbp_state = NBST_RQSENT; - error = nbssn_recv(nbp, &m0, &rplen, &rpcode, p); - if (error == EWOULDBLOCK) { /* Timeout */ - NBDEBUG("initial request timeout\n"); - return (ETIMEDOUT); - } - if (error) { - NBDEBUG("recv() error %d\n", error); - return (error); - } - /* - * Process NETBIOS reply - */ - if (m0) - md_initm(mdp, m0); - - error = 0; - if (rpcode == NB_SSN_POSRESP) { - mutex_enter(&nbp->nbp_lock); - nbp->nbp_state = NBST_SESSION; - mutex_exit(&nbp->nbp_lock); - goto out; - } - if (rpcode != NB_SSN_RTGRESP) { - error = ECONNABORTED; - goto out; - } - if (rplen != 6) { - error = ECONNABORTED; - goto out; - } - md_get_mem(mdp, (caddr_t)&sin.sin_addr, 4, MB_MSYSTEM); - md_get_uint16(mdp, &port); - sin.sin_port = port; - nbp->nbp_state = NBST_RETARGET; - nb_disconnect(nbp); - error = nb_connect_in(nbp, &sin, p); - if (!error) - error = nbssn_rq_request(nbp, p); - if (error) { - nb_disconnect(nbp); - } - -out: - if (m0) - md_done(mdp); - return (error); -} - -/* * Wait for up to 15 sec. for the next packet. * Often return ETIME and do nothing else. * When a packet header is available, check @@ -648,7 +326,7 @@ nbssn_peekhdr(struct nbpcb *nbp, size_t *lenp, uint8_t *rpcodep) return (EPIPE); } len &= 0x1ffff; - if (len > SMB_MAXPKTLEN) { + if (len > NB_MAXPKTLEN) { NBDEBUG("packet too long (%d)\n", len); return (EFBIG); } @@ -667,7 +345,7 @@ nbssn_peekhdr(struct nbpcb *nbp, size_t *lenp, uint8_t *rpcodep) /*ARGSUSED*/ static int nbssn_recv(struct nbpcb *nbp, mblk_t **mpp, int *lenp, - uint8_t *rpcodep, struct proc *p) + uint8_t *rpcodep) { TIUSER *tiptr = nbp->nbp_tiptr; mblk_t *m0; @@ -804,27 +482,14 @@ out: /* * SMB transport interface */ +/*ARGSUSED*/ static int -smb_nbst_create(struct smb_vc *vcp, struct proc *p) +smb_nbst_create(struct smb_vc *vcp, cred_t *cr) { struct nbpcb *nbp; - int error; nbp = kmem_zalloc(sizeof (struct nbpcb), KM_SLEEP); - /* - * We don't keep reference counts or otherwise - * prevent nbp->nbp_tiptr from going away, so - * do the TLI open here and keep it until the - * last ref calls smb_nbst_done. - * This does t_kopen (open endpoint) - */ - error = nb_tcpopen(nbp, p); - if (error) { - kmem_free(nbp, sizeof (*nbp)); - return (error); - } - nbp->nbp_timo.tv_sec = SMB_NBTIMO; nbp->nbp_state = NBST_CLOSED; /* really IDLE */ nbp->nbp_vc = vcp; @@ -833,14 +498,12 @@ smb_nbst_create(struct smb_vc *vcp, struct proc *p) mutex_init(&nbp->nbp_lock, NULL, MUTEX_DRIVER, NULL); vcp->vc_tdata = nbp; - nb_setopts(nbp); - return (0); } /*ARGSUSED*/ static int -smb_nbst_done(struct smb_vc *vcp, struct proc *p) +smb_nbst_done(struct smb_vc *vcp) { struct nbpcb *nbp = vcp->vc_tdata; @@ -856,7 +519,7 @@ smb_nbst_done(struct smb_vc *vcp, struct proc *p) if (nbp->nbp_flags & NBF_CONNECTED) nb_disconnect(nbp); if (nbp->nbp_tiptr) - t_kclose(nbp->nbp_tiptr, 1); + t_kclose(nbp->nbp_tiptr, 0); if (nbp->nbp_laddr) smb_free_sockaddr((struct sockaddr *)nbp->nbp_laddr); if (nbp->nbp_paddr) @@ -866,144 +529,58 @@ smb_nbst_done(struct smb_vc *vcp, struct proc *p) return (0); } -/*ARGSUSED*/ static int -smb_nbst_bind(struct smb_vc *vcp, struct sockaddr *sap, struct proc *p) +smb_nbst_loan_fp(struct smb_vc *vcp, struct file *fp, cred_t *cr) { struct nbpcb *nbp = vcp->vc_tdata; - struct sockaddr_nb *snb; + TIUSER *tiptr; int error = 0; - if (nbp->nbp_tiptr == NULL) - return (EBADF); - - /* - * Allow repeated bind calls on one endpoint. - * This happens with reconnect. - */ + mutex_enter(&nbp->nbp_lock); /* - * Null name is an "anonymous" (NULL) bind request. - * (Let the transport pick a local name.) - * This transport does not support NULL bind, - * because we require a local NetBIOS name. + * Un-loan the existing one, if any. */ - if (sap == NULL) - return (EINVAL); - - /*LINTED*/ - snb = (struct sockaddr_nb *)smb_dup_sockaddr(sap); - if (snb == NULL) - return (ENOMEM); - - mutex_enter(&nbp->nbp_lock); - if (nbp->nbp_laddr) - smb_free_sockaddr((struct sockaddr *)nbp->nbp_laddr); - nbp->nbp_laddr = snb; + if (nbp->nbp_tiptr != NULL) { + t_kclose(nbp->nbp_tiptr, 0); + nbp->nbp_tiptr = NULL; + nbp->nbp_flags &= ~NBF_CONNECTED; + nbp->nbp_state = NBST_CLOSED; + } /* - * Do local TCP bind with NULL (any address), - * but just once (for multiple connect attempts) - * or extra bind calls would cause errors. + * Loan the new one passed in. */ - if ((nbp->nbp_flags & NBF_LOCADDR) == 0) { - error = t_kbind(nbp->nbp_tiptr, NULL, NULL); - if (error) { - NBDEBUG("t_kbind failed"); - } else { - nbp->nbp_flags |= NBF_LOCADDR; - } + if (fp != NULL && 0 == (error = + t_kopen(fp, 0, 0, &tiptr, cr))) { + nbp->nbp_tiptr = tiptr; + nbp->nbp_fmode = tiptr->fp->f_flag; + nbp->nbp_flags |= NBF_CONNECTED; + nbp->nbp_state = NBST_SESSION; } + mutex_exit(&nbp->nbp_lock); return (error); } +/*ARGSUSED*/ static int -smb_nbst_connect(struct smb_vc *vcp, struct sockaddr *sap, struct proc *p) +smb_nbst_bind(struct smb_vc *vcp, struct sockaddr *sap) { - struct nbpcb *nbp = vcp->vc_tdata; - struct sockaddr_in sin; - struct sockaddr_nb *snb; - int error; - - if (nbp->nbp_tiptr == NULL) - return (EBADF); - if (nbp->nbp_laddr == NULL) - return (EINVAL); - - /* - * Note: nbssn_rq_request() will call nbssn_recv(), - * so set the RECVLOCK flag here. Otherwise we'll - * hit an ASSERT for this flag in nbssn_recv(). - */ - mutex_enter(&nbp->nbp_lock); - if (nbp->nbp_flags & NBF_RECVLOCK) { - NBDEBUG("attempt to reenter session layer!\n"); - mutex_exit(&nbp->nbp_lock); - return (EWOULDBLOCK); - } - nbp->nbp_flags |= NBF_RECVLOCK; - mutex_exit(&nbp->nbp_lock); - - /*LINTED*/ - snb = (struct sockaddr_nb *)smb_dup_sockaddr(sap); - if (snb == NULL) { - error = ENOMEM; - goto out; - } - if (nbp->nbp_paddr) - smb_free_sockaddr((struct sockaddr *)nbp->nbp_paddr); - nbp->nbp_paddr = snb; - - /* - * Setup the remote IP address. - * Try plain TCP first (port 445). - */ - bzero(&sin, sizeof (sin)); - sin.sin_family = AF_INET; - sin.sin_port = htons(IPPORT_SMB); /* port 445 */ - sin.sin_addr.s_addr = snb->snb_ipaddr; - -again: - NBDEBUG("trying port %d\n", ntohs(sin.sin_port)); - error = nb_connect_in(nbp, &sin, p); - switch (error) { - case 0: - break; - case ECONNREFUSED: - if (sin.sin_port != htons(IPPORT_NETBIOS_SSN)) { - /* Try again w/ NetBIOS (port 139) */ - sin.sin_port = htons(IPPORT_NETBIOS_SSN); - goto again; - } - /* FALLTHROUGH */ - default: - goto out; - } - - /* - * If we connected via NetBIOS (port 139), - * need to do a session request. - */ - if (sin.sin_port == htons(IPPORT_NETBIOS_SSN)) { - error = nbssn_rq_request(nbp, p); - if (error) - nb_disconnect(nbp); - } else - nbp->nbp_state = NBST_SESSION; - -out: - mutex_enter(&nbp->nbp_lock); - nbp->nbp_flags &= ~NBF_RECVLOCK; - mutex_exit(&nbp->nbp_lock); + return (ENOTSUP); +} - return (error); +/*ARGSUSED*/ +static int +smb_nbst_connect(struct smb_vc *vcp, struct sockaddr *sap) +{ + return (ENOTSUP); } /*ARGSUSED*/ static int -smb_nbst_disconnect(struct smb_vc *vcp, struct proc *p) +smb_nbst_disconnect(struct smb_vc *vcp) { struct nbpcb *nbp = vcp->vc_tdata; @@ -1036,7 +613,7 @@ nb_disconnect(struct nbpcb *nbp) nb_snddis(tiptr); if (nbp->nbp_state != NBST_RETARGET) { - nbp->nbp_state = NBST_CLOSED; /* really IDLE */ + nbp->nbp_state = NBST_CLOSED; } return (0); } @@ -1047,7 +624,7 @@ nb_disconnect(struct nbpcb *nbp) */ /*ARGSUSED*/ static int -smb_nbst_send(struct smb_vc *vcp, mblk_t *m, struct proc *p) +smb_nbst_send(struct smb_vc *vcp, mblk_t *m) { struct nbpcb *nbp = vcp->vc_tdata; ptrdiff_t diff; @@ -1080,6 +657,10 @@ smb_nbst_send(struct smb_vc *vcp, mblk_t *m, struct proc *p) * some network drivers will apparently send * each mblk in the chain as separate frames. * (That's arguably a driver bug.) + * + * Not bothering with allocb_cred_wait below + * because the message we're prepending to + * should already have a db_credp. */ diff = MBLKHEAD(m); @@ -1110,9 +691,8 @@ errout: return (error); } - static int -smb_nbst_recv(struct smb_vc *vcp, mblk_t **mpp, struct proc *p) +smb_nbst_recv(struct smb_vc *vcp, mblk_t **mpp) { struct nbpcb *nbp = vcp->vc_tdata; uint8_t rpcode; @@ -1126,7 +706,7 @@ smb_nbst_recv(struct smb_vc *vcp, mblk_t **mpp, struct proc *p) } nbp->nbp_flags |= NBF_RECVLOCK; mutex_exit(&nbp->nbp_lock); - error = nbssn_recv(nbp, mpp, &rplen, &rpcode, p); + error = nbssn_recv(nbp, mpp, &rplen, &rpcode); mutex_enter(&nbp->nbp_lock); nbp->nbp_flags &= ~NBF_RECVLOCK; mutex_exit(&nbp->nbp_lock); @@ -1140,7 +720,7 @@ smb_nbst_recv(struct smb_vc *vcp, mblk_t **mpp, struct proc *p) */ /*ARGSUSED*/ static int -smb_nbst_poll(struct smb_vc *vcp, int ticks, struct proc *p) +smb_nbst_poll(struct smb_vc *vcp, int ticks) { int error; int events = 0; @@ -1220,6 +800,7 @@ struct smb_tran_desc smb_tran_nbtcp_desc = { smb_nbst_send, smb_nbst_recv, smb_nbst_poll, + smb_nbst_loan_fp, smb_nbst_getparam, smb_nbst_setparam, smb_nbst_fatal, diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/smb_usr.c b/usr/src/uts/common/fs/smbclnt/netsmb/smb_usr.c index 3dffae55c8..19ef7bdc97 100644 --- a/usr/src/uts/common/fs/smbclnt/netsmb/smb_usr.c +++ b/usr/src/uts/common/fs/smbclnt/netsmb/smb_usr.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. */ @@ -44,7 +44,9 @@ #include <sys/conf.h> #include <sys/proc.h> #include <sys/fcntl.h> +#include <sys/file.h> #include <sys/socket.h> +#include <sys/sunddi.h> #include <sys/cmn_err.h> #include <netsmb/smb_osdep.h> @@ -55,483 +57,758 @@ #include <netsmb/smb_subr.h> #include <netsmb/smb_dev.h> +static int smb_cpdatain(struct mbchain *mbp, int len, char *data, int seg); + /* - * helpers for nsmb device. Can be moved to the smb_dev.c file. + * Ioctl function for SMBIOC_FLAGS2 */ -static void smb_usr_vcspec_free(struct smb_vcspec *spec); +int +smb_usr_get_flags2(smb_dev_t *sdp, intptr_t arg, int flags) +{ + struct smb_vc *vcp = NULL; + + /* This ioctl requires a session. */ + if ((vcp = sdp->sd_vc) == NULL) + return (ENOTCONN); + + /* + * Return the flags2 value. + */ + if (ddi_copyout(&vcp->vc_hflags2, (void *)arg, + sizeof (u_int16_t), flags)) + return (EFAULT); + + return (0); +} /* - * Moved the access checks here, just becuase - * this was a more convenient place to do it - * than in every function calling this. + * Ioctl function for SMBIOC_GETSSNKEY + * Size copied out is SMBIOC_HASH_SZ. + * + * The RPC library needs this for encrypting things + * like "set password" requests. This is called + * with an active RPC binding, so the connection + * will already be active (but this checks). */ -static int -smb_usr_ioc2vcspec(struct smbioc_ossn *dp, struct smb_vcspec *spec) +int +smb_usr_get_ssnkey(smb_dev_t *sdp, intptr_t arg, int flags) { - cred_t *cr = CRED(); - uid_t realuid; + struct smb_vc *vcp = NULL; + + /* This ioctl requires an active session. */ + if ((vcp = sdp->sd_vc) == NULL) + return (ENOTCONN); + if (vcp->vc_state != SMBIOD_ST_VCACTIVE) + return (ENOTCONN); /* - * Only superuser can specify a UID or GID. + * Return the session key. */ - realuid = crgetruid(cr); - if (dp->ioc_owner == SMBM_ANY_OWNER) - spec->owner = realuid; - else { - /* - * Do we have the privilege to create with the - * specified uid? (does uid == cr->cr_uid, etc.) - * MacOS would want suser(), or similar here. - */ - if (secpolicy_vnode_owner(cr, dp->ioc_owner)) - return (EPERM); - spec->owner = dp->ioc_owner; - } - if (dp->ioc_group == SMBM_ANY_GROUP) - spec->group = crgetgid(cr); - else { - /* - * Do we have the privilege to create with the - * specified gid? (one of our groups?) - */ - if (groupmember(dp->ioc_group, cr) || - secpolicy_vnode_create_gid(cr) == 0) - spec->group = dp->ioc_group; - else - return (EPERM); + if (ddi_copyout(vcp->vc_ssn_key, (void *)arg, + SMBIOC_HASH_SZ, flags)) + return (EFAULT); + + return (0); +} + +/* + * Ioctl function for SMBIOC_REQUEST + */ +int +smb_usr_simplerq(smb_dev_t *sdp, intptr_t arg, int flags, cred_t *cr) +{ + struct smb_cred scred; + struct smb_share *ssp; + smbioc_rq_t *ioc = NULL; + struct smb_rq *rqp = NULL; + struct mbchain *mbp; + struct mdchain *mdp; + uint32_t rsz; + int err, mbseg; + + /* This ioctl requires a share. */ + if ((ssp = sdp->sd_share) == NULL) + return (ENOTCONN); + + smb_credinit(&scred, cr); + ioc = kmem_alloc(sizeof (*ioc), KM_SLEEP); + if (ddi_copyin((void *) arg, ioc, sizeof (*ioc), flags)) { + err = EFAULT; + goto out; } - /* - * Valid codesets? XXX - */ - if (dp->ioc_localcs[0] == 0) { - spec->localcs = "ISO8859-1"; -#ifdef NOTYETRESOLVED - SMBERROR("no local charset ? dp->ioc_localcs[0]: %d\n", - dp->ioc_localcs[0]); - return (EINVAL); -#endif - } else - spec->localcs = spec->localcs; + /* See ddi_copyin, ddi_copyout */ + mbseg = (flags & FKIOCTL) ? MB_MSYSTEM : MB_MUSER; /* - * Check for valid sa_family. - * XXX: Just NetBIOS for now. + * Lots of SMB commands could be safe, but + * these are the only ones used by libsmbfs. */ - if (dp->ioc_server.sa.sa_family != AF_NETBIOS) - return (EINVAL); - spec->sap = &dp->ioc_server.sa; - - if (dp->ioc_local.sa.sa_family) { - /* If specified, local AF must be the same. */ - if (dp->ioc_local.sa.sa_family != - dp->ioc_server.sa.sa_family) - return (EINVAL); - spec->lap = &dp->ioc_local.sa; + switch (ioc->ioc_cmd) { + /* These are OK */ + case SMB_COM_CLOSE: + case SMB_COM_FLUSH: + case SMB_COM_NT_CREATE_ANDX: + case SMB_COM_OPEN_PRINT_FILE: + case SMB_COM_CLOSE_PRINT_FILE: + break; + + default: + err = EPERM; + goto out; } - if (dp->ioc_intok) { - spec->tok = smb_memdupin(dp->ioc_intok, dp->ioc_intoklen); - if (spec->tok == NULL) - return (EFAULT); - spec->toklen = dp->ioc_intoklen; + err = smb_rq_alloc(SSTOCP(ssp), ioc->ioc_cmd, &scred, &rqp); + if (err) + goto out; + + mbp = &rqp->sr_rq; + err = mb_put_mem(mbp, ioc->ioc_tbuf, ioc->ioc_tbufsz, mbseg); + + err = smb_rq_simple(rqp); + if (err == 0) { + /* + * This may have been an open, so save the + * generation ID of the share, which we + * check before trying read or write. + */ + sdp->sd_vcgenid = ssp->ss_vcgenid; + + /* + * Have reply data. to copyout. + * SMB header already parsed. + */ + mdp = &rqp->sr_rp; + rsz = msgdsize(mdp->md_top) - SMB_HDRLEN; + if (ioc->ioc_rbufsz < rsz) { + err = EOVERFLOW; + goto out; + } + ioc->ioc_rbufsz = rsz; + err = md_get_mem(mdp, ioc->ioc_rbuf, rsz, mbseg); + if (err) + goto out; + } - spec->srvname = dp->ioc_srvname; - spec->pass = dp->ioc_password; - spec->domain = dp->ioc_workgroup; - spec->username = dp->ioc_user; - spec->mode = dp->ioc_mode; - spec->rights = dp->ioc_rights; - spec->servercs = dp->ioc_servercs; - spec->optflags = dp->ioc_opt; + ioc->ioc_errclass = rqp->sr_errclass; + ioc->ioc_serror = rqp->sr_serror; + ioc->ioc_error = rqp->sr_error; + ddi_copyout(ioc, (void *)arg, sizeof (*ioc), flags); - return (0); -} +out: + if (rqp != NULL) + smb_rq_done(rqp); /* free rqp */ + if (ioc != NULL) + kmem_free(ioc, sizeof (*ioc)); + smb_credrele(&scred); + + return (err); -static void -smb_usr_shspec_free(struct smb_sharespec *sspec) -{ - kmem_free(sspec, sizeof (struct smb_sharespec)); } -static void -smb_usr_vcspec_free(struct smb_vcspec *spec) +/* + * Ioctl function for SMBIOC_T2RQ + */ +int +smb_usr_t2request(smb_dev_t *sdp, intptr_t arg, int flags, cred_t *cr) { + struct smb_cred scred; + struct smb_share *ssp; + smbioc_t2rq_t *ioc = NULL; + struct smb_t2rq *t2p = NULL; + struct mdchain *mdp; + int err, len, mbseg; + + /* This ioctl requires a share. */ + if ((ssp = sdp->sd_share) == NULL) + return (ENOTCONN); - if (spec->tok) { - kmem_free(spec->tok, spec->toklen); + smb_credinit(&scred, cr); + ioc = kmem_alloc(sizeof (*ioc), KM_SLEEP); + if (ddi_copyin((void *) arg, ioc, sizeof (*ioc), flags)) { + err = EFAULT; + goto out; } - kmem_free(spec, sizeof (*spec)); + + /* See ddi_copyin, ddi_copyout */ + mbseg = (flags & FKIOCTL) ? MB_MSYSTEM : MB_MUSER; + + if (ioc->ioc_setupcnt > SMBIOC_T2RQ_MAXSETUP) { + err = EINVAL; + goto out; + } + + t2p = kmem_alloc(sizeof (*t2p), KM_SLEEP); + err = smb_t2_init(t2p, SSTOCP(ssp), + ioc->ioc_setup, ioc->ioc_setupcnt, &scred); + if (err) + goto out; + len = t2p->t2_setupcount = ioc->ioc_setupcnt; + if (len > 1) + t2p->t2_setupdata = ioc->ioc_setup; + + /* This ioc member is a fixed-size array. */ + if (ioc->ioc_name[0]) { + /* Get the name length - carefully! */ + ioc->ioc_name[SMBIOC_T2RQ_MAXNAME-1] = '\0'; + t2p->t_name_len = strlen(ioc->ioc_name); + t2p->t_name = ioc->ioc_name; + } + t2p->t2_maxscount = 0; + t2p->t2_maxpcount = ioc->ioc_rparamcnt; + t2p->t2_maxdcount = ioc->ioc_rdatacnt; + + /* Transmit parameters */ + err = smb_cpdatain(&t2p->t2_tparam, + ioc->ioc_tparamcnt, ioc->ioc_tparam, mbseg); + if (err) + goto out; + + /* Transmit data */ + err = smb_cpdatain(&t2p->t2_tdata, + ioc->ioc_tdatacnt, ioc->ioc_tdata, mbseg); + if (err) + goto out; + + err = smb_t2_request(t2p); + + /* Copyout returned parameters. */ + mdp = &t2p->t2_rparam; + if (err == 0 && mdp->md_top != NULL) { + /* User's buffer large enough? */ + len = m_fixhdr(mdp->md_top); + if (len > ioc->ioc_rparamcnt) { + err = EMSGSIZE; + goto out; + } + ioc->ioc_rparamcnt = (ushort_t)len; + err = md_get_mem(mdp, ioc->ioc_rparam, len, mbseg); + if (err) + goto out; + } else + ioc->ioc_rparamcnt = 0; + + /* Copyout returned data. */ + mdp = &t2p->t2_rdata; + if (err == 0 && mdp->md_top != NULL) { + /* User's buffer large enough? */ + len = m_fixhdr(mdp->md_top); + if (len > ioc->ioc_rdatacnt) { + err = EMSGSIZE; + goto out; + } + ioc->ioc_rdatacnt = (ushort_t)len; + err = md_get_mem(mdp, ioc->ioc_rdata, len, mbseg); + if (err) + goto out; + } else + ioc->ioc_rdatacnt = 0; + + ioc->ioc_errclass = t2p->t2_sr_errclass; + ioc->ioc_serror = t2p->t2_sr_serror; + ioc->ioc_error = t2p->t2_sr_error; + ioc->ioc_rpflags2 = t2p->t2_sr_rpflags2; + + ddi_copyout(ioc, (void *)arg, sizeof (*ioc), flags); + + +out: + if (t2p != NULL) { + /* Note: t2p->t_name no longer allocated */ + smb_t2_done(t2p); + kmem_free(t2p, sizeof (*t2p)); + } + if (ioc != NULL) + kmem_free(ioc, sizeof (*ioc)); + smb_credrele(&scred); + + return (err); } +/* helper for _t2request */ static int -smb_usr_ioc2sharespec(struct smbioc_oshare *dp, struct smb_sharespec *spec) +smb_cpdatain(struct mbchain *mbp, int len, char *data, int mbseg) { - bzero(spec, sizeof (*spec)); - spec->name = dp->ioc_share; - spec->pass = dp->ioc_password; - spec->mode = dp->ioc_mode; - spec->rights = dp->ioc_rights; - spec->owner = dp->ioc_owner; - spec->group = dp->ioc_group; - spec->stype = dp->ioc_stype; - spec->optflags = dp->ioc_opt; - return (0); + int error; + + if (len == 0) + return (0); + error = mb_init(mbp); + if (error) + return (error); + return (mb_put_mem(mbp, data, len, mbseg)); } +/* + * Helper for nsmb_ioctl cases + * SMBIOC_READ, SMBIOC_WRITE + */ int -smb_usr_findvc(struct smbioc_lookup *dp, struct smb_cred *scred, - struct smb_vc **vcpp) +smb_usr_rw(smb_dev_t *sdp, int cmd, intptr_t arg, int flags, cred_t *cr) { - struct smb_vc *vcp = NULL; - struct smb_vcspec *vspec = NULL; - int error = 0; + struct smb_cred scred; + struct smb_share *ssp; + smbioc_rw_t *ioc = NULL; + struct iovec aiov[1]; + struct uio auio; + u_int16_t fh; + int err; + uio_rw_t rw; - if (dp->ioc_flags & SMBLK_CREATE) - return (EINVAL); - if (dp->ioc_level != SMBL_VC) - return (EINVAL); - vspec = kmem_zalloc(sizeof (struct smb_vcspec), KM_SLEEP); - error = smb_usr_ioc2vcspec(&dp->ioc_ssn, vspec); - if (error) + /* This ioctl requires a share. */ + if ((ssp = sdp->sd_share) == NULL) + return (ENOTCONN); + + /* After reconnect, force close+reopen */ + if (sdp->sd_vcgenid != ssp->ss_vcgenid) + return (ESTALE); + + smb_credinit(&scred, cr); + ioc = kmem_alloc(sizeof (*ioc), KM_SLEEP); + if (ddi_copyin((void *) arg, ioc, sizeof (*ioc), flags)) { + err = EFAULT; goto out; - error = smb_sm_findvc(vspec, scred, &vcp); - if (error == 0) - *vcpp = vcp; + } + + switch (cmd) { + case SMBIOC_READ: + rw = UIO_READ; + break; + case SMBIOC_WRITE: + rw = UIO_WRITE; + break; + default: + err = ENODEV; + goto out; + } + + fh = ioc->ioc_fh; + + aiov[0].iov_base = ioc->ioc_base; + aiov[0].iov_len = (size_t)ioc->ioc_cnt; + + auio.uio_iov = aiov; + auio.uio_iovcnt = 1; + auio.uio_loffset = ioc->ioc_offset; + auio.uio_segflg = (flags & FKIOCTL) ? + UIO_SYSSPACE : UIO_USERSPACE; + auio.uio_fmode = 0; + auio.uio_resid = (size_t)ioc->ioc_cnt; + + err = smb_rwuio(ssp, fh, rw, &auio, &scred, 0); + + /* + * On return ioc_cnt holds the + * number of bytes transferred. + */ + ioc->ioc_cnt -= auio.uio_resid; + + ddi_copyout(ioc, (void *)arg, sizeof (*ioc), flags); + out: - smb_usr_vcspec_free(vspec); - return (error); + if (ioc != NULL) + kmem_free(ioc, sizeof (*ioc)); + smb_credrele(&scred); + + return (err); } +/* + * Ioctl functions: SMBIOC_SSN_FIND, SMBIOC_SSN_CREATE + * Find or create a session (a.k.a. "VC" in here) + */ int -smb_usr_negotiate(struct smbioc_lookup *dp, struct smb_cred *scred, - struct smb_vc **vcpp) +smb_usr_get_ssn(smb_dev_t *sdp, int cmd, intptr_t arg, int flags, cred_t *cr) { + struct smb_cred scred; + smbioc_ossn_t *ossn = NULL; struct smb_vc *vcp = NULL; - struct smb_vcspec *vspec = NULL; - struct smb_sharespec *sspecp = NULL; int error = 0; + uid_t realuid; - if (dp->ioc_level < SMBL_VC || dp->ioc_level > SMBL_SHARE) - return (EINVAL); - vspec = kmem_zalloc(sizeof (struct smb_vcspec), KM_SLEEP); - error = smb_usr_ioc2vcspec(&dp->ioc_ssn, vspec); - if (error) - return (error); - if (dp->ioc_flags & SMBLK_CREATE) - vspec->optflags |= SMBVOPT_CREATE; - if (dp->ioc_level >= SMBL_SHARE) { - sspecp = kmem_alloc(sizeof (*sspecp), KM_SLEEP); - error = smb_usr_ioc2sharespec(&dp->ioc_sh, sspecp); - if (error) - goto out; + /* Should be no VC */ + if (sdp->sd_vc != NULL) + return (EISCONN); + + smb_credinit(&scred, cr); + ossn = kmem_alloc(sizeof (*ossn), KM_SLEEP); + if (ddi_copyin((void *)arg, ossn, sizeof (*ossn), flags)) { + error = EFAULT; + goto out; } - error = smb_sm_negotiate(vspec, scred, &vcp); - if (error == 0) { - *vcpp = vcp; + + /* + * Only superuser can specify a UID or GID. + */ + realuid = crgetruid(cr); + if (ossn->ssn_owner == SMBM_ANY_OWNER) + ossn->ssn_owner = realuid; + else { /* - * Used to copyout ioc_outtok, outtoklen here, - * but that's now in smb_dev. (our caller) - * - * If this call asked for extended security and - * the server does not support it, clear the - * flag so the caller knows this. - * - * XXX: Should just add sv_caps to ioc_ssn, - * set the new sv_caps field here, and let - * let the copyout of ioc_ssn handle it. + * Do we have the privilege to create with the + * specified uid? (does uid == cr->cr_uid, etc.) */ - if (!(vcp->vc_sopt.sv_caps & SMB_CAP_EXT_SECURITY) && - (dp->ioc_ssn.ioc_opt & SMBVOPT_EXT_SEC)) { - dp->ioc_ssn.ioc_opt &= ~SMBVOPT_EXT_SEC; - SMBSDEBUG("turned off extended security"); + if (secpolicy_vnode_owner(cr, ossn->ssn_owner)) { + error = EPERM; + goto out; } + /* ossn->ssn_owner is OK */ } -out: - smb_usr_vcspec_free(vspec); - smb_usr_shspec_free(sspecp); - return (error); -} -int -smb_usr_ssnsetup(struct smbioc_lookup *dp, struct smb_cred *scred, - struct smb_vc *vcp) -{ - struct smb_vcspec *vspec = NULL; - int error; + /* + * Make sure the strings are null terminated. + */ + ossn->ssn_srvname[SMBIOC_MAX_NAME-1] = '\0'; + ossn->ssn_id.id_domain[ SMBIOC_MAX_NAME-1] = '\0'; + ossn->ssn_id.id_user[ SMBIOC_MAX_NAME-1] = '\0'; - if (dp->ioc_level < SMBL_VC || dp->ioc_level > SMBL_SHARE) - return (EINVAL); + if (cmd == SMBIOC_SSN_CREATE) + ossn->ssn_vopt |= SMBVOPT_CREATE; + else /* FIND */ + ossn->ssn_vopt &= ~SMBVOPT_CREATE; - vspec = kmem_zalloc(sizeof (struct smb_vcspec), KM_SLEEP); - error = smb_usr_ioc2vcspec(&dp->ioc_ssn, vspec); + error = smb_vc_findcreate(ossn, &scred, &vcp); if (error) goto out; + ASSERT(vcp != NULL); - error = smb_sm_ssnsetup(vspec, scred, vcp); /* - * Moved the copyout of ioc_outtok to - * smb_dev.c (our caller) + * We have a VC, held, but not locked. + * If we're creating, mark this instance as + * an open from IOD so close can do cleanup. + * + * XXX: Would be nice to have a back pointer + * from the VC to this (IOD) sdp instance. */ + if (cmd == SMBIOC_SSN_CREATE) { + if (vcp->iod_thr != NULL) { + error = EEXIST; + goto out; + } + sdp->sd_flags |= NSMBFL_IOD; + } else { + /* + * Wait for it to finish connecting + * (or reconnect) if necessary. + */ + if (vcp->vc_state != SMBIOD_ST_VCACTIVE) { + error = smb_iod_reconnect(vcp); + if (error != 0) + goto out; + } + } + + /* + * The VC has a hold from _findvc + * which we keep until _SSN_RELE + * or nsmb_close(). + */ + sdp->sd_level = SMBL_VC; + sdp->sd_vc = vcp; + vcp = NULL; + (void) ddi_copyout(ossn, (void *)arg, sizeof (*ossn), flags); out: - smb_usr_vcspec_free(vspec); + if (vcp) { + /* Error path: rele hold from _findcreate */ + smb_vc_rele(vcp); + } + if (ossn != NULL) + kmem_free(ossn, sizeof (*ossn)); + smb_credrele(&scred); + return (error); } - +/* + * Ioctl functions: SMBIOC_SSN_RELE, SMBIOC_SSN_KILL + * Release or kill the current session. + */ int -smb_usr_tcon(struct smbioc_lookup *dp, struct smb_cred *scred, - struct smb_vc *vcp, struct smb_share **sspp) +smb_usr_drop_ssn(smb_dev_t *sdp, int cmd) { - struct smb_sharespec *sspecp = NULL; - int error; + struct smb_vc *vcp = NULL; - if (dp->ioc_level < SMBL_VC || dp->ioc_level > SMBL_SHARE) - return (EINVAL); + /* Must have a VC. */ + if ((vcp = sdp->sd_vc) == NULL) + return (ENOTCONN); - if (dp->ioc_level >= SMBL_SHARE) { - sspecp = kmem_alloc(sizeof (*sspecp), KM_SLEEP); - error = smb_usr_ioc2sharespec(&dp->ioc_sh, sspecp); - if (error) - goto out; + /* If we have a share ref, drop it too. */ + if (sdp->sd_share) { + smb_share_rele(sdp->sd_share); + sdp->sd_share = NULL; + sdp->sd_level = SMBL_VC; } - error = smb_sm_tcon(sspecp, scred, vcp, sspp); -out: - if (sspecp) - smb_usr_shspec_free(sspecp); + if (cmd == SMBIOC_SSN_KILL) + smb_vc_kill(vcp); - return (error); + /* Drop the VC ref. */ + smb_vc_rele(vcp); + sdp->sd_vc = NULL; + sdp->sd_level = 0; + + return (0); } /* - * Connect to the resource specified by smbioc_ossn structure. - * It may either find an existing connection or try to establish a new one. - * If no errors occured smb_vc returned locked and referenced. + * Find or create a tree (connected share) */ - int -smb_usr_simplerequest(struct smb_share *ssp, struct smbioc_rq *dp, - struct smb_cred *scred) +smb_usr_get_tree(smb_dev_t *sdp, int cmd, intptr_t arg, int flags, cred_t *cr) { - struct smb_rq rq, *rqp = &rq; - struct mbchain *mbp; - struct mdchain *mdp; - char *p; - size_t wc2; - u_int8_t wc; - u_int16_t bc; - int error; + struct smb_cred scred; + smbioc_tcon_t *tcon = NULL; + struct smb_vc *vcp = NULL; + struct smb_share *ssp = NULL; + int error = 0; - switch (dp->ioc_cmd) { - case SMB_COM_TRANSACTION2: - case SMB_COM_TRANSACTION2_SECONDARY: - case SMB_COM_CLOSE_AND_TREE_DISC: - case SMB_COM_TREE_CONNECT: - case SMB_COM_TREE_DISCONNECT: - case SMB_COM_NEGOTIATE: - case SMB_COM_SESSION_SETUP_ANDX: - case SMB_COM_LOGOFF_ANDX: - case SMB_COM_TREE_CONNECT_ANDX: - return (EPERM); + /* Must have a VC. */ + if ((vcp = sdp->sd_vc) == NULL) + return (ENOTCONN); + /* Should not have a share. */ + if (sdp->sd_share != NULL) + return (EISCONN); + + smb_credinit(&scred, cr); + tcon = kmem_alloc(sizeof (*tcon), KM_SLEEP); + if (ddi_copyin((void *)arg, tcon, sizeof (*tcon), flags)) { + error = EFAULT; + goto out; } - error = smb_rq_init(rqp, SSTOCP(ssp), dp->ioc_cmd, scred); - if (error) - return (error); - mbp = &rqp->sr_rq; - smb_rq_wstart(rqp); - error = mb_put_mem(mbp, dp->ioc_twords, - dp->ioc_twc * 2, MB_MUSER); - if (error) - goto bad; - smb_rq_wend(rqp); - smb_rq_bstart(rqp); - error = mb_put_mem(mbp, dp->ioc_tbytes, - dp->ioc_tbc, MB_MUSER); - if (error) - goto bad; - smb_rq_bend(rqp); - error = smb_rq_simple(rqp); + + /* + * Make sure the strings are null terminated. + */ + tcon->tc_sh.sh_name[SMBIOC_MAX_NAME-1] = '\0'; + tcon->tc_sh.sh_pass[SMBIOC_MAX_NAME-1] = '\0'; + tcon->tc_sh.sh_type_req[SMBIOC_STYPE_LEN-1] = '\0'; + bzero(tcon->tc_sh.sh_type_ret, SMBIOC_STYPE_LEN); + + if (cmd == SMBIOC_TREE_CONNECT) + tcon->tc_opt |= SMBSOPT_CREATE; + else /* FIND */ + tcon->tc_opt &= ~SMBSOPT_CREATE; + + error = smb_share_findcreate(tcon, vcp, &ssp, &scred); if (error) - goto bad; - mdp = &rqp->sr_rp; - md_get_uint8(mdp, &wc); - dp->ioc_rwc = wc; - wc2 = wc * 2; - if (wc2 > dp->ioc_rpbufsz) { - error = EBADRPC; - goto bad; + goto out; + ASSERT(ssp != NULL); + + /* + * We have a share, held, but not locked. + * If we're creating, do tree connect now, + * otherwise let that wait for a request. + */ + if (cmd == SMBIOC_TREE_CONNECT) { + error = smb_share_tcon(ssp, &scred); + if (error) + goto out; } - error = md_get_mem(mdp, dp->ioc_rpbuf, wc2, MB_MUSER); - if (error) - goto bad; - md_get_uint16le(mdp, &bc); - if ((wc2 + bc) > dp->ioc_rpbufsz) { - error = EBADRPC; - goto bad; + + /* + * Give caller the real share type from + * the tree connect response, so they can + * see if they got the requested type. + */ + memcpy(tcon->tc_sh.sh_type_ret, + ssp->ss_type_ret, SMBIOC_STYPE_LEN); + + /* + * The share has a hold from _tcon + * which we keep until nsmb_close() + * or the SMBIOC_TDIS below. + */ + sdp->sd_level = SMBL_SHARE; + sdp->sd_share = ssp; + ssp = NULL; + (void) ddi_copyout(tcon, (void *)arg, sizeof (*tcon), flags); + +out: + if (ssp) { + /* Error path: rele hold from _findcreate */ + smb_share_rele(ssp); } - dp->ioc_rbc = bc; - p = dp->ioc_rpbuf; - error = md_get_mem(mdp, p + wc2, bc, MB_MUSER); -bad: - dp->ioc_errclass = rqp->sr_errclass; - dp->ioc_serror = rqp->sr_serror; - dp->ioc_error = rqp->sr_error; - smb_rq_done(rqp); - return (error); + if (tcon) { + /* + * This structure may contain a + * cleartext password, so zap it. + */ + bzero(tcon, sizeof (*tcon)); + kmem_free(tcon, sizeof (*tcon)); + } + smb_credrele(&scred); + return (error); } -static int -smb_cpdatain(struct mbchain *mbp, int len, char *data) +/* + * Ioctl functions: SMBIOC_TREE_RELE, SMBIOC_TREE_KILL + * Release or kill the current tree + */ +int +smb_usr_drop_tree(smb_dev_t *sdp, int cmd) { - int error; + struct smb_share *ssp = NULL; - if (len == 0) - return (0); - error = mb_init(mbp); - if (error) - return (error); - return (mb_put_mem(mbp, data, len, MB_MUSER)); + /* Must have a VC and a share. */ + if (sdp->sd_vc == NULL) + return (ENOTCONN); + if ((ssp = sdp->sd_share) == NULL) + return (ENOTCONN); + + if (cmd == SMBIOC_TREE_KILL) + smb_share_kill(ssp); + + /* Drop the share ref. */ + smb_share_rele(sdp->sd_share); + sdp->sd_share = NULL; + sdp->sd_level = SMBL_VC; + + return (0); } + +/* + * Ioctl function: SMBIOC_IOD_WORK + * + * Become the reader (IOD) thread, until either the connection is + * reset by the server, or until the connection is idle longer than + * some max time. (max idle time not yet implemented) + */ int -smb_usr_t2request(struct smb_share *ssp, smbioc_t2rq_t *dp, - struct smb_cred *scred) +smb_usr_iod_work(smb_dev_t *sdp, intptr_t arg, int flags, cred_t *cr) { - struct smb_t2rq t2, *t2p = &t2; - struct mdchain *mdp; - int error, len; + struct smb_vc *vcp = NULL; + int err = 0; - if (dp->ioc_setupcnt > SMB_MAXSETUPWORDS) + /* Must have a valid session. */ + if ((vcp = sdp->sd_vc) == NULL) return (EINVAL); - error = smb_t2_init(t2p, SSTOCP(ssp), dp->ioc_setup, dp->ioc_setupcnt, - scred); - if (error) - return (error); - len = t2p->t2_setupcount = dp->ioc_setupcnt; - if (len > 1) - t2p->t2_setupdata = dp->ioc_setup; - /* This ioc member is a fixed-size array. */ - if (dp->ioc_name[0]) { - t2p->t_name_maxlen = SMBIOC_T2RQ_MAXNAME; - t2p->t_name = kmem_alloc(t2p->t_name_maxlen, KM_SLEEP); - bcopy(dp->ioc_name, t2p->t_name, t2p->t_name_maxlen); - /* Get the string length - carefully! */ - t2p->t_name[t2p->t_name_maxlen - 1] = '\0'; - t2p->t_name_len = strlen(t2p->t_name); + if (vcp->vc_flags & SMBV_GONE) + return (EINVAL); + + /* + * Is there already an IOD for this VC? + * (Should never happen.) + */ + SMB_VC_LOCK(vcp); + if (vcp->iod_thr == NULL) + vcp->iod_thr = curthread; + else + err = EEXIST; + SMB_VC_UNLOCK(vcp); + if (err) + return (err); + + /* + * Copy the "work" state, etc. into the VC + * The MAC key is copied separately. + */ + if (ddi_copyin((void *)arg, &vcp->vc_work, + sizeof (smbioc_ssn_work_t), flags)) { + err = EFAULT; + goto out; } - t2p->t2_maxscount = 0; - t2p->t2_maxpcount = dp->ioc_rparamcnt; - t2p->t2_maxdcount = dp->ioc_rdatacnt; - error = smb_cpdatain(&t2p->t2_tparam, dp->ioc_tparamcnt, - dp->ioc_tparam); - if (error) - goto bad; - error = smb_cpdatain(&t2p->t2_tdata, - dp->ioc_tdatacnt, dp->ioc_tdata); - if (error) - goto bad; - error = smb_t2_request(t2p); - dp->ioc_errclass = t2p->t2_sr_errclass; - dp->ioc_serror = t2p->t2_sr_serror; - dp->ioc_error = t2p->t2_sr_error; - dp->ioc_rpflags2 = t2p->t2_sr_rpflags2; - if (error) - goto bad; - mdp = &t2p->t2_rparam; - if (mdp->md_top) { - mblk_t *m = mdp->md_top; -#ifdef lint - m = m; -#endif - len = m_fixhdr(mdp->md_top); - if (len > dp->ioc_rparamcnt) { - error = EMSGSIZE; - goto bad; - } - dp->ioc_rparamcnt = (ushort_t)len; - error = md_get_mem(mdp, dp->ioc_rparam, - len, MB_MUSER); - if (error) { - goto bad; - } - } else - dp->ioc_rparamcnt = 0; - mdp = &t2p->t2_rdata; - if (mdp->md_top) { - mblk_t *m = mdp->md_top; -#ifdef lint - m = m; -#endif - len = m_fixhdr(mdp->md_top); - if (len > dp->ioc_rdatacnt) { - error = EMSGSIZE; - goto bad; - } - dp->ioc_rdatacnt = (ushort_t)len; - error = md_get_mem(mdp, dp->ioc_rdata, - len, MB_MUSER); - if (error) { - goto bad; + if (vcp->vc_u_maclen) { + vcp->vc_mackeylen = vcp->vc_u_maclen; + vcp->vc_mackey = kmem_alloc(vcp->vc_mackeylen, KM_SLEEP); + if (ddi_copyin(vcp->vc_u_mackey.lp_ptr, vcp->vc_mackey, + vcp->vc_mackeylen, flags)) { + err = EFAULT; + goto out; } - } else - dp->ioc_rdatacnt = 0; -bad: - if (t2p->t_name) { - kmem_free(t2p->t_name, t2p->t_name_maxlen); - t2p->t_name = NULL; } - smb_t2_done(t2p); - return (error); + + err = smb_iod_vc_work(vcp, cr); + + /* Caller wants state here. */ + vcp->vc_work.wk_out_state = vcp->vc_state; + + (void) ddi_copyout(&vcp->vc_work, (void *)arg, + sizeof (smbioc_ssn_work_t), flags); + +out: + if (vcp->vc_mackey) { + kmem_free(vcp->vc_mackey, vcp->vc_mackeylen); + vcp->vc_mackey = NULL; + vcp->vc_mackeylen = 0; + } + + /* + * The IOD thread is leaving the driver. Clear iod_thr, + * and wake up anybody waiting for us to quit. + */ + SMB_VC_LOCK(vcp); + vcp->iod_thr = NULL; + cv_broadcast(&vcp->vc_statechg); + SMB_VC_UNLOCK(vcp); + + return (err); } /* - * Helper for nsmb_ioctl cases - * SMBIOC_READ, SMBIOC_WRITE + * Ioctl functions: SMBIOC_IOD_IDLE, SMBIOC_IOD_RCFAIL + * + * Wait for user-level requests to be enqueued on this session, + * and then return to the user-space helper, which will then + * initiate a reconnect, etc. */ int -smb_usr_rw(struct smb_share *ssp, smbioc_rw_t *rwrq, - int cmd, struct smb_cred *scred) +smb_usr_iod_ioctl(smb_dev_t *sdp, int cmd, intptr_t arg, int flags) { - struct iovec aiov[1]; - struct uio auio; - u_int16_t fh; - int error; - uio_rw_t rw; + struct smb_vc *vcp = NULL; + int err = 0; + + /* Must have a valid session. */ + if ((vcp = sdp->sd_vc) == NULL) + return (EINVAL); + if (vcp->vc_flags & SMBV_GONE) + return (EINVAL); + + /* + * Is there already an IOD for this VC? + * (Should never happen.) + */ + SMB_VC_LOCK(vcp); + if (vcp->iod_thr == NULL) + vcp->iod_thr = curthread; + else + err = EEXIST; + SMB_VC_UNLOCK(vcp); + if (err) + return (err); + + /* nothing to copyin */ switch (cmd) { - case SMBIOC_READ: - rw = UIO_READ; + case SMBIOC_IOD_IDLE: + err = smb_iod_vc_idle(vcp); break; - case SMBIOC_WRITE: - rw = UIO_WRITE; + + case SMBIOC_IOD_RCFAIL: + err = smb_iod_vc_rcfail(vcp); break; + default: - return (ENODEV); + err = ENOTTY; + goto out; } - fh = htoles(rwrq->ioc_fh); - - aiov[0].iov_base = rwrq->ioc_base; - aiov[0].iov_len = (size_t)rwrq->ioc_cnt; - - auio.uio_iov = aiov; - auio.uio_iovcnt = 1; - auio.uio_loffset = rwrq->ioc_offset; - auio.uio_segflg = UIO_USERSPACE; - auio.uio_fmode = 0; - auio.uio_resid = (size_t)rwrq->ioc_cnt; - - error = smb_rwuio(ssp, fh, rw, &auio, scred, 0); + /* Both of these ioctls copy out the new state. */ + (void) ddi_copyout(&vcp->vc_state, (void *)arg, + sizeof (int), flags); +out: /* - * On return ioc_cnt holds the - * number of bytes transferred. + * The IOD thread is leaving the driver. Clear iod_thr, + * and wake up anybody waiting for us to quit. */ - rwrq->ioc_cnt -= auio.uio_resid; + SMB_VC_LOCK(vcp); + vcp->iod_thr = NULL; + cv_broadcast(&vcp->vc_statechg); + SMB_VC_UNLOCK(vcp); - return (error); + return (err); } diff --git a/usr/src/uts/common/fs/smbclnt/netsmb/subr_mchain.c b/usr/src/uts/common/fs/smbclnt/netsmb/subr_mchain.c index 5ea38af704..525beb671d 100644 --- a/usr/src/uts/common/fs/smbclnt/netsmb/subr_mchain.c +++ b/usr/src/uts/common/fs/smbclnt/netsmb/subr_mchain.c @@ -31,13 +31,12 @@ * * $FreeBSD: src/sys/kern/subr_mchain.c,v 1.1 2001/02/24 15:44:29 bp Exp $ */ + /* - * 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" - #include <sys/param.h> #include <sys/systm.h> #include <sys/errno.h> @@ -46,6 +45,7 @@ #include <sys/stream.h> #include <sys/strsun.h> #include <sys/strsubr.h> +#include <sys/sunddi.h> #include <sys/cmn_err.h> #ifdef APPLE @@ -127,17 +127,10 @@ */ /* - * uio_isuserspace - return non zero value if the address space + * uio_isuserspace - non zero value if the address space * flag is for a user address space (could be 32 or 64 bit). */ -int -uio_isuserspace(uio_t *a_uio) -{ - if (a_uio->uio_segflg == UIO_USERSPACE) { - return (1); - } - return (0); -} +#define uio_isuserspace(uio) (uio->uio_segflg == UIO_USERSPACE) /* * uio_curriovbase - return the base address of the current iovec associated @@ -198,10 +191,10 @@ uio_update(uio_t *a_uio, size_t a_count) a_uio->uio_resid = 0; } if (a_count > (size_t)a_uio->uio_resid) { - a_uio->uio_offset += a_uio->uio_resid; + a_uio->uio_loffset += a_uio->uio_resid; a_uio->uio_resid = 0; } else { - a_uio->uio_offset += a_count; + a_uio->uio_loffset += a_count; a_uio->uio_resid -= a_count; } } @@ -216,7 +209,10 @@ uio_update(uio_t *a_uio, size_t a_count) } } - +/* + * This is now used only to extend an existing mblk chain, + * so don't need to use allocb_cred_wait here. + */ /*ARGSUSED*/ mblk_t * m_getblk(int size, int type) @@ -265,12 +261,17 @@ mb_initm(struct mbchain *mbp, mblk_t *m) int mb_init(struct mbchain *mbp) { + cred_t *cr; mblk_t *mblk; + int error; - mblk = m_getblk(MLEN, 1); - if (mblk == NULL) { - return (ENOSR); - } + /* + * This message will be the head of a new mblk chain, + * so we'd like its db_credp set. If we extend this + * chain later, we'll just use allocb_wait() + */ + cr = ddi_get_cred(); + mblk = allocb_cred_wait(MLEN, STR_NOSIG, &error, cr, NOPID); /* * Leave room in this first mblk so we can @@ -302,7 +303,7 @@ mb_detach(struct mbchain *mbp) /* * Returns the length of the mblk_t data. - * + * Should be m_totlen() perhaps? */ int m_fixhdr(mblk_t *m0) @@ -364,70 +365,115 @@ mb_reserve(struct mbchain *mbp, int size) * All mb_put_*() functions perform an actual copy of the data into mbuf * chain. Functions which have le or be suffixes will perform conversion to * the little- or big-endian data formats. - * XXX: Assumes total data length in previous mblks is EVEN. - * XXX: Might need to compute the offset from mb_top instead. + * + * Inline version of mb_put_mem(). Handles the easy case in-line, + * and calls mb_put_mem() if crossing mblk boundaries, etc. + * + * We build with -xspace, which causes these inline functions + * to not be inlined. Using macros instead for now. + */ +#ifdef INLINE_WORKS + +static inline int +mb_put_inline(struct mbchain *mbp, void *src, int size) +{ + mblk_t *m = mbp->mb_cur; + + if (m != NULL && size <= MBLKTAIL(m)) { + uchar_t *p = src; + int n = size; + while (n--) + *(m->b_wptr)++ = *p++; + mbp->mb_count += size; + return (0); + } + return (mb_put_mem(mbp, src, size, MB_MINLINE)); +} +#define MB_PUT_INLINE(MBP, SRC, SZ) \ + return (mb_put_inline(MBP, SRC, SZ)) + +#else /* INLINE_WORKS */ + +#define MB_PUT_INLINE(MBP, SRC, SZ) \ + mblk_t *m = MBP->mb_cur; \ + if (m != NULL && SZ <= MBLKTAIL(m)) { \ + uchar_t *p = (void *) SRC; \ + int n = SZ; \ + while (n--) \ + *(m->b_wptr)++ = *p++; \ + MBP->mb_count += SZ; \ + return (0); \ + } \ + return (mb_put_mem(MBP, SRC, SZ, MB_MINLINE)) + +#endif /* INLINE_WORKS */ + +/* + * Assumes total data length in previous mblks is EVEN. + * Might need to compute the offset from mb_top instead. */ int mb_put_padbyte(struct mbchain *mbp) { - caddr_t dst; - char x = 0; - - dst = (caddr_t)mbp->mb_cur->b_wptr; + uintptr_t dst; + char v = 0; + dst = (uintptr_t)mbp->mb_cur->b_wptr; /* only add padding if address is odd */ - if ((long)dst & 1) - return (mb_put_mem(mbp, (caddr_t)&x, 1, MB_MSYSTEM)); - else - return (0); + if (dst & 1) { + MB_PUT_INLINE(mbp, &v, sizeof (v)); + } + + return (0); } int mb_put_uint8(struct mbchain *mbp, u_int8_t x) { - return (mb_put_mem(mbp, (caddr_t)&x, sizeof (x), MB_MSYSTEM)); + u_int8_t v = x; + MB_PUT_INLINE(mbp, &v, sizeof (v)); } int mb_put_uint16be(struct mbchain *mbp, u_int16_t x) { - x = htobes(x); - return (mb_put_mem(mbp, (caddr_t)&x, sizeof (x), MB_MSYSTEM)); + u_int16_t v = htobes(x); + MB_PUT_INLINE(mbp, &v, sizeof (v)); } int mb_put_uint16le(struct mbchain *mbp, u_int16_t x) { - x = htoles(x); - return (mb_put_mem(mbp, (caddr_t)&x, sizeof (x), MB_MSYSTEM)); + u_int16_t v = htoles(x); + MB_PUT_INLINE(mbp, &v, sizeof (v)); } int mb_put_uint32be(struct mbchain *mbp, u_int32_t x) { - x = htobel(x); - return (mb_put_mem(mbp, (caddr_t)&x, sizeof (x), MB_MSYSTEM)); + u_int32_t v = htobel(x); + MB_PUT_INLINE(mbp, &v, sizeof (v)); } int mb_put_uint32le(struct mbchain *mbp, u_int32_t x) { - x = htolel(x); - return (mb_put_mem(mbp, (caddr_t)&x, sizeof (x), MB_MSYSTEM)); + u_int32_t v = htolel(x); + MB_PUT_INLINE(mbp, &v, sizeof (v)); } int mb_put_uint64be(struct mbchain *mbp, u_int64_t x) { - x = htobeq(x); - return (mb_put_mem(mbp, (caddr_t)&x, sizeof (x), MB_MSYSTEM)); + u_int64_t v = htobeq(x); + MB_PUT_INLINE(mbp, &v, sizeof (v)); } int mb_put_uint64le(struct mbchain *mbp, u_int64_t x) { - x = htoleq(x); - return (mb_put_mem(mbp, (caddr_t)&x, sizeof (x), MB_MSYSTEM)); + u_int64_t v = htoleq(x); + MB_PUT_INLINE(mbp, &v, sizeof (v)); } /* @@ -436,15 +482,14 @@ mb_put_uint64le(struct mbchain *mbp, u_int64_t x) * to perform a copy */ int -mb_put_mem(struct mbchain *mbp, c_caddr_t source, int size, int type) +mb_put_mem(struct mbchain *mbp, const void *vsrc, int size, int type) { - mblk_t *m, *n; - caddr_t dst; + mblk_t *n, *m = mbp->mb_cur; + c_caddr_t source = vsrc; c_caddr_t src; - int cplen, error, mleft, count; + caddr_t dst; uint64_t diff; - - m = mbp->mb_cur; + int cplen, mleft, count; diff = MBLKTAIL(m); ASSERT(diff == (uint64_t)((int)diff)); @@ -477,15 +522,11 @@ mb_put_mem(struct mbchain *mbp, c_caddr_t source, int size, int type) *dst++ = *src++; break; case MB_MSYSTEM: - /* - * Try copying the raw bytes instead of using bcopy() - */ bcopy(source, dst, cplen); break; case MB_MUSER: - error = copyin((void *)source, dst, cplen); - if (error) - return (error); + if (copyin((void *)source, dst, cplen)) + return (EFAULT); break; case MB_MZERO: bzero(dst, cplen); @@ -523,16 +564,15 @@ mb_put_mbuf(struct mbchain *mbp, mblk_t *m) * copies a uio scatter/gather list to an mbuf chain. */ int -mb_put_uio(struct mbchain *mbp, uio_t *uiop, int size) +mb_put_uio(struct mbchain *mbp, uio_t *uiop, size_t size) { - int left; + size_t left; int mtype, error; mtype = (uio_isuserspace(uiop) ? MB_MUSER : MB_MSYSTEM); - while (size > 0 && uiop->uio_resid) { - if (uiop->uio_iovcnt <= 0 || uio_curriovbase(uiop) == - USER_ADDR_NULL) + if (uiop->uio_iovcnt <= 0 || + uio_curriovbase(uiop) == USER_ADDR_NULL) return (EFBIG); left = uio_curriovlen(uiop); if (left > size) @@ -550,17 +590,6 @@ mb_put_uio(struct mbchain *mbp, uio_t *uiop, int size) /* * Routines for fetching data from an mbuf chain */ -int -md_init(struct mdchain *mdp) -{ - mblk_t *m; - - m = m_getblk(MLEN, 1); - if (m == NULL) - return (ENOBUFS); - md_initm(mdp, m); - return (0); -} void md_initm(struct mdchain *mdp, mblk_t *m) @@ -634,43 +663,81 @@ md_next_record(struct mdchain *mdp) return (0); } -int -md_get_uint8(struct mdchain *mdp, u_int8_t *x) -{ - return (md_get_mem(mdp, (char *)x, 1, MB_MINLINE)); -} +/* + * Inline version of md_get_mem(). Handles the easy case in-line, + * and calls md_get_mem() if crossing mblk boundaries, etc. + */ +#ifdef INLINE_WORKS /* see above */ -int -md_get_uint16(struct mdchain *mdp, u_int16_t *x) +static inline int +md_get_inline(struct mdchain *mdp, void *dst, int size) { - return (md_get_mem(mdp, (char *)x, 2, MB_MINLINE)); + mblk_t *m = mdp->md_cur; + + if (m != NULL && mdp->md_pos + size <= m->b_wptr) { + uchar_t *p = dst; + int n = size; + while (n--) + *p++ = *(mdp->md_pos)++; + /* no md_count += size */ + return (0); + } + return (md_get_mem(mdp, dst, size, MB_MINLINE)); } +#define MD_GET_INLINE(MDP, DST, SZ) \ + error = md_get_inline(MDP, DST, SZ) + +#else /* INLINE_WORKS */ + +/* Note, sets variable: error */ +#define MD_GET_INLINE(MDP, DST, SZ) \ + mblk_t *m = MDP->md_cur; \ + if (m != NULL && MDP->md_pos + SZ <= m->b_wptr) { \ + uchar_t *p = (void *) DST; \ + int n = SZ; \ + while (n--) \ + *p++ = *(mdp->md_pos)++; \ + /* no md_count += SZ */ \ + error = 0; \ + } else \ + error = md_get_mem(MDP, DST, SZ, MB_MINLINE) + +#endif /* INLINE_WORKS */ + int -md_get_uint16le(struct mdchain *mdp, u_int16_t *x) +md_get_uint8(struct mdchain *mdp, u_int8_t *x) { - u_int16_t v; - int error = md_get_uint16(mdp, &v); + uint8_t v; + int error; + MD_GET_INLINE(mdp, &v, sizeof (v)); if (x) - *x = letohs(v); + *x = v; return (error); } int md_get_uint16be(struct mdchain *mdp, u_int16_t *x) { u_int16_t v; - int error = md_get_uint16(mdp, &v); + int error; + MD_GET_INLINE(mdp, &v, sizeof (v)); if (x) *x = betohs(v); return (error); } int -md_get_uint32(struct mdchain *mdp, u_int32_t *x) +md_get_uint16le(struct mdchain *mdp, u_int16_t *x) { - return (md_get_mem(mdp, (caddr_t)x, 4, MB_MINLINE)); + u_int16_t v; + int error; + + MD_GET_INLINE(mdp, &v, sizeof (v)); + if (x) + *x = letohs(v); + return (error); } int @@ -679,7 +746,7 @@ md_get_uint32be(struct mdchain *mdp, u_int32_t *x) u_int32_t v; int error; - error = md_get_uint32(mdp, &v); + MD_GET_INLINE(mdp, &v, sizeof (v)); if (x) *x = betohl(v); return (error); @@ -691,25 +758,19 @@ md_get_uint32le(struct mdchain *mdp, u_int32_t *x) u_int32_t v; int error; - error = md_get_uint32(mdp, &v); + MD_GET_INLINE(mdp, &v, sizeof (v)); if (x) *x = letohl(v); return (error); } int -md_get_uint64(struct mdchain *mdp, u_int64_t *x) -{ - return (md_get_mem(mdp, (caddr_t)x, 8, MB_MINLINE)); -} - -int md_get_uint64be(struct mdchain *mdp, u_int64_t *x) { u_int64_t v; int error; - error = md_get_uint64(mdp, &v); + MD_GET_INLINE(mdp, &v, sizeof (v)); if (x) *x = betohq(v); return (error); @@ -721,20 +782,20 @@ md_get_uint64le(struct mdchain *mdp, u_int64_t *x) u_int64_t v; int error; - error = md_get_uint64(mdp, &v); + MD_GET_INLINE(mdp, &v, sizeof (v)); if (x) *x = letohq(v); return (error); } int -md_get_mem(struct mdchain *mdp, caddr_t target, int size, int type) +md_get_mem(struct mdchain *mdp, void *vdst, int size, int type) { mblk_t *m = mdp->md_cur; - int error; - int count; + caddr_t target = vdst; unsigned char *s; uint64_t diff; + int count; while (size > 0) { if (m == NULL) { @@ -773,9 +834,8 @@ md_get_mem(struct mdchain *mdp, caddr_t target, int size, int type) continue; switch (type) { case MB_MUSER: - error = copyout(s, (void *)target, count); - if (error) - return (error); + if (copyout(s, target, count)) + return (EFAULT); break; case MB_MSYSTEM: bcopy(s, target, count); @@ -821,7 +881,7 @@ md_get_mbuf(struct mdchain *mdp, int size, mblk_t **ret) } int -md_get_uio(struct mdchain *mdp, uio_t *uiop, int size) +md_get_uio(struct mdchain *mdp, uio_t *uiop, size_t size) { size_t left; int mtype, error; diff --git a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs.h b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs.h index 4770ee6e84..9eb6cdbed3 100644 --- a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs.h +++ b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs.h @@ -48,11 +48,21 @@ * but that's now in sys/fs/smbfs_mount.h */ +#include <sys/param.h> +#include <sys/fstyp.h> #include <sys/list.h> #include <sys/vfs.h> -#include <sys/vfs_opreg.h> #include <sys/fs/smbfs_mount.h> +/* + * Path component length + * + * The generic fs code uses MAXNAMELEN to represent + * what the largest component length is, but note: + * that length DOES include the terminating NULL. + * SMB_MAXFNAMELEN does NOT include the NULL. + */ +#define SMB_MAXFNAMELEN (MAXNAMELEN-1) /* 255 */ /* * SM_MAX_STATFSTIME is the maximum time to cache statvfs data. Since this @@ -82,6 +92,16 @@ struct smb_share; #define SMI_LLOCK 0x80 /* local locking only */ /* + * Stuff returned by smbfs_smb_qfsattr + * See [CIFS] SMB_QUERY_FS_ATTRIBUTE_INFO + */ +typedef struct smb_fs_attr_info { + uint32_t fsa_aflags; /* Attr. flags [CIFS 4.1.6.6] */ + uint32_t fsa_maxname; /* max. component length */ + char fsa_tname[FSTYPSZ]; /* type name, i.e. "NTFS" */ +} smb_fs_attr_info_t; + +/* * Corresponds to Darwin: struct smbmount */ typedef struct smbmntinfo { @@ -90,11 +110,12 @@ typedef struct smbmntinfo { struct smb_share *smi_share; /* netsmb SMB share conn data */ kmutex_t smi_lock; /* mutex for flags, etc. */ uint32_t smi_flags; /* NFS-derived flag bits */ - uint32_t smi_fsattr; /* acls & streams opts */ uint32_t smi_status; /* status bits for this mount */ hrtime_t smi_statfstime; /* sm_statvfsbuf cache time */ statvfs64_t smi_statvfsbuf; /* cached statvfs data */ kcondvar_t smi_statvfs_cv; + smb_fs_attr_info_t smi_fsa; /* SMB FS attributes. */ +#define smi_fsattr smi_fsa.fsa_aflags /* * Kstat statistics diff --git a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_acl.c b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_acl.c index 88665cfd1b..e50e3b2389 100644 --- a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_acl.c +++ b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_acl.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" - /* * ACL support for smbfs */ @@ -89,7 +87,7 @@ smbfs_getsd(vnode_t *vp, uint32_t selector, mblk_t **mp, cred_t *cr) /* Shared lock for (possible) n_fid use. */ if (smbfs_rw_enter_sig(&np->r_lkserlock, RW_READER, SMBINTR(vp))) return (EINTR); - smb_credinit(&scred, curproc, cr); + smb_credinit(&scred, cr); error = smbfs_smb_tmpopen(np, rights, &scred, &fid); if (error) @@ -162,7 +160,7 @@ smbfs_setsd(vnode_t *vp, uint32_t selector, mblk_t **mp, cred_t *cr) /* Shared lock for (possible) n_fid use. */ if (smbfs_rw_enter_sig(&np->r_lkserlock, RW_READER, SMBINTR(vp))) return (EINTR); - smb_credinit(&scred, curproc, cr); + smb_credinit(&scred, cr); error = smbfs_smb_tmpopen(np, rights, &scred, &fid); if (error) diff --git a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_client.c b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_client.c index 368d5e2487..fc089d4c33 100644 --- a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_client.c +++ b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_client.c @@ -18,16 +18,15 @@ * * CDDL HEADER END */ + /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * Copyright (c) 1983,1984,1985,1986,1987,1988,1989 AT&T. * All rights reserved. */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include <sys/param.h> #include <sys/systm.h> #include <sys/thread.h> @@ -212,42 +211,35 @@ smbfs_zonelist_remove(smbmntinfo_t *smi) mutex_exit(&smg->smg_lock); } +#ifdef lint +#define NEED_SMBFS_CALLBACKS 1 +#endif #ifdef NEED_SMBFS_CALLBACKS /* * Call-back hooks for netsmb, in case we want them. * Apple's VFS wants them. We may not need them. - * - * I thought I could use the "dead" callback from netsmb - * to set the SMI_DEAD flag, but that looks like it will - * interfere with the zone shutdown mechanisms. */ +/*ARGSUSED*/ static void smbfs_dead(smb_share_t *ssp) { -#if 0 /* see above */ - smbmntinfo_t *smi = ssp->ss_mount; - if (smi) { - mutex_enter(&smi->smi_lock); - smi->smi_flags |= SMI_DEAD; - mutex_exit(&smi->smi_lock); - } -#endif -} - -static void smbfs_down(smb_share_t *ss) -{ - /* no-op */ + /* + * Walk the mount list, finding all mounts + * using this share... + */ } -static void smbfs_up(smb_share_t *ss) +/*ARGSUSED*/ +static void smbfs_cb_nop(smb_share_t *ss) { /* no-op */ } smb_fscb_t smbfs_cb = { - .fscb_dead = smbfs_dead, - .fscb_down = smbfs_down, - .fscb_up = smbfs_up }; + .fscb_disconn = smbfs_dead, + .fscb_connect = smbfs_cb_nop, + .fscb_down = smbfs_cb_nop, + .fscb_up = smbfs_cb_nop }; #endif /* NEED_SMBFS_CALLBACKS */ diff --git a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_node.h b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_node.h index 98adac6c21..6dc1396094 100644 --- a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_node.h +++ b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_node.h @@ -33,15 +33,13 @@ */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _FS_SMBFS_NODE_H_ #define _FS_SMBFS_NODE_H_ -#pragma ident "%Z%%M% %I% %E% SMI" - /* * Much code copied into here from Sun NFS. */ @@ -225,6 +223,7 @@ typedef struct smbnode { struct smbfs_fctx *n_dirseq; /* ff context */ long n_dirofs; /* last ff offset */ long n_direof; /* End of dir. offset. */ + int n_vcgenid; /* gereration no. (reconnect) */ int n_fidrefs; uint16_t n_fid; /* file handle */ uint32_t n_rights; /* granted rights */ diff --git a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_smb.c b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_smb.c index 95019bf34f..8e06faa612 100644 --- a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_smb.c +++ b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_smb.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. */ @@ -41,6 +41,7 @@ #include <sys/systm.h> #include <sys/time.h> #include <sys/vnode.h> +#include <sys/sunddi.h> #include <sys/cmn_err.h> #ifdef APPLE @@ -113,6 +114,10 @@ smbfs_smb_lockandx(struct smbnode *np, int op, uint32_t pid, /* Shared lock for n_fid use below. */ ASSERT(smbfs_rw_lock_held(&np->r_lkserlock, RW_READER)); + /* After reconnect, n_fid is invalid */ + if (np->n_vcgenid != ssp->ss_vcgenid) + return (ESTALE); + if (op == SMB_LOCK_SHARED) ltype |= SMB_LOCKING_ANDX_SHARED_LOCK; if (largelock) @@ -125,7 +130,7 @@ smbfs_smb_lockandx(struct smbnode *np, int op, uint32_t pid, mb_put_uint8(mbp, 0xff); /* secondary command */ mb_put_uint8(mbp, 0); /* MBZ */ mb_put_uint16le(mbp, 0); - mb_put_mem(mbp, (caddr_t)&np->n_fid, 2, MB_MSYSTEM); + mb_put_uint16le(mbp, np->n_fid); mb_put_uint8(mbp, ltype); /* locktype */ mb_put_uint8(mbp, 0); /* oplocklevel - 0 seems is NO_OPLOCK */ mb_put_uint32le(mbp, timeout); /* 0 nowait, -1 infinite wait */ @@ -301,14 +306,14 @@ top: } md_get_uint32le(mdp, &size); fap->fa_size = size; - md_get_uint32(mdp, NULL); /* allocation size */ + md_get_uint32le(mdp, NULL); /* allocation size */ md_get_uint16le(mdp, &wattr); fap->fa_attr = wattr; break; case SMB_QFILEINFO_ALL_INFO: timesok = 0; /* creation time (discard) */ - md_get_uint64(mdp, NULL); + md_get_uint64le(mdp, NULL); /* last access time */ md_get_uint64le(mdp, &llongint); if (llongint) { @@ -384,6 +389,10 @@ smbfs_smb_qfileinfo(struct smbnode *np, struct smbfattr *fap, */ ASSERT(np->r_lkserlock.count != 0); + /* After reconnect, n_fid is invalid */ + if (np->n_vcgenid != ssp->ss_vcgenid) + return (ESTALE); + if (np->n_fid == SMB_FID_UNUSED) return (EBADF); @@ -400,7 +409,7 @@ top: else infolevel = SMB_QFILEINFO_ALL_INFO; } - mb_put_mem(mbp, (caddr_t)&np->n_fid, 2, MB_MSYSTEM); + mb_put_uint16le(mbp, np->n_fid); mb_put_uint16le(mbp, infolevel); t2p->t2_maxpcount = 2; t2p->t2_maxdcount = vcp->vc_txmax; @@ -436,14 +445,14 @@ top: } md_get_uint32le(mdp, &size); fap->fa_size = size; - md_get_uint32(mdp, NULL); /* allocation size */ + md_get_uint32le(mdp, NULL); /* allocation size */ md_get_uint16le(mdp, &wattr); fap->fa_attr = wattr; break; case SMB_QFILEINFO_ALL_INFO: timesok = 0; /* creation time (discard) */ - md_get_uint64(mdp, NULL); + md_get_uint64le(mdp, NULL); /* last access time */ md_get_uint64le(mdp, &llongint); if (llongint) { @@ -497,20 +506,18 @@ top: /* * Support functions for _qstreaminfo - * See smbfs_xattr.c + * Moved to smbfs_xattr.c */ int -smbfs_smb_qfsattr(struct smb_share *ssp, uint32_t *attrp, - struct smb_cred *scrp) +smbfs_smb_qfsattr(struct smb_share *ssp, struct smb_fs_attr_info *fsa, + struct smb_cred *scrp) { struct smb_t2rq *t2p; struct mbchain *mbp; struct mdchain *mdp; - uint32_t nlen; int error; - char *fs_name; /* will malloc whatever the size is */ - struct smbfs_fctx ctx; + uint32_t nlen; error = smb_t2_alloc(SSTOCP(ssp), SMB_TRANS2_QUERY_FS_INFORMATION, scrp, &t2p); @@ -527,35 +534,44 @@ smbfs_smb_qfsattr(struct smb_share *ssp, uint32_t *attrp, return (error); } mdp = &t2p->t2_rdata; - md_get_uint32le(mdp, attrp); - md_get_uint32le(mdp, &ssp->ss_maxfilenamelen); + md_get_uint32le(mdp, &fsa->fsa_aflags); + md_get_uint32le(mdp, &fsa->fsa_maxname); md_get_uint32le(mdp, &nlen); /* fs name length */ - if (ssp->ss_fsname == NULL && nlen) { - ctx.f_ssp = ssp; - ctx.f_name = kmem_alloc(nlen, KM_SLEEP); - ctx.f_namesz = nlen; - md_get_mem(mdp, ctx.f_name, nlen, MB_MSYSTEM); - ctx.f_nmlen = nlen; - smbfs_fname_tolocal(&ctx); - fs_name = kmem_alloc(ctx.f_nmlen+1, KM_SLEEP); - bcopy(ctx.f_name, fs_name, ctx.f_nmlen); - fs_name[ctx.f_nmlen] = '\0'; - ssp->ss_fsname = fs_name; - kmem_free(ctx.f_name, ctx.f_namesz); - /* - * If fs_name isn't NTFS they probably require resume keys. - * This is another example of the client trying to fix a server - * bug. This code uses the logic created by PR-3983209. See - * long block comment in smbfs_smb_findnextLM2. - */ - if (strcmp(fs_name, "NTFS")) { - SMB_SS_LOCK(ssp); - ssp->ss_flags |= SMBS_RESUMEKEYS; - SMB_SS_UNLOCK(ssp); - } - SMBVDEBUG("(fyi) share '%s', attr 0x%x, maxfilename %d\n", - ssp->ss_fsname, *attrp, ssp->ss_maxfilenamelen); + + /* + * Get the FS type name. + */ + bzero(fsa->fsa_tname, FSTYPSZ); + if (SMB_UNICODE_STRINGS(SSTOVC(ssp))) { + uint16_t tmpbuf[FSTYPSZ]; + size_t tmplen, outlen; + + if (nlen > sizeof (tmpbuf)) + nlen = sizeof (tmpbuf); + md_get_mem(mdp, tmpbuf, nlen, MB_MSYSTEM); + tmplen = nlen / 2; /* UCS-2 chars */ + outlen = FSTYPSZ - 1; + uconv_u16tou8(tmpbuf, &tmplen, + (uchar_t *)fsa->fsa_tname, &outlen, + UCONV_IN_LITTLE_ENDIAN); + } else { + if (nlen > (FSTYPSZ - 1)) + nlen = FSTYPSZ - 1; + md_get_mem(mdp, fsa->fsa_tname, nlen, MB_MSYSTEM); } + + /* + * If fs_name isn't NTFS they probably require resume keys. + * This is another example of the client trying to fix a server + * bug. This code uses the logic created by PR-3983209. See + * long block comment in smbfs_smb_findnextLM2. + */ + if (strcmp(fsa->fsa_tname, "NTFS")) { + SMB_SS_LOCK(ssp); + ssp->ss_flags |= SMBS_RESUMEKEYS; + SMB_SS_UNLOCK(ssp); + } + smb_t2_done(t2p); return (0); } @@ -601,7 +617,7 @@ smbfs_smb_statfsLM2(struct smb_share *ssp, statvfs64_t *sbp, return (error); } mdp = &t2p->t2_rdata; - md_get_uint32(mdp, NULL); /* fs id */ + md_get_uint32le(mdp, NULL); /* fs id */ md_get_uint32le(mdp, &bpu); md_get_uint32le(mdp, &units); md_get_uint32le(mdp, &funits); @@ -713,7 +729,7 @@ smbfs_smb_seteof(struct smb_share *ssp, uint16_t fid, uint64_t newsize, return (error); mbp = &t2p->t2_tparam; mb_init(mbp); - mb_put_mem(mbp, (caddr_t)&fid, 2, MB_MSYSTEM); + mb_put_uint16le(mbp, fid); if (vcp->vc_sopt.sv_caps & SMB_CAP_INFOLEVEL_PASSTHRU) mb_put_uint16le(mbp, SMB_SFILEINFO_END_OF_FILE_INFORMATION); else @@ -747,6 +763,10 @@ smbfs_smb_t2rename(struct smbnode *np, struct smbnode *tdnp, /* Shared lock for n_fid use below. */ ASSERT(smbfs_rw_lock_held(&np->r_lkserlock, RW_READER)); + /* After reconnect, n_fid is invalid */ + if (np->n_vcgenid != ssp->ss_vcgenid) + return (ESTALE); + if (!(vcp->vc_sopt.sv_caps & SMB_CAP_INFOLEVEL_PASSTHRU)) return (ENOTSUP); error = smb_t2_alloc(SSTOCP(ssp), SMB_TRANS2_SET_FILE_INFORMATION, @@ -761,13 +781,13 @@ smbfs_smb_t2rename(struct smbnode *np, struct smbnode *tdnp, } mbp = &t2p->t2_tparam; mb_init(mbp); - mb_put_mem(mbp, (caddr_t)&np->n_fid, 2, MB_MSYSTEM); + mb_put_uint16le(mbp, np->n_fid); mb_put_uint16le(mbp, SMB_SFILEINFO_RENAME_INFORMATION); mb_put_uint16le(mbp, 0); /* reserved, nowadays */ mbp = &t2p->t2_tdata; mb_init(mbp); mb_put_uint32le(mbp, overwrite); - mb_put_mem(mbp, (caddr_t)&fid, 2, MB_MSYSTEM); /* base for tname */ + mb_put_uint16le(mbp, fid); /* base for tname */ mb_put_uint16le(mbp, 0); /* part of a 32bit fid? */ ucslenp = (int32_t *)mb_reserve(mbp, sizeof (int32_t)); mbp->mb_count = 0; @@ -805,12 +825,16 @@ smbfs_smb_flush(struct smbnode *np, struct smb_cred *scrp) if (np->n_fidrefs == 0) return (0); /* not open */ + /* After reconnect, n_fid is invalid */ + if (np->n_vcgenid != ssp->ss_vcgenid) + return (ESTALE); + error = smb_rq_init(rqp, SSTOCP(ssp), SMB_COM_FLUSH, scrp); if (error) return (error); smb_rq_getrequest(rqp, &mbp); smb_rq_wstart(rqp); - mb_put_mem(mbp, (caddr_t)&np->n_fid, 2, MB_MSYSTEM); + mb_put_uint16le(mbp, np->n_fid); smb_rq_wend(rqp); smb_rq_bstart(rqp); smb_rq_bend(rqp); @@ -865,7 +889,7 @@ smbfs_smb_setfsize(struct smbnode *np, uint16_t fid, uint64_t newsize, return (error); smb_rq_getrequest(rqp, &mbp); smb_rq_wstart(rqp); - mb_put_mem(mbp, (caddr_t)&fid, 2, MB_MSYSTEM); + mb_put_uint16le(mbp, fid); mb_put_uint16le(mbp, 0); mb_put_uint32le(mbp, newsize); mb_put_uint16le(mbp, 0); @@ -1273,7 +1297,7 @@ smbfs_smb_setftime1( tzoff = SSTOVC(ssp)->vc_sopt.sv_tz; smb_rq_getrequest(rqp, &mbp); smb_rq_wstart(rqp); - mb_put_mem(mbp, (caddr_t)&fid, 2, MB_MSYSTEM); + mb_put_uint16le(mbp, fid); mb_put_uint32le(mbp, 0); /* creation time */ if (atime) @@ -1320,7 +1344,7 @@ smbfs_smb_setfattrNT(struct smbnode *np, uint16_t fid, svtz = SSTOVC(ssp)->vc_sopt.sv_tz; mbp = &t2p->t2_tparam; mb_init(mbp); - mb_put_mem(mbp, (caddr_t)&fid, 2, MB_MSYSTEM); + mb_put_uint16le(mbp, fid); if (vcp->vc_sopt.sv_caps & SMB_CAP_INFOLEVEL_PASSTHRU) mb_put_uint16le(mbp, SMB_SFILEINFO_BASIC_INFORMATION); else @@ -1461,7 +1485,7 @@ smbfs_smb_ntcreatex(struct smbnode *np, uint32_t rights, md_get_uint8(mdp, NULL); /* mbz */ md_get_uint16le(mdp, NULL); /* andxoffset */ md_get_uint8(mdp, NULL); /* oplock lvl granted */ - md_get_uint16(mdp, &fid); /* yes, leaving it LE */ + md_get_uint16le(mdp, &fid); /* file ID */ md_get_uint32le(mdp, &createact); /* create_action */ md_get_uint64le(mdp, &llongint); /* creation time */ md_get_uint64le(mdp, &llongint); /* access time */ @@ -1634,7 +1658,7 @@ smbfs_smb_oldopen(struct smbnode *np, int accmode, struct smb_cred *scrp, error = EBADRPC; break; } - md_get_uint16(mdp, &fid); /* yes, we leave it LE */ + md_get_uint16le(mdp, &fid); md_get_uint16le(mdp, &wattr); fap.fa_attr = wattr; /* @@ -1703,15 +1727,19 @@ int smbfs_smb_tmpopen(struct smbnode *np, uint32_t rights, struct smb_cred *scrp, uint16_t *fidp) { - struct smb_vc *vcp = SSTOVC(np->n_mount->smi_share); + struct smb_share *ssp = np->n_mount->smi_share; + struct smb_vc *vcp = SSTOVC(ssp); enum vtype vt = VREG; int error; /* Shared lock for n_fid use below. */ ASSERT(smbfs_rw_lock_held(&np->r_lkserlock, RW_READER)); + /* Can we re-use n_fid? or must we open anew? */ mutex_enter(&np->r_statelock); - if (np->n_fidrefs && (rights & np->n_rights) == rights) { + if (np->n_fidrefs > 0 && + np->n_vcgenid == ssp->ss_vcgenid && + (rights & np->n_rights) == rights) { np->n_fidrefs++; *fidp = np->n_fid; mutex_exit(&np->r_statelock); @@ -1731,14 +1759,6 @@ smbfs_smb_tmpopen(struct smbnode *np, uint32_t rights, struct smb_cred *scrp, NULL, NULL); } - if (*fidp == np->n_fid) { - /* - * Oh no, the server gave us the same FID again? - * This will cause us to close np->n_fid early! - */ - SMBVDEBUG("duplicate fid: 0x%x\n", *fidp); - } - return (error); } @@ -1797,10 +1817,7 @@ smbfs_smb_open(struct smbnode *np, uint32_t rights, struct smb_cred *scrp, error = smbfs_smb_oldopen(np, smb_rights2mode(rights), scrp, attrcacheupdated, fidp, name, nmlen, xattr, sizep, rightsp); } -#if 0 /* let caller do this */ - if (!error && !name) - np->n_fidrefs++; -#endif + return (error); } @@ -1818,7 +1835,7 @@ smbfs_smb_close(struct smb_share *ssp, uint16_t fid, struct timespec *mtime, return (error); smb_rq_getrequest(rqp, &mbp); smb_rq_wstart(rqp); - mb_put_mem(mbp, (caddr_t)&fid, sizeof (fid), MB_MSYSTEM); + mb_put_uint16le(mbp, fid); if (mtime) { smb_time_local2server(mtime, SSTOVC(ssp)->vc_sopt.sv_tz, &time); } else @@ -1900,7 +1917,7 @@ smbfs_smb_oldcreate(struct smbnode *dnp, const char *name, int nmlen, smb_rq_getreply(rqp, &mdp); md_get_uint8(mdp, &wc); if (wc == 1) - md_get_uint16(mdp, fidp); + md_get_uint16le(mdp, fidp); else error = EBADRPC; } @@ -2327,7 +2344,7 @@ smbfs_smb_trans2find2(struct smbfs_fctx *ctx) ctx->f_t2 = t2p; mbp = &t2p->t2_tparam; mb_init(mbp); - mb_put_mem(mbp, (caddr_t)&ctx->f_Sid, 2, MB_MSYSTEM); + mb_put_uint16le(mbp, ctx->f_Sid); mb_put_uint16le(mbp, ctx->f_limit); mb_put_uint16le(mbp, ctx->f_infolevel); if (ctx->f_ssp->ss_flags & SMBS_RESUMEKEYS) { @@ -2366,7 +2383,7 @@ smbfs_smb_trans2find2(struct smbfs_fctx *ctx) return (error); mdp = &t2p->t2_rparam; if (ctx->f_flags & SMBFS_RDD_FINDFIRST) { - if ((error = md_get_uint16(mdp, &ctx->f_Sid)) != 0) + if ((error = md_get_uint16le(mdp, &ctx->f_Sid)) != 0) return (error); ctx->f_flags &= ~SMBFS_RDD_FINDFIRST; } @@ -2420,7 +2437,7 @@ smbfs_smb_findclose2(struct smbfs_fctx *ctx) return (error); smb_rq_getrequest(rqp, &mbp); smb_rq_wstart(rqp); - mb_put_mem(mbp, (caddr_t)&ctx->f_Sid, 2, MB_MSYSTEM); + mb_put_uint16le(mbp, ctx->f_Sid); smb_rq_wend(rqp); smb_rq_bstart(rqp); smb_rq_bend(rqp); @@ -2438,10 +2455,10 @@ smbfs_smb_findopenLM2(struct smbfs_fctx *ctx, struct smbnode *dnp, { ctx->f_type = ft_LM2; - ctx->f_namesz = SMB_MAXFNAMELEN; + ctx->f_namesz = SMB_MAXFNAMELEN + 1; if (SMB_UNICODE_STRINGS(SSTOVC(ctx->f_ssp))) ctx->f_namesz *= 2; - ctx->f_name = kmem_zalloc(ctx->f_namesz, KM_SLEEP); + ctx->f_name = kmem_alloc(ctx->f_namesz, KM_SLEEP); ctx->f_infolevel = SMB_DIALECT(SSTOVC(ctx->f_ssp)) < SMB_DIALECT_NTLM0_12 ? SMB_FIND_STANDARD : SMB_FIND_BOTH_DIRECTORY_INFO; @@ -2495,7 +2512,7 @@ again: smb_dos2unixtime(date, time, 0, svtz, &ctx->f_attr.fa_mtime); md_get_uint32le(mdp, &size); ctx->f_attr.fa_size = size; - md_get_uint32(mdp, NULL); /* allocation size */ + md_get_uint32le(mdp, NULL); /* allocation size */ md_get_uint16le(mdp, &wattr); ctx->f_attr.fa_attr = wattr; md_get_uint8(mdp, &tb); @@ -2507,7 +2524,7 @@ again: case SMB_FIND_BOTH_DIRECTORY_INFO: md_get_uint32le(mdp, &next); md_get_uint32le(mdp, &resumekey); /* file index (resume key) */ - md_get_uint64(mdp, NULL); /* creation time */ + md_get_uint64le(mdp, NULL); /* creation time */ md_get_uint64le(mdp, &llongint); smb_time_NT2local(llongint, svtz, &ctx->f_attr.fa_atime); md_get_uint64le(mdp, &llongint); @@ -2516,7 +2533,7 @@ again: smb_time_NT2local(llongint, svtz, &ctx->f_attr.fa_ctime); md_get_uint64le(mdp, &llongint); /* file size */ ctx->f_attr.fa_size = llongint; - md_get_uint64(mdp, NULL); /* real size (should use) */ + md_get_uint64le(mdp, NULL); /* real size (should use) */ /* freebsd bug: fa_attr endian bug */ md_get_uint32le(mdp, &dattr); /* extended file attributes */ ctx->f_attr.fa_attr = dattr; @@ -2541,10 +2558,11 @@ again: nmlen = min(size, SMB_MAXFNAMELEN * 2); else nmlen = min(size, SMB_MAXFNAMELEN); - if (ctx->f_name) - kmem_free(ctx->f_name, ctx->f_namesz); - cp = ctx->f_name = kmem_alloc(nmlen, KM_SLEEP); - ctx->f_namesz = nmlen; + + /* Allocated f_name in findopen */ + ASSERT(nmlen < ctx->f_namesz); + cp = ctx->f_name; + error = md_get_mem(mdp, cp, nmlen, MB_MSYSTEM); if (error) return (error); @@ -2663,10 +2681,7 @@ smbfs_smb_findopen(struct smbnode *dnp, const char *wild, int wlen, struct smbfs_fctx *ctx; int error; - ctx = kmem_alloc(sizeof (*ctx), KM_SLEEP); - if (ctx == NULL) - return (ENOMEM); - bzero(ctx, sizeof (*ctx)); + ctx = kmem_zalloc(sizeof (*ctx), KM_SLEEP); ctx->f_flags = SMBFS_RDD_FINDFIRST; ctx->f_dnp = dnp; @@ -2876,7 +2891,7 @@ smbfs_smb_getsec_m(struct smb_share *ssp, uint16_t fid, /* Parameters part */ mbp = &ntp->nt_tparam; mb_init(mbp); - mb_put_mem(mbp, (caddr_t)&fid, 2, MB_MSYSTEM); + mb_put_uint16le(mbp, fid); mb_put_uint16le(mbp, 0); /* reserved */ mb_put_uint32le(mbp, selector); /* Data part (none) */ @@ -2904,7 +2919,7 @@ smbfs_smb_getsec_m(struct smb_share *ssp, uint16_t fid, */ mdp = &ntp->nt_rdata; if (mdp->md_top == NULL) { - SMBVDEBUG("null md_top? fid 0x%x\n", letohs(fid)); + SMBVDEBUG("null md_top? fid 0x%x\n", fid); error = EBADRPC; goto done; } @@ -2917,7 +2932,7 @@ smbfs_smb_getsec_m(struct smb_share *ssp, uint16_t fid, len = m_fixhdr(mdp->md_top); if (len != *reslen) { SMBVDEBUG("len %d *reslen %d fid 0x%x\n", - len, *reslen, letohs(fid)); + len, *reslen, fid); } /* @@ -3023,7 +3038,7 @@ int smbfs_smb_setsec_m(struct smb_share *ssp, uint16_t fid, /* Parameters part */ mbp = &ntp->nt_tparam; mb_init(mbp); - mb_put_mem(mbp, (caddr_t)&fid, 2, MB_MSYSTEM); + mb_put_uint16le(mbp, fid); mb_put_uint16le(mbp, 0); /* reserved */ mb_put_uint32le(mbp, selector); diff --git a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr.c b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr.c index eaf3fb2238..c94b4dddff 100644 --- a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr.c +++ b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr.c @@ -33,12 +33,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" - #include <sys/param.h> #include <sys/systm.h> #include <sys/time.h> @@ -343,48 +341,55 @@ smbfs_fullpath(struct mbchain *mbp, struct smb_vc *vcp, struct smbnode *dnp, return (error); } +/* + * Convert a Unicode directory entry to UTF-8 + */ void smbfs_fname_tolocal(struct smbfs_fctx *ctx) { - int length; + uchar_t tmpbuf[SMB_MAXFNAMELEN+1]; struct smb_vc *vcp = SSTOVC(ctx->f_ssp); uchar_t *dst; const ushort_t *src; size_t inlen, outlen; - int flags = 0; + int flags; if (ctx->f_nmlen == 0) return; - /* XXX: This is temporary, right? Need iconv... */ if (!SMB_UNICODE_STRINGS(vcp)) return; + if (ctx->f_namesz < sizeof (tmpbuf)) { + ASSERT(0); + goto errout; + } + /* - * In Unix, the UTF-8 name can be larger and - * in-place conversions are not supported. - * Note: 3,9 are the maximum UTF-8 expansion - * factors when converting strings from UTF-16 - * XXX: This was removed. REVISIT + * In-place conversions are not supported, + * so convert into tmpbuf and copy. */ - if (SMB_UNICODE_STRINGS(vcp)) - length = ctx->f_nmlen * 9; /* why 9 */ - else - length = ctx->f_nmlen * 3; /* why 3 */ - length = min(length, SMB_MAXFNAMELEN); - - dst = kmem_zalloc(length, KM_SLEEP); - outlen = length; + dst = tmpbuf; + outlen = SMB_MAXFNAMELEN; /*LINTED*/ src = (const ushort_t *)ctx->f_name; - inlen = ctx->f_nmlen / 2; /* need number of UCS-2 characters */ - flags |= UCONV_IN_LITTLE_ENDIAN; - - if (uconv_u16tou8(src, &inlen, dst, &outlen, flags) == 0) { - kmem_free(ctx->f_name, ctx->f_namesz); - ctx->f_name = (char *)dst; - ctx->f_namesz = length; - ctx->f_nmlen = (int)outlen; - } else - kmem_free(dst, length); + inlen = ctx->f_nmlen / 2; /* number of UCS-2 characters */ + flags = UCONV_IN_LITTLE_ENDIAN; + + if (uconv_u16tou8(src, &inlen, dst, &outlen, flags) != 0) + goto errout; + + ASSERT(outlen < sizeof (tmpbuf)); + tmpbuf[outlen] = '\0'; + bcopy(tmpbuf, ctx->f_name, outlen + 1); + ctx->f_nmlen = (int)outlen; + return; + +errout: + /* + * Conversion failed, but our caller does not + * deal with errors here, so... (hack). + * Don't expect to ever see this. + */ + strlcpy(ctx->f_name + 1, "?", ctx->f_namesz); } diff --git a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr.h b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr.h index b2382d5438..877f6e53c3 100644 --- a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr.h +++ b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_subr.h @@ -33,15 +33,13 @@ */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _FS_SMBFS_SMBFS_SUBR_H_ #define _FS_SMBFS_SMBFS_SUBR_H_ -#pragma ident "%Z%%M% %I% %E% SMI" - #include <sys/cmn_err.h> #include <netsmb/mchain.h> @@ -150,14 +148,13 @@ typedef struct smbfs_fctx smbfs_fctx_t; #define f_rq f_urq.uf_rq #define f_t2 f_urq.uf_t2 - /* * smb level */ int smbfs_smb_lock(struct smbnode *np, int op, caddr_t id, offset_t start, uint64_t len, int largelock, struct smb_cred *scrp, uint32_t timeout); -int smbfs_smb_qfsattr(struct smb_share *ssp, uint32_t *attrp, +int smbfs_smb_qfsattr(struct smb_share *ssp, struct smb_fs_attr_info *, struct smb_cred *scrp); int smbfs_smb_statfs(struct smb_share *ssp, statvfs64_t *sbp, struct smb_cred *scrp); diff --git a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_vfsops.c b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_vfsops.c index f86518902c..b616543a92 100644 --- a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_vfsops.c +++ b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_vfsops.c @@ -33,12 +33,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" - #include <sys/systm.h> #include <sys/cred.h> #include <sys/vfs.h> @@ -119,9 +117,11 @@ static mntopts_t smbfs_mntopts = { mntopts }; +static const char fs_type_name[FSTYPSZ] = "smbfs"; + static vfsdef_t vfw = { VFSDEF_VERSION, - "smbfs", /* type name string */ + (char *)fs_type_name, smbfsinit, /* init routine */ VSW_HASPROTO|VSW_NOTZONESAFE, /* flags */ &smbfs_mntopts /* mount options table prototype */ @@ -129,7 +129,7 @@ static vfsdef_t vfw = { static struct modlfs modlfs = { &mod_fsops, - "SMBFS filesystem v" SMBFS_VER_STR, + "SMBFS filesystem", &vfw }; @@ -386,16 +386,7 @@ smbfs_mount(vfs_t *vfsp, vnode_t *mvp, struct mounta *uap, cred_t *cr) return (error); } - /* - * We don't have data structures to support multiple mounts of - * the same share object by the same owner, so don't allow it. - */ - if (ssp->ss_mount != NULL) { - smb_share_rele(ssp); - return (EBUSY); - } - - smb_credinit(&scred, curproc, cr); + smb_credinit(&scred, cr); /* * Use "goto errout" from here on. @@ -458,7 +449,6 @@ proceed: smi = kmem_zalloc(sizeof (smbmntinfo_t), KM_SLEEP); smi->smi_share = ssp; - ssp->ss_mount = smi; smi->smi_zone = mntzone; smi->smi_flags = SMI_LLOCK; @@ -481,7 +471,7 @@ proceed: * Get attributes of the remote file system, * i.e. ACL support, named streams, etc. */ - error = smbfs_smb_qfsattr(ssp, &smi->smi_fsattr, &scred); + error = smbfs_smb_qfsattr(ssp, &smi->smi_fsa, &scred); if (error) { SMBVDEBUG("smbfs_smb_qfsattr error %d\n", error); } @@ -729,6 +719,7 @@ smbfs_statvfs(vfs_t *vfsp, statvfs64_t *sbp) recheck: now = gethrtime(); if (now < smi->smi_statfstime) { + error = 0; goto cache_hit; } @@ -753,46 +744,45 @@ recheck: /* * Do the OTW call. Note: lock NOT held. */ - smb_credinit(&scred, curproc, NULL); + smb_credinit(&scred, NULL); bzero(&stvfs, sizeof (stvfs)); error = smbfs_smb_statfs(ssp, &stvfs, &scred); smb_credrele(&scred); + if (error) { + SMBVDEBUG("statfs error=%d\n", error); + } else { + + /* + * Set a few things the OTW call didn't get. + */ + stvfs.f_frsize = stvfs.f_bsize; + stvfs.f_favail = stvfs.f_ffree; + stvfs.f_fsid = (unsigned long)vfsp->vfs_fsid.val[0]; + bcopy(fs_type_name, stvfs.f_basetype, FSTYPSZ); + stvfs.f_flag = vf_to_stf(vfsp->vfs_flag); + stvfs.f_namemax = smi->smi_fsa.fsa_maxname; + + /* + * Save the result, update lifetime + */ + now = gethrtime(); + smi->smi_statfstime = now + + (SM_MAX_STATFSTIME * (hrtime_t)NANOSEC); + smi->smi_statvfsbuf = stvfs; /* struct assign! */ + } mutex_enter(&smi->smi_lock); if (smi->smi_status & SM_STATUS_STATFS_WANT) cv_broadcast(&smi->smi_statvfs_cv); smi->smi_status &= ~(SM_STATUS_STATFS_BUSY | SM_STATUS_STATFS_WANT); - if (error) { - SMBVDEBUG("statfs error=%d\n", error); - mutex_exit(&smi->smi_lock); - return (error); - } - - /* - * Set a few things the OTW call didn't get. - */ - stvfs.f_frsize = stvfs.f_bsize; - stvfs.f_favail = stvfs.f_ffree; - stvfs.f_fsid = (unsigned long)vfsp->vfs_fsid.val[0]; - (void) strncpy(stvfs.f_basetype, vfw.name, FSTYPSZ); - stvfs.f_flag = vf_to_stf(vfsp->vfs_flag); - stvfs.f_namemax = (uint32_t)MAXNAMELEN - 1; - - /* - * Save the result, update lifetime - */ - now = gethrtime(); - smi->smi_statfstime = now + - (SM_MAX_STATFSTIME * (hrtime_t)NANOSEC); - smi->smi_statvfsbuf = stvfs; /* struct assign! */ - /* * Copy the statvfs data to caller's buf. * Note: struct assignment */ cache_hit: - *sbp = smi->smi_statvfsbuf; + if (error == 0) + *sbp = smi->smi_statvfsbuf; mutex_exit(&smi->smi_lock); return (error); } @@ -861,8 +851,6 @@ smbfs_freevfs(vfs_t *vfsp) */ ssp = smi->smi_share; smi->smi_share = NULL; - ssp->ss_mount = NULL; - smb_share_rele(ssp); zone_rele(smi->smi_zone); diff --git a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_vnops.c b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_vnops.c index 9bf9d5d7c3..df78ba61ff 100644 --- a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_vnops.c +++ b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_vnops.c @@ -45,6 +45,7 @@ #include <sys/uio.h> #include <sys/dirent.h> #include <sys/errno.h> +#include <sys/sunddi.h> #include <sys/sysmacros.h> #include <sys/kmem.h> #include <sys/cmn_err.h> @@ -233,8 +234,10 @@ smbfs_open(vnode_t **vpp, int flag, cred_t *cr, caller_context_t *ct) vnode_t *vp; u_int32_t rights, rightsrcvd; u_int16_t fid, oldfid; + int oldgenid; struct smb_cred scred; smbmntinfo_t *smi; + smb_share_t *ssp; cred_t *oldcr; int attrcacheupdated = 0; int tmperror; @@ -243,6 +246,7 @@ smbfs_open(vnode_t **vpp, int flag, cred_t *cr, caller_context_t *ct) vp = *vpp; np = VTOSMB(vp); smi = VTOSMI(vp); + ssp = smi->smi_share; if (curproc->p_zone != smi->smi_zone) return (EIO); @@ -261,7 +265,7 @@ smbfs_open(vnode_t **vpp, int flag, cred_t *cr, caller_context_t *ct) */ if (smbfs_rw_enter_sig(&np->r_lkserlock, RW_WRITER, SMBINTR(vp))) return (EINTR); - smb_credinit(&scred, curproc, cr); + smb_credinit(&scred, cr); /* * Keep track of the vnode type at first open. @@ -296,10 +300,11 @@ smbfs_open(vnode_t **vpp, int flag, cred_t *cr, caller_context_t *ct) flag |= FWRITE; /* - * If we already have it open, check to see if current rights - * are sufficient for this open. + * If we already have it open, and the FID is still valid, + * check whether the rights are sufficient for FID reuse. */ - if (np->n_fidrefs) { + if (np->n_fidrefs > 0 && + np->n_vcgenid == ssp->ss_vcgenid) { int upgrade = 0; /* BEGIN CSTYLED */ @@ -346,16 +351,19 @@ smbfs_open(vnode_t **vpp, int flag, cred_t *cr, caller_context_t *ct) * We have a new FID and access rights. */ oldfid = np->n_fid; + oldgenid = np->n_vcgenid; np->n_fid = fid; + np->n_vcgenid = ssp->ss_vcgenid; np->n_rights = rightsrcvd; np->n_fidrefs++; - if (np->n_fidrefs > 1) { + if (np->n_fidrefs > 1 && + oldgenid == ssp->ss_vcgenid) { /* * We already had it open (presumably because * it was open with insufficient rights.) * Close old wire-open. */ - tmperror = smbfs_smb_close(smi->smi_share, + tmperror = smbfs_smb_close(ssp, oldfid, &np->n_mtime, &scred); if (tmperror) SMBVDEBUG("error %d closing %s\n", @@ -413,10 +421,14 @@ smbfs_close(vnode_t *vp, int flag, int count, offset_t offset, cred_t *cr, caller_context_t *ct) { smbnode_t *np; + smbmntinfo_t *smi; + smb_share_t *ssp; int error = 0; struct smb_cred scred; np = VTOSMB(vp); + smi = VTOSMI(vp); + ssp = smi->smi_share; /* * Don't "bail out" for VFS_UNMOUNTED here, @@ -457,8 +469,9 @@ smbfs_close(vnode_t *vp, int flag, int count, offset_t offset, cred_t *cr, * incoming reference count is. */ if (VTOSMI(vp)->smi_flags & SMI_LLOCK) { - cleanlocks(vp, ttoproc(curthread)->p_pid, 0); - cleanshares(vp, ttoproc(curthread)->p_pid); + pid_t pid = ddi_get_pid(); + cleanlocks(vp, pid, 0); + cleanshares(vp, pid); } if (count > 1) @@ -478,7 +491,7 @@ smbfs_close(vnode_t *vp, int flag, int count, offset_t offset, cred_t *cr, * Don't want this one ever interruptible. */ (void) smbfs_rw_enter_sig(&np->r_lkserlock, RW_WRITER, 0); - smb_credinit(&scred, curproc, cr); + smb_credinit(&scred, cr); error = 0; @@ -505,8 +518,11 @@ smbfs_close(vnode_t *vp, int flag, int count, offset_t offset, cred_t *cr, goto out; if ((ofid = np->n_fid) != SMB_FID_UNUSED) { np->n_fid = SMB_FID_UNUSED; - error = smbfs_smb_close(np->n_mount->smi_share, - ofid, NULL, &scred); + /* After reconnect, n_fid is invalid */ + if (np->n_vcgenid == ssp->ss_vcgenid) { + error = smbfs_smb_close( + ssp, ofid, NULL, &scred); + } } } if (error) { @@ -533,18 +549,18 @@ static int smbfs_read(vnode_t *vp, struct uio *uiop, int ioflag, cred_t *cr, caller_context_t *ct) { - smbmntinfo_t *smi; - smbnode_t *np; struct smb_cred scred; struct vattr va; - /* u_offset_t off; */ - /* offset_t diff; */ + smbnode_t *np; + smbmntinfo_t *smi; + smb_share_t *ssp; offset_t endoff; ssize_t past_eof; int error; np = VTOSMB(vp); smi = VTOSMI(vp); + ssp = smi->smi_share; if (curproc->p_zone != smi->smi_zone) return (EIO); @@ -565,8 +581,8 @@ smbfs_read(vnode_t *vp, struct uio *uiop, int ioflag, cred_t *cr, * Our SMB layer takes care to return EFBIG * when it has to fallback to a 32-bit call. */ - if (uiop->uio_loffset < 0 || - uiop->uio_loffset + uiop->uio_resid < 0) + endoff = uiop->uio_loffset + uiop->uio_resid; + if (uiop->uio_loffset < 0 || endoff < 0) return (EINVAL); /* get vnode attributes from server */ @@ -585,7 +601,6 @@ smbfs_read(vnode_t *vp, struct uio *uiop, int ioflag, cred_t *cr, * Do this by temporarily reducing uio_resid * by the amount the lies beyoned the EOF. */ - endoff = uiop->uio_loffset + uiop->uio_resid; if (endoff > va.va_size) { past_eof = (ssize_t)(endoff - va.va_size); uiop->uio_resid -= past_eof; @@ -595,10 +610,14 @@ smbfs_read(vnode_t *vp, struct uio *uiop, int ioflag, cred_t *cr, /* Shared lock for n_fid use in smb_rwuio */ if (smbfs_rw_enter_sig(&np->r_lkserlock, RW_READER, SMBINTR(vp))) return (EINTR); - smb_credinit(&scred, curproc, cr); + smb_credinit(&scred, cr); - error = smb_rwuio(smi->smi_share, np->n_fid, UIO_READ, uiop, - &scred, smb_timo_read); + /* After reconnect, n_fid is invalid */ + if (np->n_vcgenid != ssp->ss_vcgenid) + error = ESTALE; + else + error = smb_rwuio(ssp, np->n_fid, UIO_READ, + uiop, &scred, smb_timo_read); smb_credrele(&scred); smbfs_rw_exit(&np->r_lkserlock); @@ -615,16 +634,18 @@ static int smbfs_write(vnode_t *vp, struct uio *uiop, int ioflag, cred_t *cr, caller_context_t *ct) { - smbmntinfo_t *smi; - smbnode_t *np; struct smb_cred scred; struct vattr va; + smbnode_t *np; + smbmntinfo_t *smi; + smb_share_t *ssp; offset_t endoff, limit; ssize_t past_limit; int error, timo; np = VTOSMB(vp); smi = VTOSMI(vp); + ssp = smi->smi_share; if (curproc->p_zone != smi->smi_zone) return (EIO); @@ -696,10 +717,14 @@ smbfs_write(vnode_t *vp, struct uio *uiop, int ioflag, cred_t *cr, /* Shared lock for n_fid use in smb_rwuio */ if (smbfs_rw_enter_sig(&np->r_lkserlock, RW_READER, SMBINTR(vp))) return (EINTR); - smb_credinit(&scred, curproc, cr); + smb_credinit(&scred, cr); - error = smb_rwuio(smi->smi_share, np->n_fid, UIO_WRITE, uiop, - &scred, timo); + /* After reconnect, n_fid is invalid */ + if (np->n_vcgenid != ssp->ss_vcgenid) + error = ESTALE; + else + error = smb_rwuio(ssp, np->n_fid, UIO_WRITE, + uiop, &scred, timo); if (error == 0) { mutex_enter(&np->r_statelock); @@ -864,7 +889,7 @@ smbfsgetattr(vnode_t *vp, struct vattr *vap, cred_t *cr) /* Shared lock for (possible) n_fid use. */ if (smbfs_rw_enter_sig(&np->r_lkserlock, RW_READER, SMBINTR(vp))) return (EINTR); - smb_credinit(&scred, curproc, cr); + smb_credinit(&scred, cr); bzero(&fattr, sizeof (fattr)); error = smbfs_smb_getfattr(np, &fattr, &scred); @@ -964,7 +989,7 @@ smbfssetattr(vnode_t *vp, struct vattr *vap, int flags, cred_t *cr) /* Shared lock for (possible) n_fid use. */ if (smbfs_rw_enter_sig(&np->r_lkserlock, RW_READER, SMBINTR(vp))) return (EINTR); - smb_credinit(&scred, curproc, cr); + smb_credinit(&scred, cr); /* * Will we need an open handle for this setattr? @@ -984,6 +1009,7 @@ smbfssetattr(vnode_t *vp, struct vattr *vap, int flags, cred_t *cr) * Only SIZE requires a handle. * XXX May be more reliable to just * always get the file handle here. + * The tmpopen checks n_vcgenid. */ error = smbfs_smb_tmpopen(np, rights, &scred, &fid); if (error) { @@ -1024,6 +1050,9 @@ smbfssetattr(vnode_t *vp, struct vattr *vap, int flags, cred_t *cr) * Tests indicate the server will zero-fill, * so looks like we don't need to do this. * Good thing, as this could take forever. + * + * XXX: Reportedly, writing one byte of zero + * at the end offset avoids problems here. */ mutex_enter(&np->r_statelock); np->r_size = vap->va_size; @@ -1265,7 +1294,7 @@ smbfs_fsync(vnode_t *vp, int syncflag, cred_t *cr, caller_context_t *ct) /* Shared lock for n_fid use in _flush */ if (smbfs_rw_enter_sig(&np->r_lkserlock, RW_READER, SMBINTR(vp))) return (EINTR); - smb_credinit(&scred, curproc, cr); + smb_credinit(&scred, cr); error = smbfs_smb_flush(np, &scred); @@ -1563,7 +1592,7 @@ smbfslookup(vnode_t *dvp, char *nm, vnode_t **vpp, cred_t *cr, int dnlc, * First, go over-the-wire to get the * node type (and attributes). */ - smb_credinit(&scred, curproc, cr); + smb_credinit(&scred, cr); /* Note: this can allocate a new "name" */ error = smbfs_smb_lookup(dnp, &name, &nmlen, &fa, &scred); smb_credrele(&scred); @@ -1748,7 +1777,7 @@ smbfs_create(vnode_t *dvp, char *nm, struct vattr *va, enum vcexcl exclusive, if (smbfs_rw_enter_sig(&dnp->r_rwlock, RW_WRITER, SMBINTR(dvp))) return (EINTR); - smb_credinit(&scred, curproc, cr); + smb_credinit(&scred, cr); /* * XXX: Do we need r_lkserlock too? @@ -2016,7 +2045,7 @@ smbfs_remove(vnode_t *dvp, char *nm, cred_t *cr, caller_context_t *ct, } else { mutex_exit(&np->r_statelock); - smb_credinit(&scred, curproc, cr); + smb_credinit(&scred, cr); error = smbfs_smb_delete(np, &scred, NULL, 0, 0); smb_credrele(&scred); @@ -2254,7 +2283,7 @@ smbfsrename(vnode_t *odvp, char *onm, vnode_t *ndvp, char *nnm, cred_t *cr, /* * Target file is not active. Try to remove it. */ - smb_credinit(&scred, curproc, cr); + smb_credinit(&scred, cr); error = smbfs_smb_delete(nnp, &scred, NULL, 0, 0); smb_credrele(&scred); if (error) @@ -2273,7 +2302,7 @@ smbfsrename(vnode_t *odvp, char *onm, vnode_t *ndvp, char *nnm, cred_t *cr, dnlc_remove(ndvp, nnm); onp = VTOSMB(ovp); - smb_credinit(&scred, curproc, cr); + smb_credinit(&scred, cr); error = smbfs_smb_rename(onp, ndnp, nnm, strlen(nnm), &scred); smb_credrele(&scred); @@ -2331,7 +2360,7 @@ smbfs_mkdir(vnode_t *dvp, char *nm, struct vattr *va, vnode_t **vpp, if (smbfs_rw_enter_sig(&dnp->r_rwlock, RW_WRITER, SMBINTR(dvp))) return (EINTR); - smb_credinit(&scred, curproc, cr); + smb_credinit(&scred, cr); /* * XXX: Do we need r_lkserlock too? @@ -2409,7 +2438,7 @@ smbfs_rmdir(vnode_t *dvp, char *nm, vnode_t *cdir, cred_t *cr, if (smbfs_rw_enter_sig(&dnp->r_rwlock, RW_WRITER, SMBINTR(dvp))) return (EINTR); - smb_credinit(&scred, curproc, cr); + smb_credinit(&scred, cr); /* * Require w/x access in the containing directory. @@ -2572,8 +2601,8 @@ smbfs_readvdir(vnode_t *vp, uio_t *uio, cred_t *cr, int *eofp, dnlc_purge_vp(vp); #endif SMBVDEBUG("dirname='%s'\n", np->n_rpath); - smb_credinit(&scred, curproc, cr); - dbufsiz = DIRENT64_RECLEN(MAXNAMELEN); + smb_credinit(&scred, cr); + dbufsiz = DIRENT64_RECLEN(SMB_MAXFNAMELEN); dp = kmem_alloc(dbufsiz, KM_SLEEP); offset = uio->uio_offset; /* NB: "cookie" */ @@ -2670,8 +2699,8 @@ smbfs_readvdir(vnode_t *vp, uio_t *uio, cred_t *cr, int *eofp, np->n_dirofs++; /* Sanity check the name length. */ nmlen = ctx->f_nmlen; - if (nmlen > (MAXNAMELEN - 1)) { - nmlen = MAXNAMELEN - 1; + if (nmlen > SMB_MAXFNAMELEN) { + nmlen = SMB_MAXFNAMELEN; SMBVDEBUG("Truncating name: %s\n", ctx->f_name); } reclen = DIRENT64_RECLEN(nmlen); diff --git a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_xattr.c b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_xattr.c index 4760576731..ad68648d12 100644 --- a/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_xattr.c +++ b/usr/src/uts/common/fs/smbclnt/smbfs/smbfs_xattr.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. */ @@ -198,7 +198,7 @@ smbfs_xa_exists(vnode_t *vp, cred_t *cr) /* NB: have VN_HOLD on xpv */ xnp = VTOSMB(xvp); - smb_credinit(&scred, curproc, cr); + smb_credinit(&scred, cr); bzero(&ctx, sizeof (ctx)); ctx.f_flags = SMBFS_RDD_FINDFIRST; @@ -303,6 +303,10 @@ smbfs_xa_findopen(struct smbfs_fctx *ctx, struct smbnode *dnp, ASSERT(dnp->n_flag & N_XATTR); ctx->f_type = ft_XA; + ctx->f_namesz = SMB_MAXFNAMELEN + 1; + if (SMB_UNICODE_STRINGS(SSTOVC(ctx->f_ssp))) + ctx->f_namesz *= 2; + ctx->f_name = kmem_alloc(ctx->f_namesz, KM_SLEEP); error = smbfs_xa_parent(SMBTOV(dnp), &pvp); if (error) @@ -404,12 +408,8 @@ again: nmlen = min(size, SMB_MAXFNAMELEN); } - if (ctx->f_name) - kmem_free(ctx->f_name, ctx->f_namesz); + ASSERT(nmlen < ctx->f_namesz); ctx->f_nmlen = nmlen; - /* Add one to prevent allocating size zero. */ - ctx->f_namesz = nmlen + 1; - ctx->f_name = kmem_alloc(ctx->f_namesz, KM_SLEEP); error = md_get_mem(mdp, ctx->f_name, nmlen, MB_MSYSTEM); if (error) return (error); diff --git a/usr/src/uts/common/netsmb/mchain.h b/usr/src/uts/common/netsmb/mchain.h index 7009ba75bf..bccb9aab7f 100644 --- a/usr/src/uts/common/netsmb/mchain.h +++ b/usr/src/uts/common/netsmb/mchain.h @@ -31,16 +31,15 @@ * * $FreeBSD: src/sys/sys/mchain.h,v 1.1 2001/02/24 15:44:30 bp Exp $ */ + /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _MCHAIN_H_ #define _MCHAIN_H_ -#pragma ident "%Z%%M% %I% %E% SMI" - #include <sys/types.h> #include <sys/isa_defs.h> #include <sys/byteorder.h> @@ -158,10 +157,10 @@ int mb_put_uint32be(struct mbchain *mbp, uint32_t x); int mb_put_uint32le(struct mbchain *mbp, uint32_t x); int mb_put_uint64be(struct mbchain *mbp, uint64_t x); int mb_put_uint64le(struct mbchain *mbp, uint64_t x); -int mb_put_mem(struct mbchain *mbp, const char *src, int size, int type); +int mb_put_mem(struct mbchain *mbp, const void *src, int size, int type); int mb_put_mbuf(struct mbchain *mbp, mblk_t *m); -int mb_put_uio(struct mbchain *mbp, uio_t *uiop, int size); +int mb_put_uio(struct mbchain *mbp, uio_t *uiop, size_t size); int md_init(struct mdchain *mdp); void md_initm(struct mdchain *mbp, mblk_t *m); @@ -169,18 +168,15 @@ void md_done(struct mdchain *mdp); void md_append_record(struct mdchain *mdp, mblk_t *top); int md_next_record(struct mdchain *mdp); int md_get_uint8(struct mdchain *mdp, uint8_t *x); -int md_get_uint16(struct mdchain *mdp, uint16_t *x); int md_get_uint16le(struct mdchain *mdp, uint16_t *x); int md_get_uint16be(struct mdchain *mdp, uint16_t *x); -int md_get_uint32(struct mdchain *mdp, uint32_t *x); int md_get_uint32be(struct mdchain *mdp, uint32_t *x); int md_get_uint32le(struct mdchain *mdp, uint32_t *x); -int md_get_uint64(struct mdchain *mdp, uint64_t *x); int md_get_uint64be(struct mdchain *mdp, uint64_t *x); int md_get_uint64le(struct mdchain *mdp, uint64_t *x); -int md_get_mem(struct mdchain *mdp, caddr_t target, int size, int type); +int md_get_mem(struct mdchain *mdp, void *vdst, int size, int type); int md_get_mbuf(struct mdchain *mdp, int size, mblk_t **m); -int md_get_uio(struct mdchain *mdp, uio_t *uiop, int size); +int md_get_uio(struct mdchain *mdp, uio_t *uiop, size_t size); /* * Additions for Solaris to replace things that came from diff --git a/usr/src/uts/common/netsmb/netbios.h b/usr/src/uts/common/netsmb/netbios.h index b61f19c2dd..b9ebce1018 100644 --- a/usr/src/uts/common/netsmb/netbios.h +++ b/usr/src/uts/common/netsmb/netbios.h @@ -32,11 +32,14 @@ * $Id: netbios.h,v 1.5 2004/03/19 01:49:45 lindak Exp $ */ +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + #ifndef _NETSMB_NETBIOS_H_ #define _NETSMB_NETBIOS_H_ -#pragma ident "%Z%%M% %I% %E% SMI" - #ifndef _NETINET_IN_H_ #include <netinet/in.h> #endif @@ -66,6 +69,8 @@ #define NB_ENCNAMELEN NB_NAMELEN * 2 #define NB_MAXLABLEN 63 +#define NB_MAXPKTLEN 0x1FFFF + #define NB_MINSALEN (sizeof (struct sockaddr_nb)) /* diff --git a/usr/src/uts/common/netsmb/smb.h b/usr/src/uts/common/netsmb/smb.h index 40c3522cfc..f0957634cb 100644 --- a/usr/src/uts/common/netsmb/smb.h +++ b/usr/src/uts/common/netsmb/smb.h @@ -45,8 +45,6 @@ #ifndef _NETSMB_SMB_H_ #define _NETSMB_SMB_H_ -#pragma ident "%Z%%M% %I% %E% SMI" - /* * This file should be purely SMB protocol definition stuff. * (Please don't make it a catch-all:) @@ -81,7 +79,7 @@ enum smb_dialects { #define SMB_SIGNATURE "\xFFSMB" #define SMB_SIGLEN 4 #define SMB_HDRCMD(p) (*((uchar_t *)(p) + SMB_SIGLEN)) -#define SMB_HDRMID(p) (letohs(*(ushort_t *)((uchar_t *)(p) + 30))) +#define SMB_HDRMID(p) (*(ushort_t *)((uchar_t *)(p) + 30)) #define SMB_HDRLEN 32 /* * bits in the smb_flags field @@ -751,26 +749,6 @@ typedef struct ntsid ntsid_t; #define SMB_LOCKING_ANDX_LARGE_FILES 0x10 /* - * Some names length limitations. Some of them aren't declared by specs, - * but we need reasonable limits. - */ -#define SMB_MAXSRVNAMELEN 15 /* NetBIOS limit */ -#define SMB_MAXUSERNAMELEN 128 -#define SMB_MAXPASSWORDLEN 128 -#define SMB_MAXSHARENAMELEN 128 -#define SMB_MAXPKTLEN 0x1FFFF -#define SMB_MAXCHALLENGELEN 8 -#define SMB_MAXFNAMELEN 255 /* Keep in sync with MAXNAMLEN */ - -#define SMB_RCNDELAY 2 /* seconds between reconnect attempts */ -/* - * leave this zero - we can't ssecond guess server side effects of - * duplicate ops, this isn't nfs! - */ -#define SMBMAXRESTARTS 0 -#define SMB_MAXSETUPWORDS 3 /* max # of setup words in trans/t2 */ - -/* * Error classes */ #define SMBSUCCESS 0x00 diff --git a/usr/src/uts/common/netsmb/smb_dev.h b/usr/src/uts/common/netsmb/smb_dev.h index ac09858c7d..8fee2bccf2 100644 --- a/usr/src/uts/common/netsmb/smb_dev.h +++ b/usr/src/uts/common/netsmb/smb_dev.h @@ -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. */ @@ -59,13 +59,9 @@ * any part of this user/kernel I/F changes. */ -#ifndef _KERNEL #include <sys/types.h> -#endif - #include <sys/socket_impl.h> -#include <netsmb/smb.h> -#include <netsmb/netbios.h> +#include <netinet/in.h> #define NSMB_NAME "nsmb" @@ -75,35 +71,25 @@ * make them incompatible with an old driver. */ #define NSMB_VERMAJ 1 -#define NSMB_VERMIN 3600 +#define NSMB_VERMIN 3900 #define NSMB_VERSION (NSMB_VERMAJ * 100000 + NSMB_VERMIN) -#define NSMB_VER_STR "1.36" - -#define NSMBFL_OPEN 0x0001 -#define NSMBFL_NEWVC 0x0002 /* - * Hack-ish errno values we need to expose to the library. + * Some errno values we need to expose to the library. + * NB: these are also defined in the library smbfs_api.h + * to avoid exposing all of this stuff in that API. + * * EBADRPC is used for message decoding errors. * EAUTH is used for CIFS authentication errors. */ #ifndef EBADRPC -#define EBADRPC 113 /* XXX */ +#define EBADRPC 113 #endif #ifndef EAUTH -#define EAUTH 114 /* XXX */ +#define EAUTH 114 #endif /* - * "Level" in the connection object hierarchy - */ -#define SMBL_SM 0 -#define SMBL_VC 1 -#define SMBL_SHARE 2 -#define SMBL_NUM 3 -#define SMBL_NONE (-1) - -/* * Upper/lower case options */ #define SMB_CS_NONE 0x0000 @@ -115,14 +101,6 @@ */ #define SMBM_ANY_OWNER ((uid_t)-1) #define SMBM_ANY_GROUP ((gid_t)-1) -#define SMBM_MASK 0777 -#define SMBM_EXACT 010000 /* check for specified mode exactly */ -#ifdef _KERNEL -/* In-kernel, we prefer the vnode.h names. */ -#define SMBM_READ VREAD /* (S_IRUSR) read conn attrs. */ -#define SMBM_WRITE VWRITE /* (S_IWUSR) modify conn attrs */ -#define SMBM_EXEC VEXEC /* (S_IXUSR) can send SMB requests */ -#endif /* * Option flags in smbioc_ossn.ioc_opt @@ -140,14 +118,6 @@ #define SMBVOPT_SIGNING_REQUIRED 0x0200 /* signing required */ #define SMBVOPT_SIGNING_MASK 0x0300 /* all signing bits */ -/* XXX: How about a separate field for these? */ -#define SMBVOPT_MINAUTH 0x7000 /* min. auth. level (mask) */ -#define SMBVOPT_MINAUTH_NONE 0x0000 /* any authentication OK */ -#define SMBVOPT_MINAUTH_LM 0x1000 /* no plaintext passwords */ -#define SMBVOPT_MINAUTH_NTLM 0x2000 /* don't send LM reply */ -#define SMBVOPT_MINAUTH_NTLMV2 0x3000 /* don't fall back to NTLMv1 */ -#define SMBVOPT_MINAUTH_KERBEROS 0x4000 /* don't do NTLMv1 or v2 */ - /* * Option flags in smbioc_oshare.ioc_opt * and sharespec.optflags @@ -155,7 +125,27 @@ #define SMBSOPT_CREATE SMBVOPT_CREATE #define SMBSOPT_PERMANENT SMBVOPT_PERMANENT -#define MAX_STR_LEN 8 /* Maxilum length of the minor device name */ +/* All user and machine names. */ +#define SMBIOC_MAX_NAME 256 + +/* + * Size of storage for p/w hashes. + * Also for SMBIOC_GETSSNKEY. + */ +#define SMBIOC_HASH_SZ 16 + +/* + * network IO daemon states + * really connection states. + */ +enum smbiod_state { + SMBIOD_ST_IDLE = 0, /* no user requests enqueued yet */ + SMBIOD_ST_RECONNECT, /* a [re]connect attempt is in progress */ + SMBIOD_ST_RCFAILED, /* a reconnect attempt has failed */ + SMBIOD_ST_VCACTIVE, /* session established */ + SMBIOD_ST_DEAD /* connection gone, no IOD */ +}; + /* * We're now using structures that are invariant @@ -187,98 +177,141 @@ typedef union lptr { * Handy union of sockaddr types we use. * Type discriminator is sa_family */ -union sockaddr_any { - struct sockaddr sa; - struct sockaddr_in in; - struct sockaddr_nb nb; +union smbioc_sockaddr { + struct sockaddr sa; /* generic */ + struct sockaddr_in sin; + struct sockaddr_in6 sin6; }; +typedef union smbioc_sockaddr smbioc_sockaddr_t; +/* + * This is what identifies a session. + */ +struct smbioc_ssn_ident { + smbioc_sockaddr_t id_srvaddr; + char id_domain[SMBIOC_MAX_NAME]; + char id_user[SMBIOC_MAX_NAME]; +}; +typedef struct smbioc_ssn_ident smbioc_ssn_ident_t; /* - * SMBIOC_LOOKUP flags + * Flags for smbioc_ossn.ssn_opt */ #define SMBLK_CREATE SMBVOPT_CREATE -#define DEF_SEC_TOKEN_LEN 2048 - +/* + * Structure used with SMBIOC_SSN_FIND, _CREATE + */ struct smbioc_ossn { - union sockaddr_any ioc_server; - union sockaddr_any ioc_local; - char ioc_localcs[16]; /* local charset */ - char ioc_servercs[16]; /* server charset */ - char ioc_srvname[SMB_MAXSRVNAMELEN + 1]; - char ioc_user[SMB_MAXUSERNAMELEN + 1]; - char ioc_workgroup[SMB_MAXUSERNAMELEN + 1]; - char ioc_password[SMB_MAXPASSWORDLEN + 1]; - int32_t ioc_opt; - int32_t ioc_timeout; /* ignored?! XXX */ - int32_t ioc_retrycount; /* number of retries before giveup */ - uid_t ioc_owner; /* proposed owner */ - gid_t ioc_group; /* proposed group */ - mode_t ioc_mode; /* desired access mode */ - mode_t ioc_rights; /* SMBM_* */ - int32_t ioc_intoklen; - int32_t ioc_outtoklen; - /* copyout ends at this offset */ - lptr_t _ioc_intok; - lptr_t _ioc_outtok; + uint32_t ssn_vopt; /* i.e. SMBVOPT_CREATE */ + uint32_t ssn_owner; /* Unix owner (UID) */ + smbioc_ssn_ident_t ssn_id; + char ssn_srvname[SMBIOC_MAX_NAME]; }; typedef struct smbioc_ossn smbioc_ossn_t; -#define ioc_intok _ioc_intok.lp_ptr -#define ioc_outtok _ioc_outtok.lp_ptr - +/* Convenience names for members under ssn_id */ +#define ssn_srvaddr ssn_id.id_srvaddr +#define ssn_domain ssn_id.id_domain +#define ssn_user ssn_id.id_user +/* + * Structure used with SMBIOC_TREE_FIND, _CONNECT + */ +#define SMBIOC_STYPE_LEN 8 struct smbioc_oshare { - char ioc_share[SMB_MAXSHARENAMELEN + 1]; - char ioc_password[SMB_MAXPASSWORDLEN + 1]; - int32_t ioc_opt; - int32_t ioc_stype; /* share type */ - uid_t ioc_owner; /* proposed owner of share */ - gid_t ioc_group; /* proposed group of share */ - mode_t ioc_mode; /* desired access mode to share */ - mode_t ioc_rights; /* SMBM_* */ - /* - * Hack: need the size of this to be 8-byte aligned - * so that the ioc_ossn following it in smbioc_lookup - * is correctly aligned... - */ - int32_t ioc__pad; + uint32_t sh_pwlen; + char sh_name[SMBIOC_MAX_NAME]; + char sh_pass[SMBIOC_MAX_NAME]; + /* share types, in ASCII form, i.e. "A:", "IPC", ... */ + char sh_type_req[SMBIOC_STYPE_LEN]; /* requested */ + char sh_type_ret[SMBIOC_STYPE_LEN]; /* returned */ }; typedef struct smbioc_oshare smbioc_oshare_t; +typedef struct smbioc_tcon { + int32_t tc_flags; + int32_t tc_opt; + smbioc_oshare_t tc_sh; +} smbioc_tcon_t; + + +/* + * Negotiated protocol parameters + */ +struct smb_sopt { + int16_t sv_proto; /* protocol dialect */ + uchar_t sv_sm; /* security mode */ + int16_t sv_tz; /* offset in min relative to UTC */ + uint16_t sv_maxmux; /* max number of outstanding rq's */ + uint16_t sv_maxvcs; /* max number of VCs */ + uint16_t sv_rawmode; + uint32_t sv_maxtx; /* maximum transmit buf size */ + uint32_t sv_maxraw; /* maximum raw-buffer size */ + uint32_t sv_skey; /* session key */ + uint32_t sv_caps; /* capabilites SMB_CAP_ */ +}; +typedef struct smb_sopt smb_sopt_t; + +/* + * State carried in/out of the driver by the IOD thread. + * Inside the driver, these are members of the "VC" object. + */ +struct smb_iods { + int32_t is_tran_fd; /* transport FD */ + uint32_t is_vcflags; /* SMBV_... */ + uint8_t is_hflags; /* SMB header flags */ + uint16_t is_hflags2; /* SMB header flags2 */ + uint16_t is_smbuid; /* SMB header UID */ + uint16_t is_next_mid; /* SMB header MID */ + uint32_t is_txmax; /* max tx/rx packet size */ + uint32_t is_rwmax; /* max read/write data size */ + uint32_t is_rxmax; /* max readx data size */ + uint32_t is_wxmax; /* max writex data size */ + uint8_t is_ssn_key[SMBIOC_HASH_SZ]; /* session key */ + /* Signing state */ + uint32_t is_next_seq; /* my next sequence number */ + uint32_t is_u_maclen; /* MAC key length */ + lptr_t is_u_mackey; /* user-space ptr! */ +}; +typedef struct smb_iods smb_iods_t; + +/* + * This is the operational state information passed + * in and out of the driver for SMBIOC_SSN_WORK + */ +struct smbioc_ssn_work { + smb_iods_t wk_iods; + smb_sopt_t wk_sopt; + int wk_out_state; +}; +typedef struct smbioc_ssn_work smbioc_ssn_work_t; + +/* + * User-level SMB requests + */ + +/* + * SMBIOC_REQUEST (simple SMB request) + */ typedef struct smbioc_rq { uchar_t ioc_cmd; - uchar_t ioc_twc; /* _twords */ - ushort_t ioc_tbc; /* _tbytes */ - int32_t ioc_rpbufsz; /* _rpbuf */ - uchar_t ioc__pad1; - uchar_t ioc_rwc; - ushort_t ioc_rbc; - uchar_t ioc__pad2; uint8_t ioc_errclass; uint16_t ioc_serror; uint32_t ioc_error; - uint32_t ioc__pad3; - /* - * Copyout all but the pointers, which - * we may have set to kernel memory. - * See ..._COPYOUT_SIZE - */ - lptr_t _ioc_twords; - lptr_t _ioc_tbytes; - lptr_t _ioc_rpbuf; + uint32_t ioc_tbufsz; /* transmit */ + uint32_t ioc_rbufsz; /* receive */ + lptr_t _ioc_tbuf; + lptr_t _ioc_rbuf; } smbioc_rq_t; -#define ioc_twords _ioc_twords.lp_ptr -#define ioc_tbytes _ioc_tbytes.lp_ptr -#define ioc_rpbuf _ioc_rpbuf.lp_ptr -#define SMBIOC_RQ_COPYOUT_SIZE \ - (offsetof(smbioc_rq_t, _ioc_twords)) +#define ioc_tbuf _ioc_tbuf.lp_ptr +#define ioc_rbuf _ioc_rbuf.lp_ptr -#define SMBIOC_T2RQ_MAXNAME 128 +#define SMBIOC_T2RQ_MAXSETUP 4 +#define SMBIOC_T2RQ_MAXNAME 128 typedef struct smbioc_t2rq { - uint16_t ioc_setup[SMB_MAXSETUPWORDS]; + uint16_t ioc_setup[SMBIOC_T2RQ_MAXSETUP]; int32_t ioc_setupcnt; char ioc_name[SMBIOC_T2RQ_MAXNAME]; ushort_t ioc_tparamcnt; @@ -291,11 +324,6 @@ typedef struct smbioc_t2rq { uint32_t ioc_error; uint16_t ioc_rpflags2; uint16_t ioc__pad2; - /* - * Copyout all but the pointers, which - * we may have set to kernel memory. - * See ..._COPYOUT_SIZE - */ lptr_t _ioc_tparam; lptr_t _ioc_tdata; lptr_t _ioc_rparam; @@ -305,43 +333,30 @@ typedef struct smbioc_t2rq { #define ioc_tdata _ioc_tdata.lp_ptr #define ioc_rparam _ioc_rparam.lp_ptr #define ioc_rdata _ioc_rdata.lp_ptr -#define SMBIOC_T2RQ_COPYOUT_SIZE \ - (offsetof(smbioc_t2rq_t, _ioc_tparam)) typedef struct smbioc_flags { int32_t ioc_level; /* 0 - session, 1 - share */ - int32_t ioc_mask; int32_t ioc_flags; + int32_t ioc_mask; } smbioc_flags_t; -typedef struct smbioc_lookup { - int32_t ioc_level; - int32_t ioc_flags; - struct smbioc_oshare ioc_sh; - struct smbioc_ossn ioc_ssn; -} smbioc_lookup_t; -#define SMBIOC_LOOK_COPYOUT_SIZE \ - (offsetof(smbioc_lookup_t, ioc_ssn._ioc_intok)) - typedef struct smbioc_rw { - uint16_t ioc_fh; + uint32_t ioc_fh; uint32_t ioc_cnt; lloff_t _ioc_offset; lptr_t _ioc_base; } smbioc_rw_t; #define ioc_offset _ioc_offset._f #define ioc_base _ioc_base.lp_ptr -#define SMBIOC_RW_COPYOUT_SIZE \ - (offsetof(smbioc_rw_t, _ioc_base)) /* Password Keychain (PK) support. */ -#define SMBIOC_PK_MAXLEN 255 typedef struct smbioc_pk { uid_t pk_uid; /* UID for PAM use */ - char pk_dom[SMBIOC_PK_MAXLEN+1]; /* CIFS domain name */ - char pk_usr[SMBIOC_PK_MAXLEN+1]; /* CIFS user name */ - char pk_pass[SMBIOC_PK_MAXLEN+1]; /* CIFS password */ + char pk_dom[SMBIOC_MAX_NAME]; /* CIFS domain name */ + char pk_usr[SMBIOC_MAX_NAME]; /* CIFS user name */ + uchar_t pk_lmhash[SMBIOC_HASH_SZ]; /* LanMan p/w hash */ + uchar_t pk_nthash[SMBIOC_HASH_SZ]; /* NTLM p/w hash */ } smbioc_pk_t; @@ -357,18 +372,29 @@ typedef struct smbioc_pk { */ #define SMBIOC_BASE ((('n' << 8) | 's') << 8) typedef enum nsmb_ioc { - SMBIOC_GETVERS = SMBIOC_BASE, - SMBIOC_REQUEST, - SMBIOC_T2RQ, - SMBIOC_LOOKUP, - SMBIOC_READ, - SMBIOC_WRITE, - SMBIOC_FINDVC, - SMBIOC_NEGOTIATE, - SMBIOC_SSNSETUP, - SMBIOC_TCON, - SMBIOC_TDIS, - SMBIOC_FLAGS2, + SMBIOC_GETVERS = SMBIOC_BASE, /* keep first */ + SMBIOC_FLAGS2, /* get hflags2 */ + SMBIOC_GETSSNKEY, /* get SMB session key */ + + SMBIOC_REQUEST, /* simple request */ + SMBIOC_T2RQ, /* trans2 request */ + SMBIOC_READ, /* read (pipe) */ + SMBIOC_WRITE, /* write (pipe) */ + + SMBIOC_SSN_CREATE, + SMBIOC_SSN_FIND, + SMBIOC_SSN_KILL, /* force disconnect */ + SMBIOC_SSN_RELE, /* drop our reference */ + + SMBIOC_TREE_CONNECT, /* create and connect */ + SMBIOC_TREE_FIND, + SMBIOC_TREE_KILL, + SMBIOC_TREE_RELE, + + SMBIOC_IOD_WORK, /* work on session requests */ + SMBIOC_IOD_IDLE, /* wait for requests on this session */ + SMBIOC_IOD_RCFAIL, /* notify that reconnect failed */ + /* Password Keychain (PK) support. */ SMBIOC_PK_ADD, /* Add/Modify a password entry */ SMBIOC_PK_CHK, /* Check for a password entry */ @@ -377,52 +403,4 @@ typedef enum nsmb_ioc { SMBIOC_PK_DEL_EVERYONE /* all owned by everyone */ } nsmb_ioc_t; -#ifdef _KERNEL -#include <sys/dditypes.h> /* for dev_info_t */ - -#define SMBST_CONNECTED 1 - -/* Size of storage for p/w hashes. */ -#define SMB_PWH_MAX 24 - -extern const uint32_t nsmb_version; - -struct smb_cred; -struct smb_share; -struct smb_vc; - -typedef struct smb_dev { - int sd_opened; /* Opened or not */ - int sd_level; /* Future use */ - struct smb_vc *sd_vc; /* Reference to VC */ - struct smb_share *sd_share; /* Reference to share if any */ - int sd_poll; /* Future use */ - int sd_seq; /* Kind of minor number/instance no */ - int sd_flags; /* State of connection */ - zoneid_t zoneid; /* Zone id */ - dev_info_t *smb_dip; /* ptr to dev_info node */ - void *sd_devfs; /* Dont know how to use this. but */ - struct cred *smb_cred; /* per dev credentails. Future use */ -} smb_dev_t; - -/* - * Compound user interface - */ -int smb_usr_findvc(struct smbioc_lookup *dp, struct smb_cred *scred, - struct smb_vc **vcpp); -int smb_usr_negotiate(struct smbioc_lookup *dp, struct smb_cred *scred, - struct smb_vc **vcpp); -int smb_usr_ssnsetup(struct smbioc_lookup *dp, struct smb_cred *scred, - struct smb_vc *vcp); -int smb_usr_tcon(struct smbioc_lookup *dp, struct smb_cred *scred, - struct smb_vc *vcp, struct smb_share **sspp); -int smb_usr_simplerequest(struct smb_share *ssp, struct smbioc_rq *data, - struct smb_cred *scred); -int smb_usr_t2request(struct smb_share *ssp, struct smbioc_t2rq *data, - struct smb_cred *scred); -int smb_usr_rw(struct smb_share *ssp, smbioc_rw_t *dp, - int cmd, struct smb_cred *scred); -int smb_dev2share(int fd, struct smb_share **sspp); - -#endif /* _KERNEL */ #endif /* _NETSMB_DEV_H_ */ diff --git a/usr/src/uts/intel/nsmb/Makefile b/usr/src/uts/intel/nsmb/Makefile index 3b8d751ac2..a55850a141 100644 --- a/usr/src/uts/intel/nsmb/Makefile +++ b/usr/src/uts/intel/nsmb/Makefile @@ -21,10 +21,10 @@ # # uts/intel/nsmb/Makefile # -# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -#ident "%Z%%M% %I% %E% SMI" + # # intel architecture dependent # @@ -70,6 +70,8 @@ LDFLAGS += -dy -Ncrypto/md4 -Ncrypto/md5 -Nmisc/tlimod LINTTAGS += -erroff=E_FUNC_RET_ALWAYS_IGNOR2 LINTTAGS += -erroff=E_FUNC_RET_MAYBE_IGNORED2 +# Until CR 4994570 is fixed... +LINTTAGS += -erroff=E_BAD_FORMAT_ARG_TYPE2 # # Default build targets. diff --git a/usr/src/uts/intel/nsmb/ioc_check.ref b/usr/src/uts/intel/nsmb/ioc_check.ref index ead69ec44f..76660e2ef5 100644 --- a/usr/src/uts/intel/nsmb/ioc_check.ref +++ b/usr/src/uts/intel/nsmb/ioc_check.ref @@ -1,56 +1,62 @@ -#define SIZEOF_SOCKADDR_ANY 0x18 -#define SIZEOF_SOCKADDR_IN 0x10 -#define SIZEOF_SOCKADDR_NB 0x18 -#define SIZEOF_SMBIOC_OSSN 0x218 -#define IOC_SERVER 0x0 -#define IOC_LOCAL 0x18 -#define IOC_LOCALCS 0x30 -#define IOC_LOCALCS_INCR 0x1 -#define IOC_SERVERCS 0x40 -#define IOC_SERVERCS_INCR 0x1 -#define IOC_SRVNAME 0x50 -#define IOC_SRVNAME_INCR 0x1 -#define IOC_USER 0x60 -#define IOC_USER_INCR 0x1 -#define IOC_WORKGROUP 0xe1 -#define IOC_WORKGROUP_INCR 0x1 -#define IOC_SSN_PASSWD 0x162 -#define IOC_SSN_PASSWD_INCR 0x1 -#define IOC_SSN_OPT 0x1e4 -#define IOC_TIMEOUT 0x1e8 -#define IOC_RETRYCOUNT 0x1ec -#define IOC_SSN_OWNER 0x1f0 -#define IOC_SSN_GROUP 0x1f4 -#define IOC_SSN_MODE 0x1f8 -#define IOC_SSN_RIGHTS 0x1fc -#define IOC_INTOKLEN 0x200 -#define IOC_OUTTOKLEN 0x204 -#define _IOC_INTOK 0x208 -#define _IOC_OUTTOK 0x210 -#define SIZEOF_SMBIOC_OSHARE 0x120 -#define IOC_SHARE 0x0 -#define IOC_SHARE_INCR 0x1 -#define IOC_SH_PASSWD 0x81 -#define IOC_SH_PASSWD_INCR 0x1 -#define IOC_SH_OPT 0x104 -#define IOC_STYPE 0x108 -#define IOC_SH_OWNER 0x10c -#define IOC_SH_GROUP 0x110 -#define IOC_SH_MODE 0x114 -#define IOC_SH_RIGHTS 0x118 -#define SIZEOF_SMBIOC_RQ 0x30 +#define ID_SRVADDR 0x0 +#define ID_DOMAIN 0x20 +#define ID_DOMAIN_INCR 0x1 +#define ID_USER 0x120 +#define ID_USER_INCR 0x1 +#define SSN_VOPT 0x0 +#define SSN_OWNER 0x4 +#define SSN_ID 0x8 +#define SSN_SRVNAME 0x228 +#define SSN_SRVNAME_INCR 0x1 +#define SH_PWLEN 0x0 +#define SH_NAME 0x4 +#define SH_NAME_INCR 0x1 +#define SH_PASS 0x104 +#define SH_PASS_INCR 0x1 +#define SH_TYPE_REQ 0x204 +#define SH_TYPE_REQ_INCR 0x1 +#define SH_TYPE_RET 0x20c +#define SH_TYPE_RET_INCR 0x1 +#define TC_FLAGS 0x0 +#define TC_OPT 0x4 +#define TC_SH 0x8 +#define SV_PROTO 0x0 +#define SV_SM 0x2 +#define SV_TZ 0x4 +#define SV_MAXMUX 0x6 +#define SV_MAXVCS 0x8 +#define SV_RAWMODE 0xa +#define SV_MAXTX 0xc +#define SV_MAXRAW 0x10 +#define SV_SKEY 0x14 +#define SV_CAPS 0x18 +#define IS_TRAN_FD 0x0 +#define IS_VCFLAGS 0x4 +#define IS_HFLAGS 0x8 +#define IS_HFLAGS2 0xa +#define IS_SMBUID 0xc +#define IS_NEXT_MID 0xe +#define IS_TXMAX 0x10 +#define IS_RWMAX 0x14 +#define IS_RXMAX 0x18 +#define IS_WXMAX 0x1c +#define IS_SSN_KEY 0x20 +#define IS_SSN_KEY_INCR 0x1 +#define IS_NEXT_SEQ 0x30 +#define IS_U_MACLEN 0x34 +#define IS_U_MACKEY 0x38 +#define WK_IODS 0x0 +#define WK_SOPT 0x40 +#define WK_OUT_STATE 0x5c +#define SIZEOF_SMBIOC_RQ 0x20 #define IOC_CMD 0x0 -#define IOC_TWC 0x1 -#define IOC_TBC 0x2 -#define IOC_RPBUFSZ 0x4 -#define IOC_RWC 0x9 -#define IOC_RBC 0xa -#define IOC_RQ_ERRCLASS 0xd -#define IOC_RQ_SERROR 0xe -#define IOC_RQ_ERROR 0x10 -#define _IOC_TWORDS 0x18 -#define _IOC_TBYTES 0x20 -#define _IOC_RPBUF 0x28 +#define IOC_RQ_ERRCLASS 0x1 +#define IOC_RQ_SERROR 0x2 +#define IOC_RQ_ERROR 0x4 +#define IOC_TBUFSZ 0x8 +#define IOC_RBUFSZ 0xc +#define _IOC_TBUF 0x10 +#define _IOC_RBUF 0x18 #define SIZEOF_SMBIOC_T2RQ 0xc0 #define IOC_SETUP 0x0 #define IOC_SETUP_INCR 0x2 @@ -71,22 +77,20 @@ #define _IOC_RDATA 0xb8 #define SIZEOF_SMBIOC_FLAGS 0xc #define IOC_LEVEL 0x0 -#define IOC_MASK 0x4 -#define IOC_FLAGS 0x8 -#define SIZEOF_SMBIOC_LOOKUP 0x340 -#define IOC_LOOK_LEVEL 0x0 -#define IOC_LOOK_FLAGS 0x4 -#define IOC_SH 0x8 -#define IOC_SSN 0x128 +#define IOC_MASK 0x8 +#define IOC_FLAGS 0x4 #define SIZEOF_SMBIOC_RW 0x18 #define IOC_FH 0x0 #define IOC_CNT 0x4 #define _IOC_OFFSET 0x8 #define _IOC_BASE 0x10 -#define SIZEOF_SMBIOC_PK 0x304 +#define SIZEOF_SMBIOC_PK 0x224 +#define PK_UID 0x0 #define PK_DOM 0x4 #define PK_DOM_INCR 0x1 #define PK_USR 0x104 #define PK_USR_INCR 0x1 -#define PK_PASS 0x204 -#define PK_PASS_INCR 0x1 +#define PK_LMHASH 0x204 +#define PK_LMHASH_INCR 0x1 +#define PK_NTHASH 0x214 +#define PK_NTHASH_INCR 0x1 diff --git a/usr/src/uts/sparc/nsmb/Makefile b/usr/src/uts/sparc/nsmb/Makefile index 85c05e9fcb..09b2bc72b9 100644 --- a/usr/src/uts/sparc/nsmb/Makefile +++ b/usr/src/uts/sparc/nsmb/Makefile @@ -21,10 +21,10 @@ # # uts/sparc/nsmb/Makefile # -# Copyright 2008 Sun Microsystems, Inc. All rights reserved. +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -#ident "%Z%%M% %I% %E% SMI" + # # sparc architecture dependent # @@ -94,6 +94,8 @@ LDFLAGS += -dy -Ncrypto/md4 -Ncrypto/md5 -Nmisc/tlimod LINTTAGS += -erroff=E_FUNC_RET_ALWAYS_IGNOR2 LINTTAGS += -erroff=E_FUNC_RET_MAYBE_IGNORED2 +# Until CR 4994570 is fixed... +LINTTAGS += -erroff=E_BAD_FORMAT_ARG_TYPE2 # # Default build targets. diff --git a/usr/src/uts/sparc/nsmb/ioc_check.ref b/usr/src/uts/sparc/nsmb/ioc_check.ref index ead69ec44f..76660e2ef5 100644 --- a/usr/src/uts/sparc/nsmb/ioc_check.ref +++ b/usr/src/uts/sparc/nsmb/ioc_check.ref @@ -1,56 +1,62 @@ -#define SIZEOF_SOCKADDR_ANY 0x18 -#define SIZEOF_SOCKADDR_IN 0x10 -#define SIZEOF_SOCKADDR_NB 0x18 -#define SIZEOF_SMBIOC_OSSN 0x218 -#define IOC_SERVER 0x0 -#define IOC_LOCAL 0x18 -#define IOC_LOCALCS 0x30 -#define IOC_LOCALCS_INCR 0x1 -#define IOC_SERVERCS 0x40 -#define IOC_SERVERCS_INCR 0x1 -#define IOC_SRVNAME 0x50 -#define IOC_SRVNAME_INCR 0x1 -#define IOC_USER 0x60 -#define IOC_USER_INCR 0x1 -#define IOC_WORKGROUP 0xe1 -#define IOC_WORKGROUP_INCR 0x1 -#define IOC_SSN_PASSWD 0x162 -#define IOC_SSN_PASSWD_INCR 0x1 -#define IOC_SSN_OPT 0x1e4 -#define IOC_TIMEOUT 0x1e8 -#define IOC_RETRYCOUNT 0x1ec -#define IOC_SSN_OWNER 0x1f0 -#define IOC_SSN_GROUP 0x1f4 -#define IOC_SSN_MODE 0x1f8 -#define IOC_SSN_RIGHTS 0x1fc -#define IOC_INTOKLEN 0x200 -#define IOC_OUTTOKLEN 0x204 -#define _IOC_INTOK 0x208 -#define _IOC_OUTTOK 0x210 -#define SIZEOF_SMBIOC_OSHARE 0x120 -#define IOC_SHARE 0x0 -#define IOC_SHARE_INCR 0x1 -#define IOC_SH_PASSWD 0x81 -#define IOC_SH_PASSWD_INCR 0x1 -#define IOC_SH_OPT 0x104 -#define IOC_STYPE 0x108 -#define IOC_SH_OWNER 0x10c -#define IOC_SH_GROUP 0x110 -#define IOC_SH_MODE 0x114 -#define IOC_SH_RIGHTS 0x118 -#define SIZEOF_SMBIOC_RQ 0x30 +#define ID_SRVADDR 0x0 +#define ID_DOMAIN 0x20 +#define ID_DOMAIN_INCR 0x1 +#define ID_USER 0x120 +#define ID_USER_INCR 0x1 +#define SSN_VOPT 0x0 +#define SSN_OWNER 0x4 +#define SSN_ID 0x8 +#define SSN_SRVNAME 0x228 +#define SSN_SRVNAME_INCR 0x1 +#define SH_PWLEN 0x0 +#define SH_NAME 0x4 +#define SH_NAME_INCR 0x1 +#define SH_PASS 0x104 +#define SH_PASS_INCR 0x1 +#define SH_TYPE_REQ 0x204 +#define SH_TYPE_REQ_INCR 0x1 +#define SH_TYPE_RET 0x20c +#define SH_TYPE_RET_INCR 0x1 +#define TC_FLAGS 0x0 +#define TC_OPT 0x4 +#define TC_SH 0x8 +#define SV_PROTO 0x0 +#define SV_SM 0x2 +#define SV_TZ 0x4 +#define SV_MAXMUX 0x6 +#define SV_MAXVCS 0x8 +#define SV_RAWMODE 0xa +#define SV_MAXTX 0xc +#define SV_MAXRAW 0x10 +#define SV_SKEY 0x14 +#define SV_CAPS 0x18 +#define IS_TRAN_FD 0x0 +#define IS_VCFLAGS 0x4 +#define IS_HFLAGS 0x8 +#define IS_HFLAGS2 0xa +#define IS_SMBUID 0xc +#define IS_NEXT_MID 0xe +#define IS_TXMAX 0x10 +#define IS_RWMAX 0x14 +#define IS_RXMAX 0x18 +#define IS_WXMAX 0x1c +#define IS_SSN_KEY 0x20 +#define IS_SSN_KEY_INCR 0x1 +#define IS_NEXT_SEQ 0x30 +#define IS_U_MACLEN 0x34 +#define IS_U_MACKEY 0x38 +#define WK_IODS 0x0 +#define WK_SOPT 0x40 +#define WK_OUT_STATE 0x5c +#define SIZEOF_SMBIOC_RQ 0x20 #define IOC_CMD 0x0 -#define IOC_TWC 0x1 -#define IOC_TBC 0x2 -#define IOC_RPBUFSZ 0x4 -#define IOC_RWC 0x9 -#define IOC_RBC 0xa -#define IOC_RQ_ERRCLASS 0xd -#define IOC_RQ_SERROR 0xe -#define IOC_RQ_ERROR 0x10 -#define _IOC_TWORDS 0x18 -#define _IOC_TBYTES 0x20 -#define _IOC_RPBUF 0x28 +#define IOC_RQ_ERRCLASS 0x1 +#define IOC_RQ_SERROR 0x2 +#define IOC_RQ_ERROR 0x4 +#define IOC_TBUFSZ 0x8 +#define IOC_RBUFSZ 0xc +#define _IOC_TBUF 0x10 +#define _IOC_RBUF 0x18 #define SIZEOF_SMBIOC_T2RQ 0xc0 #define IOC_SETUP 0x0 #define IOC_SETUP_INCR 0x2 @@ -71,22 +77,20 @@ #define _IOC_RDATA 0xb8 #define SIZEOF_SMBIOC_FLAGS 0xc #define IOC_LEVEL 0x0 -#define IOC_MASK 0x4 -#define IOC_FLAGS 0x8 -#define SIZEOF_SMBIOC_LOOKUP 0x340 -#define IOC_LOOK_LEVEL 0x0 -#define IOC_LOOK_FLAGS 0x4 -#define IOC_SH 0x8 -#define IOC_SSN 0x128 +#define IOC_MASK 0x8 +#define IOC_FLAGS 0x4 #define SIZEOF_SMBIOC_RW 0x18 #define IOC_FH 0x0 #define IOC_CNT 0x4 #define _IOC_OFFSET 0x8 #define _IOC_BASE 0x10 -#define SIZEOF_SMBIOC_PK 0x304 +#define SIZEOF_SMBIOC_PK 0x224 +#define PK_UID 0x0 #define PK_DOM 0x4 #define PK_DOM_INCR 0x1 #define PK_USR 0x104 #define PK_USR_INCR 0x1 -#define PK_PASS 0x204 -#define PK_PASS_INCR 0x1 +#define PK_LMHASH 0x204 +#define PK_LMHASH_INCR 0x1 +#define PK_NTHASH 0x214 +#define PK_NTHASH_INCR 0x1 |