/usr/src/lib/libsmbfs/smb/ctx.c
C | 1616 lines | 1121 code | 171 blank | 324 comment | 292 complexity | 3a2115365105468e7459a8049828b641 MD5 | raw file
Possible License(s): BSD-3-Clause-No-Nuclear-License-2014, MPL-2.0-no-copyleft-exception, BSD-3-Clause, BSD-2-Clause, LGPL-3.0, 0BSD, GPL-2.0, LGPL-2.0, AGPL-1.0, AGPL-3.0, GPL-3.0, LGPL-2.1
- /*
- * 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.
- *
- * $Id: ctx.c,v 1.32.70.2 2005/06/02 00:55:40 lindak Exp $
- */
- /*
- * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
- */
- #include <sys/param.h>
- #include <sys/ioctl.h>
- #include <sys/time.h>
- #include <sys/mount.h>
- #include <sys/types.h>
- #include <sys/byteorder.h>
- #include <fcntl.h>
- #include <ctype.h>
- #include <errno.h>
- #include <stdio.h>
- #include <string.h>
- #include <strings.h>
- #include <stdlib.h>
- #include <pwd.h>
- #include <grp.h>
- #include <unistd.h>
- #include <libintl.h>
- #include <assert.h>
- #include <nss_dbdefs.h>
- #include <cflib.h>
- #include <netsmb/smb_lib.h>
- #include <netsmb/netbios.h>
- #include <netsmb/nb_lib.h>
- #include <netsmb/smb_dev.h>
- #include "charsets.h"
- #include "spnego.h"
- #include "derparse.h"
- #include "private.h"
- #include "ntlm.h"
- #ifndef FALSE
- #define FALSE 0
- #endif
- #ifndef TRUE
- #define TRUE 1
- #endif
- struct nv {
- char *name;
- int value;
- };
- /* 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
- * originally a direct call into the RPC code.
- */
- static smb_ctx_close_hook_t close_hook;
- static void
- rpc_cleanup_smbctx(struct smb_ctx *ctx)
- {
- if (close_hook)
- (*close_hook)(ctx);
- }
- void
- smb_ctx_set_close_hook(smb_ctx_close_hook_t hook)
- {
- close_hook = hook;
- }
- void
- dump_ctx_flags(int flags)
- {
- printf(" Flags: ");
- if (flags == 0)
- printf("0");
- if (flags & SMBCF_NOPWD)
- printf("NOPWD ");
- if (flags & SMBCF_SRIGHTS)
- printf("SRIGHTS ");
- if (flags & SMBCF_LOCALE)
- printf("LOCALE ");
- if (flags & SMBCF_CMD_DOM)
- printf("CMD_DOM ");
- if (flags & SMBCF_CMD_USR)
- printf("CMD_USR ");
- if (flags & SMBCF_CMD_PW)
- printf("CMD_PW ");
- if (flags & SMBCF_RESOLVED)
- printf("RESOLVED ");
- if (flags & SMBCF_KCBAD)
- printf("KCBAD ");
- if (flags & SMBCF_KCFOUND)
- printf("KCFOUND ");
- if (flags & SMBCF_BROWSEOK)
- printf("BROWSEOK ");
- if (flags & SMBCF_AUTHREQ)
- printf("AUTHREQ ");
- if (flags & SMBCF_KCSAVE)
- printf("KCSAVE ");
- if (flags & SMBCF_XXX)
- printf("XXX ");
- if (flags & SMBCF_SSNACTIVE)
- printf("SSNACTIVE ");
- if (flags & SMBCF_KCDOMAIN)
- printf("KCDOMAIN ");
- printf("\n");
- }
- void
- dump_iod_ssn(smb_iod_ssn_t *is)
- {
- 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");
- 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
- dump_ctx(char *where, struct smb_ctx *ctx)
- {
- printf("context %s:\n", where);
- dump_ctx_flags(ctx->ct_flags);
- 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_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(" 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_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 (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_door_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;
- error = nb_ctx_setscope(ctx->ct_nb, "");
- if (error)
- return (error);
- /*
- * 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) {
- error = smb_ctx_setuser(ctx, pw.pw_name, 0);
- if (error)
- return (error);
- ctx->ct_home = strdup(pw.pw_name);
- if (ctx->ct_home == NULL)
- return (ENOMEM);
- }
- /*
- * Set a built-in default domain (workgroup).
- * Using the Windows/NT default for now.
- */
- error = smb_ctx_setdomain(ctx, "WORKGROUP", 0);
- if (error)
- return (error);
- 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:
- *
- * The search order for options is as follows:
- * command line options
- * values parsed from UNC path (cmd)
- * values from RC file (per-user)
- * values from SMF (system-wide)
- * built-in defaults
- *
- * Normally, one would simply get all the values starting with
- * the bottom of the above list and working to the top, and
- * overwriting values as you go. But we need an exception.
- *
- * In this function, we parse the UNC path and command line options,
- * because we need (at least) the server name when we're getting the
- * SMF and RC file values. However, values we get from the command
- * should not be overwritten by SMF or RC file parsing, so we mark
- * values from the command as "from CMD" and the RC file parser
- * leaves in place any values so marked. See: SMBCF_CMD_*
- *
- * The semantics of these flags are: "This value came from the
- * current command instance, not from sources that may apply to
- * multiple commands." (Different from the old "FROMUSR" flag.)
- *
- * Note that smb_ctx_opt() is called later to handle the
- * remaining options, which should be ignored here.
- * The (magic) leading ":" in cf_getopt() makes it
- * ignore options not in the options string.
- */
- int
- smb_ctx_scan_argv(struct smb_ctx *ctx, int argc, char **argv,
- int minlevel, int maxlevel, int sharetype)
- {
- int ind, opt, error = 0;
- int aflg = 0, uflg = 0;
- const char *arg;
- /*
- * Parse options, if any. Values from here too
- * are marked as "from CMD".
- */
- 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);
- ctx->ct_flags |= SMBCF_NOPWD;
- 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 (EINVAL);
- }
- /*
- * 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);
- if (ctx->ct_dev_fd != -1) {
- close(ctx->ct_dev_fd);
- ctx->ct_dev_fd = -1;
- }
- if (ctx->ct_door_fd != -1) {
- close(ctx->ct_door_fd);
- ctx->ct_door_fd = -1;
- }
- 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);
- ctx->ct_nb = NULL;
- }
- if (ctx->ct_locname) {
- free(ctx->ct_locname);
- ctx->ct_locname = NULL;
- }
- if (ctx->ct_origshare) {
- free(ctx->ct_origshare);
- 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
- getsubstring(const char *p, uchar_t sep, char *dest, int maxlen,
- const char **next)
- {
- int len;
- maxlen--;
- for (len = 0; len < maxlen && *p != sep; p++, len++, dest++) {
- if (*p == 0)
- return (EINVAL);
- *dest = *p;
- }
- *dest = 0;
- *next = *p ? p + 1 : p;
- return (0);
- }
- /*
- * Parse the UNC path. Here we expect something like
- * "//[workgroup;][user[:password]@]host[/share[/path]]"
- * See http://ietf.org/internet-drafts/draft-crhertel-smb-url-07.txt
- * Values found here are marked as "from CMD".
- */
- int
- 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;
- char tmp[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);
- error = EINVAL;
- goto out;
- }
- p1 = tmp;
- error = getsubstring(p, ';', p1, sizeof (tmp), &p);
- if (!error) {
- if (*p1 == 0) {
- smb_error(dgettext(TEXT_DOMAIN,
- "empty workgroup name"), 0);
- error = EINVAL;
- goto out;
- }
- error = smb_ctx_setdomain(ctx, unpercent(tmp), TRUE);
- if (error)
- goto out;
- }
- colon = (char *)p;
- error = getsubstring(p, '@', p1, sizeof (tmp), &p);
- if (!error) {
- if (ctx->ct_maxlevel < SMBL_VC) {
- smb_error(dgettext(TEXT_DOMAIN,
- "no user name required"), 0);
- error = EINVAL;
- goto out;
- }
- p1 = strchr(tmp, ':');
- if (p1) {
- colon += p1 - tmp;
- *p1++ = (char)0;
- error = smb_ctx_setpassword(ctx, unpercent(p1), TRUE);
- if (error)
- goto out;
- if (p - colon > 2)
- memset(colon+1, '*', p - colon - 2);
- }
- p1 = tmp;
- if (*p1 == 0) {
- smb_error(dgettext(TEXT_DOMAIN,
- "empty user name"), 0);
- error = EINVAL;
- goto out;
- }
- error = smb_ctx_setuser(ctx, unpercent(tmp), TRUE);
- if (error)
- goto out;
- ctx->ct_parsedlevel = SMBL_VC;
- }
- error = getsubstring(p, '/', p1, sizeof (tmp), &p);
- if (error) {
- error = getsubstring(p, '\0', p1, sizeof (tmp), &p);
- if (error) {
- smb_error(dgettext(TEXT_DOMAIN,
- "no server name found"), 0);
- goto out;
- }
- }
- if (*p1 == 0) {
- smb_error(dgettext(TEXT_DOMAIN, "empty server name"), 0);
- error = EINVAL;
- goto out;
- }
- /*
- * Save ct_fullserver without case conversion.
- */
- if (strchr(tmp, '%'))
- (void) unpercent(tmp);
- error = smb_ctx_setfullserver(ctx, tmp);
- if (error)
- goto out;
- #ifdef SMB_ST_NONE
- if (sharetype == SMB_ST_NONE) {
- 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);
- error = EINVAL;
- goto out;
- }
- error = getsubstring(p, '/', p1, sizeof (tmp), &p);
- if (error) {
- error = getsubstring(p, '\0', p1, sizeof (tmp), &p);
- if (error) {
- smb_error(dgettext(TEXT_DOMAIN,
- "unexpected end of line"), 0);
- goto out;
- }
- }
- if (*p1 == 0 && ctx->ct_minlevel >= SMBL_SHARE &&
- !(ctx->ct_flags & SMBCF_BROWSEOK)) {
- smb_error(dgettext(TEXT_DOMAIN, "empty share name"), 0);
- error = EINVAL;
- goto out;
- }
- if (next)
- *next = p;
- if (*p1 == 0) {
- error = 0;
- goto out;
- }
- 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)
- {
- char *cp, *servercs, *localcs;
- int cslen = sizeof (ctx->ct_ssn.ioc_localcs);
- int scslen, lcslen, error;
- cp = strchr(arg, ':');
- lcslen = cp ? (cp - arg) : 0;
- if (lcslen == 0 || lcslen >= cslen) {
- smb_error(dgettext(TEXT_DOMAIN,
- "invalid local charset specification (%s)"), 0, arg);
- return (EINVAL);
- }
- scslen = (size_t)strlen(++cp);
- if (scslen == 0 || scslen >= cslen) {
- smb_error(dgettext(TEXT_DOMAIN,
- "invalid server charset specification (%s)"), 0, arg);
- return (EINVAL);
- }
- localcs = memcpy(ctx->ct_ssn.ioc_localcs, arg, lcslen);
- localcs[lcslen] = 0;
- servercs = strcpy(ctx->ct_ssn.ioc_servercs, cp);
- error = nls_setrecode(localcs, servercs);
- if (error == 0)
- return (0);
- smb_error(dgettext(TEXT_DOMAIN,
- "can't initialize iconv support (%s:%s)"),
- error, localcs, servercs);
- localcs[0] = 0;
- servercs[0] = 0;
- return (error);
- }
- #endif /* KICONV_SUPPORT */
- int
- smb_ctx_setauthflags(struct smb_ctx *ctx, int flags)
- {
- ctx->ct_authflags = flags;
- return (0);
- }
- int
- smb_ctx_setfullserver(struct smb_ctx *ctx, const char *name)
- {
- char *p = strdup(name);
- if (p == NULL)
- return (ENOMEM);
- if (ctx->ct_fullserver)
- free(ctx->ct_fullserver);
- ctx->ct_fullserver = p;
- return (0);
- }
- int
- smb_ctx_setserver(struct smb_ctx *ctx, const char *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) >= sizeof (ctx->ct_user)) {
- smb_error(dgettext(TEXT_DOMAIN,
- "user name '%s' too long"), 0, name);
- return (ENAMETOOLONG);
- }
- /*
- * Don't overwrite a value from the command line
- * with one from anywhere else.
- */
- if (!from_cmd && (ctx->ct_flags & SMBCF_CMD_USR))
- return (0);
- strlcpy(ctx->ct_user, name,
- sizeof (ctx->ct_user));
- /* Mark this as "from the command line". */
- if (from_cmd)
- ctx->ct_flags |= SMBCF_CMD_USR;
- return (0);
- }
- /*
- * Don't overwrite a domain name from the
- * command line with one from anywhere else.
- * See smb_ctx_init() for notes about this.
- */
- int
- smb_ctx_setdomain(struct smb_ctx *ctx, const char *name, int from_cmd)
- {
- if (strlen(name) >= sizeof (ctx->ct_domain)) {
- smb_error(dgettext(TEXT_DOMAIN,
- "workgroup name '%s' too long"), 0, name);
- return (ENAMETOOLONG);
- }
- /*
- * Don't overwrite a value from the command line
- * with one from anywhere else.
- */
- if (!from_cmd && (ctx->ct_flags & SMBCF_CMD_DOM))
- return (0);
- strlcpy(ctx->ct_domain, name,
- sizeof (ctx->ct_domain));
- /* Mark this as "from the command line". */
- if (from_cmd)
- ctx->ct_flags |= SMBCF_CMD_DOM;
- return (0);
- }
- int
- smb_ctx_setpassword(struct smb_ctx *ctx, const char *passwd, int from_cmd)
- {
- int err;
- if (passwd == NULL)
- return (EINVAL);
- if (strlen(passwd) >= sizeof (ctx->ct_password)) {
- smb_error(dgettext(TEXT_DOMAIN, "password too long"), 0);
- return (ENAMETOOLONG);
- }
- /*
- * 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)
- (void) smb_simpledecrypt(ctx->ct_password, passwd);
- else
- 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)
- ctx->ct_flags |= SMBCF_CMD_PW;
- 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) >= SMBIOC_MAX_NAME) {
- smb_error(dgettext(TEXT_DOMAIN,
- "share name '%s' too long"), 0, share);
- return (ENAMETOOLONG);
- }
- if (ctx->ct_origshare)
- free(ctx->ct_origshare);
- if ((ctx->ct_origshare = strdup(share)) == NULL)
- return (ENOMEM);
- ctx->ct_shtype_req = stype;
- return (0);
- }
- int
- smb_ctx_setsrvaddr(struct smb_ctx *ctx, const char *addr)
- {
- if (addr == NULL || addr[0] == 0)
- return (EINVAL);
- if (ctx->ct_srvaddr_s)
- free(ctx->ct_srvaddr_s);
- if ((ctx->ct_srvaddr_s = strdup(addr)) == NULL)
- return (ENOMEM);
- return (0);
- }
- /*
- * API for library caller to set signing enabled, required
- * Note: if not enable, ignore require
- */
- int
- smb_ctx_setsigning(struct smb_ctx *ctx, int enable, int require)
- {
- ctx->ct_vopt &= ~SMBVOPT_SIGNING_MASK;
- if (enable) {
- ctx->ct_vopt |= SMBVOPT_SIGNING_ENABLED;
- if (require)
- ctx->ct_vopt |= SMBVOPT_SIGNING_REQUIRED;
- }
- return (0);
- }
- static int
- smb_parse_owner(char *pair, uid_t *uid, gid_t *gid)
- {
- struct group gr;
- struct passwd pw;
- char buf[NSS_BUFLEN_PASSWD];
- char *cp;
- cp = strchr(pair, ':');
- if (cp) {
- *cp++ = '\0';
- if (*cp && gid) {
- if (getgrnam_r(cp, &gr, buf, sizeof (buf)) != NULL) {
- *gid = gr.gr_gid;
- } else
- smb_error(dgettext(TEXT_DOMAIN,
- "Invalid group name %s, ignored"), 0, cp);
- }
- }
- if (*pair) {
- if (getpwnam_r(pair, &pw, buf, sizeof (buf)) != NULL) {
- *uid = pw.pw_uid;
- } else
- smb_error(dgettext(TEXT_DOMAIN,
- "Invalid user name %s, ignored"), 0, pair);
- }
- return (0);
- }
- /*
- * Suport a securty options arg, i.e. -S noext,lm,ntlm
- * for testing various type of authenticators.
- */
- static struct nv
- sectype_table[] = {
- /* noext - handled below */
- { "anon", SMB_AT_ANON },
- { "lm", SMB_AT_LM1 },
- { "ntlm", SMB_AT_NTLM1 },
- { "ntlm2", SMB_AT_NTLM2 },
- { "krb5", SMB_AT_KRB5 },
- { NULL, 0 },
- };
- int
- smb_parse_secopts(struct smb_ctx *ctx, const char *arg)
- {
- const char *sep = ":;,";
- const char *p = arg;
- struct nv *nv;
- int nlen, tlen;
- int authflags = 0;
- for (;;) {
- /* skip separators */
- tlen = strspn(p, sep);
- p += tlen;
- nlen = strcspn(p, sep);
- if (nlen == 0)
- break;
- if (nlen == 5 && 0 == strncmp(p, "noext", nlen)) {
- /* Don't offer extended security. */
- ctx->ct_vopt &= ~SMBVOPT_EXT_SEC;
- p += nlen;
- continue;
- }
- /* This is rarely called, so not optimized. */
- for (nv = sectype_table; nv->name; nv++) {
- tlen = strlen(nv->name);
- if (tlen == nlen && 0 == strncmp(p, nv->name, tlen))
- break;
- }
- if (nv->name == NULL) {
- smb_error(dgettext(TEXT_DOMAIN,
- "%s: invalid security options"), 0, p);
- return (EINVAL);
- }
- authflags |= nv->value;
- p += nlen;
- }
- if (authflags)
- ctx->ct_authflags = authflags;
- return (0);
- }
- /*
- * Commands use this with getopt. See:
- * STDPARAM_OPT, STDPARAM_ARGS
- * Called after smb_ctx_readrc().
- */
- int
- smb_ctx_opt(struct smb_ctx *ctx, int opt, const char *arg)
- {
- int error = 0;
- char *p, *cp;
- char tmp[1024];
- switch (opt) {
- case 'A':
- case 'U':
- /* Handled in smb_ctx_init() */
- break;
- case 'I':
- error = smb_ctx_setsrvaddr(ctx, arg);
- break;
- case 'M':
- /* share connect rights - ignored */
- ctx->ct_flags |= SMBCF_SRIGHTS;
- break;
- case 'N':
- ctx->ct_flags |= SMBCF_NOPWD;
- break;
- case 'O':
- p = strdup(arg);
- cp = strchr(p, '/');
- if (cp)
- *cp = '\0';
- error = smb_parse_owner(cp, &ctx->ct_owner, NULL);
- free(p);
- break;
- case 'P':
- /* ctx->ct_vopt |= SMBCOPT_PERMANENT; */
- break;
- case 'R':
- /* retry count - ignored */
- break;
- case 'S':
- /* Security options (undocumented, just for tests) */
- error = smb_parse_secopts(ctx, arg);
- break;
- case 'T':
- /* timeout - ignored */
- break;
- case 'D': /* domain */
- case 'W': /* workgroup (legacy alias) */
- error = smb_ctx_setdomain(ctx, tmp, TRUE);
- break;
- }
- return (error);
- }
- /*
- * 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 = 0;
- error = kiconv_add_xlat_table(to, from, tbl);
- if (error && error != EEXIST) {
- smb_error(dgettext(TEXT_DOMAIN,
- "can not setup kernel iconv table (%s:%s)"),
- error, from, to);
- return (error);
- }
- return (error);
- }
- #endif /* KICONV_SUPPORT */
- /*
- * 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;
- int error = 0;
- #ifdef KICONV_SUPPORT
- uchar_t cstbl[256];
- uint_t i;
- #endif
- if (smb_debug)
- dump_ctx("before smb_ctx_resolve", ctx);
- ctx->ct_flags &= ~SMBCF_RESOLVED;
- if (ctx->ct_fullserver == NULL) {
- smb_error(dgettext(TEXT_DOMAIN,
- "no server name specified"), 0);
- return (EINVAL);
- }
- if (ctx->ct_minlevel >= SMBL_SHARE &&
- ctx->ct_origshare == NULL) {
- smb_error(dgettext(TEXT_DOMAIN,
- "no share name specified for %s@%s"),
- 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);
- if (error)
- return (error);
- error = smb_addiconvtbl("toupper", ssn->ioc_localcs, nls_upper);
- if (error)
- return (error);
- if (ssn->ioc_servercs[0] != 0) {
- for (i = 0; i < sizeof (cstbl); i++)
- cstbl[i] = i;
- nls_mem_toext(cstbl, cstbl, sizeof (cstbl));
- error = smb_addiconvtbl(ssn->ioc_servercs, ssn->ioc_localcs,
- cstbl);
- if (error)
- return (error);
- for (i = 0; i < sizeof (cstbl); i++)
- cstbl[i] = i;
- nls_mem_toloc(cstbl, cstbl, sizeof (cstbl));
- error = smb_addiconvtbl(ssn->ioc_localcs, ssn->ioc_servercs,
- cstbl);
- if (error)
- return (error);
- }
- #endif /* KICONV_SUPPORT */
- /*
- * Lookup the IP address and fill in ct_addrinfo.
- *
- * Note: smb_ctx_getaddr() returns a EAI_xxx
- * error value like getaddrinfo(3), but this
- * function needs to return an errno value.
- */
- error = smb_ctx_getaddr(ctx);
- if (error) {
- const char *ais = gai_strerror(error);
- smb_error(dgettext(TEXT_DOMAIN,
- "can't resolve name\"%s\", %s"),
- 0, ctx->ct_fullserver, ais);
- return (ENODATA);
- }
- assert(ctx->ct_addrinfo != NULL);
- /*
- * 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') {
- /*
- * No user name (anonymous session).
- * The minauth checks do not apply.
- */
- ctx->ct_authflags = SMB_AT_ANON;
- } else {
- /*
- * Have a user name.
- * If we don't have a p/w yet,
- * try the keychain.
- */
- if (ctx->ct_password[0] == '\0')
- (void) smb_get_keychain(ctx);
- /*
- * 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;
- if (smb_debug)
- dump_ctx("after smb_ctx_resolve", ctx);
- return (0);
- }
- int
- smb_open_driver()
- {
- int err, fd;
- uint32_t version;
- fd = open("/dev/"NSMB_NAME, O_RDWR);
- if (fd < 0) {
- err = errno;
- smb_error(dgettext(TEXT_DOMAIN,
- "failed to open driver"), err);
- return (-1);
- }
- /*
- * Check the driver version (paranoia)
- * Do this BEFORE any other ioctl calls.
- */
- if (ioctl(fd, SMBIOC_GETVERS, &version) < 0)
- version = 0;
- if (version != NSMB_VERSION) {
- smb_error(dgettext(TEXT_DOMAIN,
- "incorrect driver version"), 0);
- close(fd);
- return (-1);
- }
- /* This handle controls per-process resources. */
- (void) fcntl(fd, F_SETFD, FD_CLOEXEC);
- return (fd);
- }
- int
- smb_ctx_gethandle(struct smb_ctx *ctx)
- {
- int fd;
- if (ctx->ct_dev_fd != -1) {
- rpc_cleanup_smbctx(ctx);
- close(ctx->ct_dev_fd);
- ctx->ct_dev_fd = -1;
- ctx->ct_flags &= ~SMBCF_SSNACTIVE;
- }
- fd = smb_open_driver();
- if (fd < 0)
- return (ENODEV);
- ctx->ct_dev_fd = fd;
- return (0);
- }
- /*
- * Find or create a connection + logon session
- */
- int
- smb_ctx_get_ssn(struct smb_ctx *ctx)
- {
- int err = 0;
- if ((ctx->ct_flags & SMBCF_RESOLVED) == 0)
- return (EINVAL);
- if (ctx->ct_dev_fd < 0) {
- if ((err = smb_ctx_gethandle(ctx)))
- return (err);
- }
- /*
- * Check whether the driver already has a VC
- * we can use. If so, we're done!
- */
- err = smb_ctx_findvc(ctx);
- if (err == 0) {
- DPRINT("found an existing VC");
- } else {
- /*
- * This calls the IOD to create a new session.
- */
- DPRINT("setup a new VC");
- err = smb_ctx_newvc(ctx);
- if (err != 0)
- return (err);
- /*
- * Call findvc again. The new VC sould be
- * found in the driver this time.
- */
- err = smb_ctx_findvc(ctx);
- }
- return (err);
- }
- /*
- * Get the string representation of a share "use" type,
- * as needed for the "service" in tree connect.
- */
- static const char *
- smb_use_type_str(smb_use_shtype_t stype)
- {
- const char *pp;
- 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 (pp);
- }
- /*
- * Find or create a tree connection
- */
- int
- smb_ctx_get_tree(struct smb_ctx *ctx)
- {
- smbioc_tcon_t *tcon = NULL;
- const char *stype;
- int cmd, err = 0;
- if (ctx->ct_dev_fd < 0 ||
- ctx->ct_origshare == NULL) {
- return (EINVAL);
- }
- 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;
- /* The share name */
- strlcpy(tcon->tc_sh.sh_name, ctx->ct_origshare,
- sizeof (tcon->tc_sh.sh_name));
- /*
- * Share password (unused - no share-level security)
- * MS-SMB 2.2.6 says this should be null terminated,
- * and the length includes the null. Did bzero above,
- * so just set length for the null.
- */
- tcon->tc_sh.sh_pwlen = 1;
- /* 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));
- /*
- * Todo: share passwords for share-level security.
- *
- * The driver does the actual TCON call.
- */
- if (ioctl(ctx->ct_dev_fd, cmd, tcon) == -1) {
- err = errno;
- goto out;
- }
- /*
- * Check the returned share type
- */
- 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;
- }
- out:
- if (tcon != NULL)
- free(tcon);
- return (err);
- }
- /*
- * Return the hflags2 word for an smb_ctx.
- */
- int
- smb_ctx_flags2(struct smb_ctx *ctx)
- {
- uint16_t flags2;
- if (ioctl(ctx->ct_dev_fd, SMBIOC_FLAGS2, &flags2) == -1) {
- smb_error(dgettext(TEXT_DOMAIN,
- "can't get flags2 for a session"), errno);
- return (-1);
- }
- return (flags2);
- }
- /*
- * Get the transport level session key.
- * Must already have an active SMB session.
- */
- int
- smb_ctx_get_ssnkey(struct smb_ctx *ctx, uchar_t *key, size_t len)
- {
- if (len < SMBIOC_HASH_SZ)
- return (EINVAL);
- if (ioctl(ctx->ct_dev_fd, SMBIOC_GETSSNKEY, key) == -1)
- return (errno);
- return (0);
- }
- /*
- * RC file parsing stuff
- */
- static struct nv
- 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:
- * 0 - default
- * 1 - server
- * 2 - server:user
- * 3 - server:user:share
- */
- static int
- smb_ctx_readrcsection(struct smb_ctx *ctx, const char *sname, int level)
- {
- char *p;
- int error;
- #ifdef KICONV_SUPPORT
- if (level > 0) {
- rc_getstringptr(smb_rc, sname, "charsets", &p);
- if (p) {
- error = smb_ctx_setcharset(ctx, p);
- if (error)
- smb_error(dgettext(TEXT_DOMAIN,
- "charset specification in the section '%s' ignored"),
- error, sname);
- }
- }
- #endif
- if (level <= 1) {
- /* Section is: [default] or [server] */
- rc_getstringptr(smb_rc, sname, "minauth", &p);
- if (p) {
- /*
- * "minauth" was set in this section; override
- * the current minimum authentication setting.
- */
- 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.
- */
- smb_error(dgettext(TEXT_DOMAIN,
- "invalid minimum authentication level \"%s\" specified in the section %s"),
- 0, p, sname);
- return (EINVAL);
- }
- }
- rc_getstringptr(smb_rc, sname, "signing", &p);
- if (p) {
- /*
- * "signing" was set in this section; override
- * the current signing settings. Note:
- * setsigning flags are: enable, require
- */
- if (strcmp(p, "disabled") == 0) {
- (void) smb_ctx_setsigning(ctx, FALSE, FALSE);
- } else if (strcmp(p, "enabled") == 0) {
- (void) smb_ctx_setsigning(ctx, TRUE, FALSE);
- } else if (strcmp(p, "required") == 0) {
- (void) smb_ctx_setsigning(ctx, TRUE, TRUE);
- } else {
- /*
- * Unknown "signing" value.
- */
- smb_error(dgettext(TEXT_DOMAIN,
- "invalid signing policy \"%s\" specified in the section %s"),
- 0, p, sname);
- return (EINVAL);
- }
- }
- /*
- * Domain name. Allow both keywords:
- * "workgroup", "domain"
- *
- * Note: these are NOT marked "from CMD".
- * See long comment at smb_ctx_init()
- */
- rc_getstringptr(smb_rc, sname, "workgroup", &p);
- if (p) {
- error = smb_ctx_setdomain(ctx, p, 0);
- if (error)
- smb_error(dgettext(TEXT_DOMAIN,
- "workgroup specification in the "
- "section '%s' ignored"), error, sname);
- }
- rc_getstringptr(smb_rc, sname, "domain", &p);
- if (p) {
- error = smb_ctx_setdomain(ctx, p, 0);
- if (error)
- smb_error(dgettext(TEXT_DOMAIN,
- "domain specification in the "
- "section '%s' ignored"), error, sname);
- }
- rc_getstringptr(smb_rc, sname, "user", &p);
- if (p) {
- error = smb_ctx_setuser(ctx, p, 0);
- if (error)
- smb_error(dgettext(TEXT_DOMAIN,
- "user specification in the "
- "section '%s' ignored"), error, sname);
- }
- }
- if (level == 1) {
- /* Section is: [server] */
- rc_getstringptr(smb_rc, sname, "addr", &p);
- if (p) {
- error = smb_ctx_setsrvaddr(ctx, p);
- if (error) {
- smb_error(dgettext(TEXT_DOMAIN,
- "invalid address specified in section %s"),
- 0, sname);
- return (error);
- }
- }
- }
- rc_getstringptr(smb_rc, sname, "password", &p);
- if (p) {
- error = smb_ctx_setpassword(ctx, p, 0);
- if (error)
- smb_error(dgettext(TEXT_DOMAIN,
- "password specification in the section '%s' ignored"),
- error, sname);
- }
- return (0);
- }
- /*
- * read rc file as follows:
- * 0: read [default] section
- * 1: override with [server] section
- * 2: override with [server:user] section
- * 3: override with [server:user:share] section
- * Since absence of rcfile is not fatal, silently ignore this fact.
- * smb_rc file should be closed by caller.
- */
- int
- smb_ctx_readrc(struct smb_ctx *ctx)
- {
- 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);
- }
- sname_max = 3 * SMBIOC_MAX_NAME + 4;
- sname = malloc(sname_max);
- if (sname == NULL) {
- err = ENOMEM;
- goto done;
- }
- /*
- * default parameters (level=0)
- */
- smb_ctx_readrcsection(ctx, "default", 0);
- nb_ctx_readrcsection(smb_rc, ctx->ct_nb, "default", 0);
- /*
- * If we don't have a server name, we can't read any of the
- * [server...] sections.
- */
- if (ctx->ct_fullserver == NULL)
- goto done;
- /*
- * SERVER parameters.
- */
- 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_user[0] == 0)
- goto done;
- /*
- * SERVER:USER parameters
- */
- 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_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 (err);
- }