smbfs_ctx.c revision 2
2N/A/*
2N/A * Copyright (c) 2000, Boris Popov
2N/A * All rights reserved.
2N/A *
2N/A * Redistribution and use in source and binary forms, with or without
2N/A * modification, are permitted provided that the following conditions
2N/A * are met:
2N/A * 1. Redistributions of source code must retain the above copyright
2N/A * notice, this list of conditions and the following disclaimer.
2N/A * 2. Redistributions in binary form must reproduce the above copyright
2N/A * notice, this list of conditions and the following disclaimer in the
2N/A * documentation and/or other materials provided with the distribution.
2N/A * 3. All advertising materials mentioning features or use of this software
2N/A * must display the following acknowledgement:
2N/A * This product includes software developed by Boris Popov.
2N/A * 4. Neither the name of the author nor the names of any co-contributors
2N/A * may be used to endorse or promote products derived from this software
2N/A * without specific prior written permission.
2N/A *
2N/A * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
2N/A * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2N/A * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2N/A * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2N/A * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2N/A * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2N/A * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2N/A * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2N/A * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2N/A * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2N/A * SUCH DAMAGE.
2N/A */
2N/A
2N/A/*
2N/A * Copyright (c) 2008, 2012, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A#include <sys/param.h>
2N/A#include <sys/ioctl.h>
2N/A#include <sys/time.h>
2N/A#include <sys/mount.h>
2N/A#include <sys/types.h>
2N/A#include <sys/byteorder.h>
2N/A
2N/A#include <fcntl.h>
2N/A#include <ctype.h>
2N/A#include <errno.h>
2N/A#include <stdio.h>
2N/A#include <string.h>
2N/A#include <strings.h>
2N/A#include <stdlib.h>
2N/A#include <pwd.h>
2N/A#include <grp.h>
2N/A#include <unistd.h>
2N/A#include <libintl.h>
2N/A#include <assert.h>
2N/A#include <nss_dbdefs.h>
2N/A
2N/A#include "smbfs_lib.h"
2N/A#include <netsmb/netbios.h>
2N/A#include "smbfs_nb_lib.h"
2N/A#include <netsmb/smb_dev.h>
2N/A#include <smbsrv/libsmb.h>
2N/A
2N/A#include "smbfs_charsets.h"
2N/A#include "smbfs_private.h"
2N/A#include "smbfs_ntlm.h"
2N/A
2N/A#ifndef FALSE
2N/A#define FALSE 0
2N/A#endif
2N/A#ifndef TRUE
2N/A#define TRUE 1
2N/A#endif
2N/A
2N/Astruct nv {
2N/A char *name;
2N/A int value;
2N/A};
2N/A
2N/A/* These two may be set by commands. */
2N/Aint smbfs_debug;
2N/A
2N/A/*
2N/A * Defaults for new contexts (connections to servers).
2N/A * These are set by smbfs_set_default_...
2N/A */
2N/Astatic char default_domain[SMBIOC_MAX_NAME];
2N/Astatic char default_user[SMBIOC_MAX_NAME];
2N/A
2N/A
2N/A/*
2N/A * Give the RPC library a callback hook that will be
2N/A * called whenever we destroy or reinit an smb_ctx_t.
2N/A * [The legacy name is rpc_cleanup_smbctx(), and was
2N/A * originally a direct call into the RPC code.]
2N/A */
2N/Astatic smb_ctx_close_hook_t close_hook;
2N/Astatic void
2N/Asmbfs_ctx_rpc_cleanup(struct smb_ctx *ctx)
2N/A{
2N/A if (close_hook)
2N/A (*close_hook)(ctx);
2N/A}
2N/A
2N/Aint
2N/Asmbfs_ctx_alloc(struct smb_ctx **ctx_pp)
2N/A{
2N/A smb_ctx_t *ctx;
2N/A int err;
2N/A
2N/A ctx = malloc(sizeof (*ctx));
2N/A if (ctx == NULL)
2N/A return (ENOMEM);
2N/A err = smbfs_ctx_init(ctx);
2N/A if (err != 0) {
2N/A free(ctx);
2N/A return (err);
2N/A }
2N/A *ctx_pp = ctx;
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * Get configurations from SMF.
2N/A *
2N/A * The LM authentication levels are shown in the table below.
2N/A *
2N/A * To enable Kerberos authentication, Kerberos flag is always set with all LM
2N/A * compatibility levels during SMB context initialization.
2N/A *
2N/A * Client Server
2N/A * +---+------------------------------+-----------------------------+
2N/A * | 1 | Uses LM and NTLM | Accepts LM, NTLM and NTLMv2 |
2N/A * | | authentication | authentication |
2N/A * +---+------------------------------+-----------------------------+
2N/A * | 2 | Uses NTLM authentication | Accepts LM, NTLM and NTLMv2 |
2N/A * | | | authentication |
2N/A * +---+------------------------------+-----------------------------+
2N/A * | 3 | Uses NTLMv2 authentication | Accepts LM, NTLM and NTLMv2 |
2N/A * | | | authentication |
2N/A * +---+------------------------------+-----------------------------+
2N/A * | 4 | Uses NTLMv2 authentication | Accepts NTLM and NTLMv2 |
2N/A * | | | authentication |
2N/A * +---+------------------------------+-----------------------------+
2N/A * | 5 | Uses NTLMv2 authentication | Accepts NTLMv2 |
2N/A * | | | authentication |
2N/A * +---+------------------------------+-----------------------------+
2N/A */
2N/Astatic void
2N/Asmbfs_ctx_getconfigs(struct smb_ctx *ctx)
2N/A{
2N/A char domain[SMB_PI_MAX_DOMAIN];
2N/A int64_t lmlevel;
2N/A boolean_t signing_req;
2N/A
2N/A if (smb_config_getnum(SMB_CI_CLNT_LM_LEVEL, &lmlevel) == SMBD_SMF_OK) {
2N/A switch (lmlevel) {
2N/A case 1:
2N/A ctx->ct_authflags =
2N/A SMB_AT_LM1 | SMB_AT_NTLM1 | SMB_AT_KRB5;
2N/A break;
2N/A case 2:
2N/A ctx->ct_authflags = SMB_AT_NTLM1 | SMB_AT_KRB5;
2N/A break;
2N/A case 3:
2N/A case 4:
2N/A case 5:
2N/A ctx->ct_authflags = SMB_AT_NTLM2 | SMB_AT_KRB5;
2N/A break;
2N/A default:
2N/A /* no change from default: SMB_AT_DEFAULT */
2N/A break;
2N/A }
2N/A }
2N/A
2N/A signing_req = smb_config_getbool(SMB_CI_CLNT_SIGNING_REQD);
2N/A (void) smbfs_ctx_setsigning(ctx, signing_req);
2N/A
2N/A (void) smb_config_getstr(SMB_CI_DOMAIN_NB, domain,
2N/A sizeof (domain));
2N/A
2N/A if (*domain != '\0')
2N/A (void) smbfs_ctx_setdomain(ctx, domain, FALSE);
2N/A
2N/A (void) smbfs_nb_ctx_getconfigs(ctx->ct_nb);
2N/A}
2N/A
2N/A/*
2N/A * Initialize an smb_ctx struct (defaults)
2N/A *
2N/A * By default, SMB signing is enabled but not require.
2N/A */
2N/Aint
2N/Asmbfs_ctx_init(struct smb_ctx *ctx)
2N/A{
2N/A int error;
2N/A
2N/A bzero(ctx, sizeof (*ctx));
2N/A
2N/A error = smbfs_nb_ctx_create(&ctx->ct_nb);
2N/A if (error)
2N/A return (error);
2N/A
2N/A ctx->ct_dev_fd = -1;
2N/A ctx->ct_door_fd = -1;
2N/A ctx->ct_tran_fd = -1;
2N/A ctx->ct_parsedlevel = SMBL_NONE;
2N/A ctx->ct_minlevel = SMBL_NONE;
2N/A ctx->ct_maxlevel = SMBL_PATH;
2N/A
2N/A /* Fill in defaults */
2N/A ctx->ct_vopt = SMBVOPT_EXT_SEC;
2N/A ctx->ct_owner = SMBM_ANY_OWNER;
2N/A ctx->ct_authflags = SMB_AT_DEFAULT;
2N/A ctx->ct_ntstatus = NT_STATUS_SUCCESS;
2N/A
2N/A (void) smbfs_ctx_setsigning(ctx, FALSE);
2N/A
2N/A smbfs_ctx_getconfigs(ctx);
2N/A
2N/A /*
2N/A * Default domain, user, ...
2N/A */
2N/A if (*default_domain != '\0')
2N/A (void) strlcpy(ctx->ct_domain, default_domain,
2N/A sizeof (ctx->ct_domain));
2N/A (void) strlcpy(ctx->ct_user, default_user,
2N/A sizeof (ctx->ct_user));
2N/A
2N/A return (0);
2N/A}
2N/A
2N/Avoid
2N/Asmbfs_ctx_free(smb_ctx_t *ctx)
2N/A{
2N/A if (ctx == NULL)
2N/A return;
2N/A
2N/A smbfs_ctx_done(ctx);
2N/A free(ctx);
2N/A}
2N/A
2N/Avoid
2N/Asmbfs_ctx_done(struct smb_ctx *ctx)
2N/A{
2N/A smbfs_ctx_rpc_cleanup(ctx);
2N/A
2N/A if (ctx->ct_dev_fd != -1) {
2N/A (void) close(ctx->ct_dev_fd);
2N/A ctx->ct_dev_fd = -1;
2N/A }
2N/A if (ctx->ct_door_fd != -1) {
2N/A (void) close(ctx->ct_door_fd);
2N/A ctx->ct_door_fd = -1;
2N/A }
2N/A if (ctx->ct_tran_fd != -1) {
2N/A (void) close(ctx->ct_tran_fd);
2N/A ctx->ct_tran_fd = -1;
2N/A }
2N/A if (ctx->ct_srvaddr_s) {
2N/A free(ctx->ct_srvaddr_s);
2N/A ctx->ct_srvaddr_s = NULL;
2N/A }
2N/A if (ctx->ct_nb) {
2N/A smbfs_nb_ctx_done(ctx->ct_nb);
2N/A ctx->ct_nb = NULL;
2N/A }
2N/A if (ctx->ct_locname) {
2N/A free(ctx->ct_locname);
2N/A ctx->ct_locname = NULL;
2N/A }
2N/A if (ctx->ct_origshare) {
2N/A free(ctx->ct_origshare);
2N/A ctx->ct_origshare = NULL;
2N/A }
2N/A if (ctx->ct_fullserver) {
2N/A free(ctx->ct_fullserver);
2N/A ctx->ct_fullserver = NULL;
2N/A }
2N/A if (ctx->ct_addrinfo) {
2N/A freeaddrinfo(ctx->ct_addrinfo);
2N/A ctx->ct_addrinfo = NULL;
2N/A }
2N/A if (ctx->ct_rpath)
2N/A free(ctx->ct_rpath);
2N/A if (ctx->ct_srv_OS) {
2N/A free(ctx->ct_srv_OS);
2N/A ctx->ct_srv_OS = NULL;
2N/A }
2N/A if (ctx->ct_srv_LM) {
2N/A free(ctx->ct_srv_LM);
2N/A ctx->ct_srv_LM = NULL;
2N/A }
2N/A if (ctx->ct_mackey) {
2N/A free(ctx->ct_mackey);
2N/A ctx->ct_mackey = NULL;
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * Parse the UNC path. Here we expect something like
2N/A * "//host[/share[/path]]"
2N/A */
2N/Aint
2N/Asmbfs_ctx_parseunc(struct smb_ctx *ctx, const char *unc,
2N/A int minlevel, int maxlevel, int sharetype,
2N/A const char **next)
2N/A{
2N/A char tmp[1024];
2N/A char *host, *share, *path;
2N/A char *p;
2N/A int error;
2N/A
2N/A /*
2N/A * This may be called outside of _scan_argv,
2N/A * so make sure these get initialized.
2N/A */
2N/A ctx->ct_minlevel = minlevel;
2N/A ctx->ct_maxlevel = maxlevel;
2N/A ctx->ct_shtype_req = sharetype;
2N/A ctx->ct_parsedlevel = SMBL_NONE;
2N/A
2N/A host = NULL;
2N/A
2N/A /* Work on a temporary copy, fix back slashes. */
2N/A (void) strlcpy(tmp, unc, sizeof (tmp));
2N/A for (p = tmp; *p; p++)
2N/A if (*p == '\\')
2N/A *p = '/';
2N/A
2N/A if (tmp[0] != '/' || tmp[1] != '/') {
2N/A smbfs_error(dgettext(TEXT_DOMAIN,
2N/A "UNC should start with '//'"), 0);
2N/A error = EINVAL;
2N/A goto out;
2N/A }
2N/A p = tmp + 2;
2N/A
2N/A /* Find the share part, if any. */
2N/A share = strchr(p, '/');
2N/A if (share)
2N/A *share = '\0';
2N/A (void) smbfs_unpercent(p); /* host component */
2N/A
2N/A host = p;
2N/A
2N/A if (*host == '\0') {
2N/A smbfs_error(dgettext(TEXT_DOMAIN, "empty server name"), 0);
2N/A error = EINVAL;
2N/A goto out;
2N/A }
2N/A error = smbfs_ctx_setfullserver(ctx, host);
2N/A if (error)
2N/A goto out;
2N/A ctx->ct_parsedlevel = SMBL_VC;
2N/A
2N/A if (share) {
2N/A /* restore the slash */
2N/A *share = '/';
2N/A p = share + 1;
2N/A
2N/A /* Find the path part, if any. */
2N/A path = strchr(p, '/');
2N/A if (path)
2N/A *path = '\0';
2N/A (void) smbfs_unpercent(p); /* share component */
2N/A
2N/A if (*p == '\0') {
2N/A smbfs_error(dgettext(TEXT_DOMAIN,
2N/A "empty share name"), 0);
2N/A error = EINVAL;
2N/A goto out;
2N/A }
2N/A if (ctx->ct_maxlevel < SMBL_SHARE) {
2N/A smbfs_error(dgettext(TEXT_DOMAIN,
2N/A "no share name required"), 0);
2N/A error = EINVAL;
2N/A goto out;
2N/A }
2N/A
2N/A /*
2N/A * Special case UNC names like:
2N/A * //host/PIPE/endpoint
2N/A * to have share: IPC$
2N/A */
2N/A if (0 == strcasecmp(p, "PIPE")) {
2N/A sharetype = USE_IPC;
2N/A p = "IPC$";
2N/A }
2N/A error = smbfs_ctx_setshare(ctx, p, sharetype);
2N/A if (error)
2N/A goto out;
2N/A ctx->ct_parsedlevel = SMBL_SHARE;
2N/A
2N/A if (path) {
2N/A /* restore the slash */
2N/A *path = '/';
2N/A p = path + 1;
2N/A (void) smbfs_unpercent(p); /* remainder */
2N/A free(ctx->ct_rpath);
2N/A ctx->ct_rpath = strdup(path);
2N/A }
2N/A } else if (ctx->ct_minlevel >= SMBL_SHARE) {
2N/A smbfs_error(dgettext(TEXT_DOMAIN, "empty share name"), 0);
2N/A error = EINVAL;
2N/A goto out;
2N/A }
2N/A
2N/A if (next)
2N/A *next = NULL;
2N/A
2N/Aout:
2N/A if (error == 0 && smbfs_debug > 0)
2N/A smbfs_dump_ctx("after smbfs_ctx_parseunc", ctx);
2N/A
2N/A return (error);
2N/A}
2N/A
2N/A/*
2N/A * Parse the string: domuser, which may be any of:
2N/A * "user@domain" or "domain/user" or "domain\\user"
2N/A * and return pointers to the domain and user parts.
2N/A * Modifies the string domuser in-place. Returned
2N/A * string pointers are within the string domusr.
2N/A */
2N/Aint
2N/Asmbfs_ctx_parsedomuser(char *domuser, char **dom, char **usr)
2N/A{
2N/A const char sep[] = "@/\\";
2N/A char sc, *p, *s1, *s2;
2N/A
2N/A p = strpbrk(domuser, sep);
2N/A if (p == NULL) {
2N/A /* No separators - whole string is the user. */
2N/A *dom = NULL;
2N/A *usr = domuser;
2N/A return (0);
2N/A }
2N/A
2N/A /* Have two strings. */
2N/A s1 = domuser;
2N/A sc = *p; /* Save the sep. char */
2N/A *p++ = '\0'; /* zap it */
2N/A s2 = p;
2N/A
2N/A /* Enforce just one separator */
2N/A p = strpbrk(s2, sep);
2N/A if (p)
2N/A return (-1);
2N/A
2N/A /*
2N/A * Now, which order are they?
2N/A * "user@domain" or "domain/user"
2N/A */
2N/A if (sc == '@') {
2N/A *usr = s1;
2N/A *dom = s2;
2N/A } else {
2N/A *dom = s1;
2N/A *usr = s2;
2N/A }
2N/A
2N/A return (0);
2N/A}
2N/A
2N/Aint
2N/Asmbfs_ctx_setauthflags(struct smb_ctx *ctx, int flags)
2N/A{
2N/A ctx->ct_authflags = flags;
2N/A return (0);
2N/A}
2N/A
2N/Aint
2N/Asmbfs_ctx_setfullserver(struct smb_ctx *ctx, const char *name)
2N/A{
2N/A char *p = strdup(name);
2N/A
2N/A if (p == NULL)
2N/A return (ENOMEM);
2N/A if (ctx->ct_fullserver)
2N/A free(ctx->ct_fullserver);
2N/A ctx->ct_fullserver = p;
2N/A return (0);
2N/A}
2N/A
2N/Aint
2N/Asmbfs_ctx_setserver(struct smb_ctx *ctx, const char *name)
2N/A{
2N/A (void) strlcpy(ctx->ct_srvname, name,
2N/A sizeof (ctx->ct_srvname));
2N/A return (0);
2N/A}
2N/A
2N/Aint
2N/Asmbfs_ctx_setuser(struct smb_ctx *ctx, const char *name, int from_cmd)
2N/A{
2N/A if (strlen(name) >= sizeof (ctx->ct_user)) {
2N/A smbfs_error(dgettext(TEXT_DOMAIN,
2N/A "user name '%s' too long"), 0, name);
2N/A return (ENAMETOOLONG);
2N/A }
2N/A
2N/A /*
2N/A * Don't overwrite a value from the command line
2N/A * with one from anywhere else.
2N/A */
2N/A if (!from_cmd && (ctx->ct_flags & SMBCF_CMD_USR))
2N/A return (0);
2N/A
2N/A (void) strlcpy(ctx->ct_user, name,
2N/A sizeof (ctx->ct_user));
2N/A
2N/A /* Mark this as "from the command line". */
2N/A if (from_cmd)
2N/A ctx->ct_flags |= SMBCF_CMD_USR;
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * Realm is obtained by converting the domain name to uppercase.
2N/A * Set realm by getting domain name from the resolver if either the full DNS
2N/A * domain name or the first label of the DNS domain name matches the passed
2N/A * name.
2N/A */
2N/Aint
2N/Asmbfs_ctx_setrealm(struct smb_ctx *ctx, const char *name)
2N/A{
2N/A char buf[MAXHOSTNAMELEN];
2N/A char *realm, *dc_realm, *p;
2N/A
2N/A if (smb_getdomainname_resolv(buf, MAXHOSTNAMELEN) != 0)
2N/A return (-1);
2N/A
2N/A if ((dc_realm = smbfs_utf8_str_toupper(buf)) == NULL)
2N/A return (-1);
2N/A
2N/A if ((p = strchr(buf, '.')) != NULL)
2N/A *p = '\0';
2N/A
2N/A if (smb_strcasecmp(buf, name, 0) == 0) {
2N/A bzero(ctx->ct_realm, sizeof (ctx->ct_realm));
2N/A (void) strlcpy(ctx->ct_realm, dc_realm, sizeof (ctx->ct_realm));
2N/A free(dc_realm);
2N/A return (0);
2N/A }
2N/A free(dc_realm);
2N/A
2N/A if ((realm = smbfs_utf8_str_toupper(name)) != NULL) {
2N/A (void) strlcpy(ctx->ct_realm, realm, sizeof (ctx->ct_realm));
2N/A free(realm);
2N/A }
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * Don't overwrite a domain name from the
2N/A * command line with one from anywhere else.
2N/A * See smbfs_ctx_init() for notes about this.
2N/A */
2N/Aint
2N/Asmbfs_ctx_setdomain(struct smb_ctx *ctx, const char *name, int from_cmd)
2N/A{
2N/A if (strlen(name) >= sizeof (ctx->ct_domain)) {
2N/A smbfs_error(dgettext(TEXT_DOMAIN,
2N/A "workgroup name '%s' too long"), 0, name);
2N/A return (ENAMETOOLONG);
2N/A }
2N/A
2N/A /*
2N/A * Don't overwrite a value from the command line
2N/A * with one from anywhere else.
2N/A */
2N/A if (!from_cmd && (ctx->ct_flags & SMBCF_CMD_DOM))
2N/A return (0);
2N/A
2N/A (void) strlcpy(ctx->ct_domain, name,
2N/A sizeof (ctx->ct_domain));
2N/A
2N/A /* Mark this as "from the command line". */
2N/A if (from_cmd)
2N/A ctx->ct_flags |= SMBCF_CMD_DOM;
2N/A
2N/A return (0);
2N/A}
2N/A
2N/Aint
2N/Asmbfs_ctx_setpassword(struct smb_ctx *ctx, const char *passwd, int from_cmd)
2N/A{
2N/A int err;
2N/A
2N/A if (passwd == NULL)
2N/A return (EINVAL);
2N/A if (strlen(passwd) >= sizeof (ctx->ct_password)) {
2N/A smbfs_error(dgettext(TEXT_DOMAIN, "password too long"), 0);
2N/A return (ENAMETOOLONG);
2N/A }
2N/A
2N/A /*
2N/A * If called again after comand line parsing,
2N/A * don't overwrite a value from the command line
2N/A * with one from any stored config.
2N/A */
2N/A if (!from_cmd && (ctx->ct_flags & SMBCF_CMD_PW))
2N/A return (0);
2N/A
2N/A (void) memset(ctx->ct_password, 0, sizeof (ctx->ct_password));
2N/A if (strncmp(passwd, "$$1", 3) == 0)
2N/A (void) smbfs_simpledecrypt(ctx->ct_password, passwd);
2N/A else
2N/A (void) strlcpy(ctx->ct_password, passwd,
2N/A sizeof (ctx->ct_password));
2N/A
2N/A /*
2N/A * Compute LM hash, NT hash.
2N/A */
2N/A if (ctx->ct_password[0]) {
2N/A err = smbfs_ntlm_compute_nt_hash(ctx->ct_nthash,
2N/A ctx->ct_password);
2N/A if (err != 0)
2N/A return (err);
2N/A err = smbfs_ntlm_compute_lm_hash(ctx->ct_lmhash,
2N/A ctx->ct_password);
2N/A if (err != 0)
2N/A return (err);
2N/A }
2N/A
2N/A /* Mark this as "from the command line". */
2N/A if (from_cmd)
2N/A ctx->ct_flags |= SMBCF_CMD_PW;
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * Use this to set NTLM auth. info (hashes)
2N/A * when we don't have the password.
2N/A */
2N/Aint
2N/Asmbfs_ctx_setpwhash(smb_ctx_t *ctx,
2N/A const uchar_t *nthash, const uchar_t *lmhash)
2N/A{
2N/A
2N/A /* Need ct_password to be non-null. */
2N/A if (ctx->ct_password[0] == '\0')
2N/A (void) strlcpy(ctx->ct_password, "$HASH",
2N/A sizeof (ctx->ct_password));
2N/A
2N/A (void) memcpy(ctx->ct_nthash, nthash, NTLM_HASH_SZ);
2N/A
2N/A /* The LM hash is optional */
2N/A if (lmhash)
2N/A (void) memcpy(ctx->ct_lmhash, lmhash, NTLM_HASH_SZ);
2N/A
2N/A return (0);
2N/A}
2N/A
2N/Aint
2N/Asmbfs_ctx_setshare(struct smb_ctx *ctx, const char *share, int stype)
2N/A{
2N/A if (strlen(share) >= SMBIOC_MAX_NAME) {
2N/A smbfs_error(dgettext(TEXT_DOMAIN,
2N/A "share name '%s' too long"), 0, share);
2N/A return (ENAMETOOLONG);
2N/A }
2N/A if (ctx->ct_origshare)
2N/A free(ctx->ct_origshare);
2N/A if ((ctx->ct_origshare = strdup(share)) == NULL)
2N/A return (ENOMEM);
2N/A
2N/A ctx->ct_shtype_req = stype;
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * API for library caller to set/unset signing required
2N/A */
2N/Aint
2N/Asmbfs_ctx_setsigning(struct smb_ctx *ctx, boolean_t required)
2N/A{
2N/A if (required)
2N/A ctx->ct_vopt |= SMBVOPT_SIGNING_REQUIRED;
2N/A else
2N/A ctx->ct_vopt &= ~SMBVOPT_SIGNING_REQUIRED;
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * SMB client name resolution - normal, and/or NetBIOS.
2N/A * Returns an EAI_xxx error number like getaddrinfo(3)
2N/A */
2N/Astatic int
2N/Asmbfs_ctx_getaddr(struct smb_ctx *ctx)
2N/A{
2N/A struct nb_ctx *nbc = ctx->ct_nb;
2N/A struct addrinfo hints, *res;
2N/A char *srvaddr_str;
2N/A int gaierr, gaierr2;
2N/A
2N/A if (ctx->ct_fullserver == NULL || ctx->ct_fullserver[0] == '\0')
2N/A return (EAI_NONAME);
2N/A
2N/A if (ctx->ct_addrinfo != NULL) {
2N/A freeaddrinfo(ctx->ct_addrinfo);
2N/A ctx->ct_addrinfo = NULL;
2N/A }
2N/A
2N/A /*
2N/A * If the user specified an address, use it,
2N/A * and don't do NetBIOS lookup.
2N/A */
2N/A if (ctx->ct_srvaddr_s) {
2N/A srvaddr_str = ctx->ct_srvaddr_s;
2N/A nbc->nb_flags &= ~NBCF_NS_ENABLE;
2N/A } else
2N/A srvaddr_str = ctx->ct_fullserver;
2N/A
2N/A /*
2N/A * Default the server name we'll use in the
2N/A * protocol (i.e. NTLM, tree connect).
2N/A */
2N/A (void) strlcpy(ctx->ct_srvname, ctx->ct_fullserver,
2N/A sizeof (ctx->ct_srvname));
2N/A
2N/A /*
2N/A * Try to lookup the host address using the
2N/A * normal name-to-IP address mechanisms.
2N/A * If that fails, we MAY try NetBIOS.
2N/A */
2N/A (void) memset(&hints, 0, sizeof (hints));
2N/A hints.ai_flags = AI_CANONNAME;
2N/A hints.ai_family = PF_UNSPEC;
2N/A hints.ai_socktype = SOCK_STREAM;
2N/A gaierr = getaddrinfo(srvaddr_str, NULL, &hints, &res);
2N/A if (gaierr == 0) {
2N/A ctx->ct_addrinfo = res;
2N/A return (0);
2N/A }
2N/A
2N/A /*
2N/A * If regular IP name lookup failed, try NetBIOS,
2N/A * but only if given a valid NetBIOS name and if
2N/A * NetBIOS name lookup is enabled.
2N/A */
2N/A if (nbc->nb_flags & NBCF_NS_ENABLE) {
2N/A gaierr2 = smbfs_nbns_getaddrinfo(ctx->ct_fullserver, nbc, &res);
2N/A if (gaierr2 == 0) {
2N/A if (res->ai_canonname)
2N/A (void) strlcpy(ctx->ct_srvname,
2N/A res->ai_canonname,
2N/A sizeof (ctx->ct_srvname));
2N/A ctx->ct_addrinfo = res;
2N/A return (0);
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * Return the original error from getaddrinfo
2N/A */
2N/A if (smbfs_debug) {
2N/A smbfs_error(dgettext(TEXT_DOMAIN,
2N/A "getaddrinfo: %s: %s"), 0,
2N/A ctx->ct_fullserver,
2N/A gai_strerror(gaierr));
2N/A }
2N/A return (gaierr);
2N/A}
2N/A
2N/A/*
2N/A * Verify context info. before connect operation(s),
2N/A * lookup specified server and try to fill all forgotten fields.
2N/A * Legacy name used by commands.
2N/A */
2N/Aint
2N/Asmbfs_ctx_resolve(struct smb_ctx *ctx)
2N/A{
2N/A struct smbioc_ossn *ssn = &ctx->ct_ssn;
2N/A int error = 0;
2N/A
2N/A if (smbfs_debug)
2N/A smbfs_dump_ctx("before smb_ctx_resolve", ctx);
2N/A
2N/A ctx->ct_flags &= ~SMBCF_RESOLVED;
2N/A
2N/A if (ctx->ct_fullserver == NULL) {
2N/A smbfs_error(dgettext(TEXT_DOMAIN,
2N/A "no server name specified"), 0);
2N/A return (EINVAL);
2N/A }
2N/A
2N/A if (ctx->ct_minlevel >= SMBL_SHARE &&
2N/A ctx->ct_origshare == NULL) {
2N/A smbfs_error(dgettext(TEXT_DOMAIN,
2N/A "no share name specified for %s@%s"),
2N/A 0, ssn->ssn_user, ctx->ct_fullserver);
2N/A return (EINVAL);
2N/A }
2N/A error = smbfs_nb_ctx_resolve(ctx->ct_nb);
2N/A if (error)
2N/A return (error);
2N/A
2N/A /*
2N/A * Lookup the IP address and fill in ct_addrinfo.
2N/A *
2N/A * Note: smbfs_ctx_getaddr() returns a EAI_xxx
2N/A * error value like getaddrinfo(3), but this
2N/A * function needs to return an errno value.
2N/A */
2N/A error = smbfs_ctx_getaddr(ctx);
2N/A if (error) {
2N/A const char *ais = gai_strerror(error);
2N/A smbfs_error(dgettext(TEXT_DOMAIN,
2N/A "can't resolve name \"%s\", %s"),
2N/A 0, ctx->ct_fullserver, ais);
2N/A return (ENODATA);
2N/A }
2N/A assert(ctx->ct_addrinfo != NULL);
2N/A
2N/A /*
2N/A * If we have a user name but no password,
2N/A * check for a keychain entry.
2N/A * XXX: Only for auth NTLM?
2N/A */
2N/A if (ctx->ct_user[0] == '\0') {
2N/A /*
2N/A * No user name (anonymous session).
2N/A */
2N/A ctx->ct_authflags = SMB_AT_ANON;
2N/A } else {
2N/A /*
2N/A * Have a user name.
2N/A * If we don't have a p/w yet,
2N/A * try the keychain.
2N/A */
2N/A if (ctx->ct_password[0] == '\0')
2N/A (void) smbfs_get_keychain(ctx);
2N/A }
2N/A if (ctx->ct_authflags == 0) {
2N/A smbfs_error(dgettext(TEXT_DOMAIN,
2N/A "no valid auth. types"), 0);
2N/A return (ENOTSUP);
2N/A }
2N/A
2N/A ctx->ct_flags |= SMBCF_RESOLVED;
2N/A if (smbfs_debug)
2N/A smbfs_dump_ctx("after smb_ctx_resolve", ctx);
2N/A
2N/A return (0);
2N/A}
2N/A
2N/Aint
2N/Asmbfs_open_driver()
2N/A{
2N/A int fd;
2N/A
2N/A fd = open("/dev/"NSMB_NAME, O_RDWR);
2N/A if (fd < 0) {
2N/A return (-1);
2N/A }
2N/A
2N/A /* This handle controls per-process resources. */
2N/A (void) fcntl(fd, F_SETFD, FD_CLOEXEC);
2N/A
2N/A return (fd);
2N/A}
2N/A
2N/Aint
2N/Asmbfs_ctx_gethandle(struct smb_ctx *ctx)
2N/A{
2N/A int fd, err;
2N/A uint32_t version;
2N/A
2N/A if (ctx->ct_dev_fd != -1) {
2N/A smbfs_ctx_rpc_cleanup(ctx);
2N/A (void) close(ctx->ct_dev_fd);
2N/A ctx->ct_dev_fd = -1;
2N/A ctx->ct_flags &= ~SMBCF_SSNACTIVE;
2N/A }
2N/A
2N/A fd = smbfs_open_driver();
2N/A if (fd < 0) {
2N/A err = errno;
2N/A smbfs_error(dgettext(TEXT_DOMAIN,
2N/A "failed to open driver"), err);
2N/A return (err);
2N/A }
2N/A
2N/A /*
2N/A * Check the driver version (paranoia)
2N/A */
2N/A if (ioctl(fd, SMBIOC_GETVERS, &version) < 0)
2N/A version = 0;
2N/A if (version != NSMB_VERSION) {
2N/A smbfs_error(dgettext(TEXT_DOMAIN,
2N/A "incorrect driver version"), 0);
2N/A (void) close(fd);
2N/A return (ENODEV);
2N/A }
2N/A
2N/A ctx->ct_dev_fd = fd;
2N/A return (0);
2N/A}
2N/A
2N/A
2N/A/*
2N/A * Find or create a connection + logon session
2N/A */
2N/Aint
2N/Asmbfs_ctx_get_ssn(struct smb_ctx *ctx)
2N/A{
2N/A int err = 0;
2N/A
2N/A ctx->ct_ntstatus = NT_STATUS_UNSUCCESSFUL;
2N/A
2N/A if ((ctx->ct_flags & SMBCF_RESOLVED) == 0)
2N/A return (EINVAL);
2N/A
2N/A if (ctx->ct_dev_fd < 0) {
2N/A if ((err = smbfs_ctx_gethandle(ctx)))
2N/A return (err);
2N/A }
2N/A
2N/A /*
2N/A * Check whether the driver already has a VC
2N/A * we can use. If so, we're done!
2N/A */
2N/A err = smbfs_ctx_findvc(ctx);
2N/A if (err == 0) {
2N/A ctx->ct_ntstatus = NT_STATUS_SUCCESS;
2N/A DPRINT("found an existing VC");
2N/A } else {
2N/A /*
2N/A * This calls the IOD to create a new session.
2N/A */
2N/A DPRINT("setup a new VC");
2N/A err = smbfs_ctx_newvc(ctx);
2N/A if (err != 0)
2N/A return (err);
2N/A
2N/A /*
2N/A * Call findvc again. The new VC sould be
2N/A * found in the driver this time.
2N/A */
2N/A err = smbfs_ctx_findvc(ctx);
2N/A }
2N/A
2N/A return (err);
2N/A}
2N/A
2N/A/*
2N/A * Find or create a tree connection
2N/A */
2N/Aint
2N/Asmbfs_ctx_get_tree(struct smb_ctx *ctx)
2N/A{
2N/A smbioc_tcon_t *tcon = NULL;
2N/A int cmd, err = 0;
2N/A
2N/A if (ctx->ct_dev_fd < 0 ||
2N/A ctx->ct_origshare == NULL) {
2N/A return (EINVAL);
2N/A }
2N/A
2N/A cmd = SMBIOC_TREE_CONNECT;
2N/A tcon = malloc(sizeof (*tcon));
2N/A if (tcon == NULL)
2N/A return (ENOMEM);
2N/A bzero(tcon, sizeof (*tcon));
2N/A tcon->tc_flags = SMBLK_CREATE;
2N/A tcon->tc_opt = 0;
2N/A
2N/A /* The share name */
2N/A (void) strlcpy(tcon->tc_sh.sh_name, ctx->ct_origshare,
2N/A sizeof (tcon->tc_sh.sh_name));
2N/A
2N/A /* The share "use" type. */
2N/A tcon->tc_sh.sh_use = ctx->ct_shtype_req;
2N/A
2N/A /*
2N/A * Todo: share passwords for share-level security.
2N/A *
2N/A * The driver does the actual TCON call.
2N/A */
2N/A if (ioctl(ctx->ct_dev_fd, cmd, tcon) == -1) {
2N/A err = errno;
2N/A goto out;
2N/A }
2N/A
2N/A /*
2N/A * Check the returned share type
2N/A */
2N/A DPRINT("ret. sh_type: \"%d\"", tcon->tc_sh.sh_type);
2N/A if (ctx->ct_shtype_req != USE_WILDCARD &&
2N/A ctx->ct_shtype_req != tcon->tc_sh.sh_type) {
2N/A smbfs_error(dgettext(TEXT_DOMAIN,
2N/A "%s: incompatible share type"),
2N/A 0, ctx->ct_origshare);
2N/A }
2N/A
2N/Aout:
2N/A if (tcon != NULL)
2N/A free(tcon);
2N/A
2N/A return (err);
2N/A}
2N/A
2N/A/*
2N/A * Return the hflags2 word for an smb_ctx.
2N/A */
2N/Aint
2N/Asmbfs_ctx_flags2(struct smb_ctx *ctx)
2N/A{
2N/A uint16_t flags2;
2N/A
2N/A if (ioctl(ctx->ct_dev_fd, SMBIOC_FLAGS2, &flags2) == -1) {
2N/A smbfs_error(dgettext(TEXT_DOMAIN,
2N/A "can't get flags2 for a session"), errno);
2N/A return (-1);
2N/A }
2N/A return (flags2);
2N/A}
2N/A
2N/A/*
2N/A * Get the transport level session key.
2N/A * Must already have an active SMB session.
2N/A */
2N/Aint
2N/Asmbfs_fh_getssnkey(int dev_fd, uchar_t *key, size_t len)
2N/A{
2N/A if (len < SMBIOC_HASH_SZ)
2N/A return (EINVAL);
2N/A
2N/A if (ioctl(dev_fd, SMBIOC_GETSSNKEY, key) == -1)
2N/A return (errno);
2N/A
2N/A return (0);
2N/A}
2N/A
2N/Avoid
2N/Asmbfs_set_default_domain(const char *domain)
2N/A{
2N/A (void) strlcpy(default_domain, domain, sizeof (default_domain));
2N/A}
2N/A
2N/Avoid
2N/Asmbfs_set_default_user(const char *user)
2N/A{
2N/A (void) strlcpy(default_user, user, sizeof (default_user));
2N/A}
2N/A
2N/A#pragma init(libsmbfs_init)
2N/Avoid
2N/Alibsmbfs_init(void)
2N/A{
2N/A char pwbuf[NSS_BUFLEN_PASSWD];
2N/A struct passwd pw;
2N/A
2N/A /*
2N/A * if the user name is not specified some other way,
2N/A * use the current user name (built-in default)
2N/A */
2N/A if (getpwuid_r(getuid(), &pw, pwbuf, sizeof (pwbuf)) != NULL) {
2N/A smbfs_set_default_user(pw.pw_name);
2N/A }
2N/A
2N/A /*
2N/A * Leave default domain empty. (That's valid).
2N/A */
2N/A}
2N/A
2N/A/*
2N/A * API for seting NetBIOS name lookup flags:
2N/A * NetBIOS name lookup enable,
2N/A * NetBIOS broadcast enable.
2N/A * Currently this APIs is unused.
2N/A */
2N/Aint
2N/Asmbfs_ctx_setnbflags(struct smb_ctx *ctx, int ns_ena, int bc_ena)
2N/A{
2N/A struct nb_ctx *nb = ctx->ct_nb;
2N/A
2N/A if (nb == NULL)
2N/A return (EINVAL);
2N/A
2N/A smbfs_nb_ctx_setnbflags(nb, ns_ena, bc_ena);
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * API for library consumer to set wins1, wins2
2N/A */
2N/Aint
2N/Asmbfs_ctx_setwins(struct smb_ctx *ctx, const char *wins1, const char *wins2)
2N/A{
2N/A struct nb_ctx *nb = ctx->ct_nb;
2N/A
2N/A if (nb == NULL)
2N/A return (EINVAL);
2N/A
2N/A return (smbfs_nb_ctx_setwins(nb, wins1, wins2));
2N/A}
2N/A
2N/A/*
2N/A * API for library consumer to set NB scope.
2N/A */
2N/Aint
2N/Asmbfs_ctx_setscope(struct smb_ctx *ctx, const char *scope)
2N/A{
2N/A struct nb_ctx *nb = ctx->ct_nb;
2N/A
2N/A if (nb == NULL)
2N/A return (EINVAL);
2N/A
2N/A return (smbfs_nb_ctx_setscope(nb, scope));
2N/A}