/*
* 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
* 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 2013 Nexenta Systems, Inc. All rights reserved.
*/
#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 "charsets.h"
#include "private.h"
#include "ntlm.h"
#ifndef FALSE
#define FALSE 0
#endif
#ifndef TRUE
#endif
struct nv {
char *name;
int value;
};
/* These two may be set by commands. */
/*
* Was: STDPARAM_OPT - see smb_ctx_scan_argv, smb_ctx_opt
*/
/*
* Defaults for new contexts (connections to servers).
* These are set by smbfs_set_default_...
*/
/*
* 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 void
{
if (close_hook)
(*close_hook)(ctx);
}
void
{
close_hook = hook;
}
void
{
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 ");
printf("XXX ");
if (flags & SMBCF_SSNACTIVE)
printf("SSNACTIVE ");
if (flags & SMBCF_KCDOMAIN)
printf("KCDOMAIN ");
printf("\n");
}
void
{
printf(" dom=\"%s\", user=\"%s\"\n",
printf(" ct_vopt=0x%x, ct_owner=%d\n",
printf(" ct_nthash:");
else
printf(" {0}\n");
printf(" ct_lmhash:");
else
printf(" {0}\n");
}
void
{
if (ctx->ct_locname)
else
printf(" localname=NULL");
if (ctx->ct_fullserver)
else
printf(" fullserver=NULL");
if (ctx->ct_srvaddr_s)
else
printf(" srvaddr_s=NULL\n");
if (ctx->ct_addrinfo)
else
printf(" ct_addrinfo = NULL\n");
printf(" share_name=\"%s\", share_type=%d\n",
ctx->ct_shtype_req);
}
int
{
int err;
return (ENOMEM);
if (err != 0) {
return (err);
}
return (0);
}
/*
* Initialize an smb_ctx struct (defaults)
*/
int
{
int error;
if (error)
return (error);
/* Fill in defaults */
/*
* Default domain, user, ...
*/
return (0);
}
/*
* "Scan" the command line args to find the server name,
* user name, and share name, as needed. We need these
*
* 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
{
const char *arg;
/*
* Parse options, if any. Values from here too
* are marked as "from CMD".
*/
return (0);
cf_opt_lock();
while (error == 0) {
if (opt == -1)
break;
/* NB: handle most in smb_ctx_opt */
switch (opt) {
case 'A':
aflg = 1;
break;
case 'U':
uflg = 1;
break;
default:
break;
}
}
if (error)
return (error);
return (EINVAL);
}
/*
* Parse the UNC path. Values from here are
* marked as "from CMD".
*/
continue;
if (error)
return (error);
break;
}
return (error);
}
void
{
}
void
{
}
}
}
if (ctx->ct_srvaddr_s) {
}
}
if (ctx->ct_locname) {
}
if (ctx->ct_origshare) {
}
if (ctx->ct_fullserver) {
}
if (ctx->ct_addrinfo) {
}
}
}
}
}
}
}
/*
* Parse the UNC path. Here we expect something like
* "//[[domain;]user[:password]@]host[/share[/path]]"
* Values found here are marked as "from CMD".
*/
int
const char **next)
{
int error;
/*
* This may be called outside of _scan_argv,
* so make sure these get initialized.
*/
/* Work on a temporary copy, fix back slashes. */
for (p = tmp; *p; p++)
if (*p == '\\')
*p = '/';
"UNC should start with '//'"), 0);
goto out;
}
/* Find the share part, if any. */
if (share)
*share = '\0';
(void) unpercent(p); /* host component */
/*
* Parse the "host" stuff right to left:
* 1: trailing "@hostname" (or whole field)
* 2: trailing ":password"
* 3: trailing "domain;user" (or just user)
*/
host = p; /* no user@ prefix */
} else {
*host++ = '\0';
/* may have [[domain;]user[:passwd]] */
if (pw)
*pw++ = '\0';
if (usr) {
*usr++ = '\0';
dom = p;
} else
usr = p;
}
if (*host == '\0') {
goto out;
}
if (error)
goto out;
if (error)
goto out;
}
if (*usr == '\0') {
"empty user name"), 0);
goto out;
}
"no user name required"), 0);
goto out;
}
if (error)
goto out;
}
if (error)
goto out;
}
/* restore the slash */
*share = '/';
p = share + 1;
/* Find the path part, if any. */
if (path)
*path = '\0';
(void) unpercent(p); /* share component */
if (*p == '\0') {
"empty share name"), 0);
goto out;
}
"no share name required"), 0);
goto out;
}
/*
* Special case UNC names like:
* to have share: IPC$
*/
if (strcasecmp(p, "PIPE") == 0) {
p = "IPC$";
}
if (error)
goto out;
if (path) {
/* restore the slash */
*path = '/';
p = path + 1;
(void) unpercent(p); /* remainder */
}
goto out;
}
if (next)
out:
return (error);
}
#ifdef KICONV_SUPPORT
int
{
"invalid local charset specification (%s)"), 0, arg);
return (EINVAL);
}
"invalid server charset specification (%s)"), 0, arg);
return (EINVAL);
}
if (error == 0)
return (0);
"can't initialize iconv support (%s:%s)"),
localcs[0] = 0;
servercs[0] = 0;
return (error);
}
#endif /* KICONV_SUPPORT */
int
{
return (0);
}
int
{
if (p == NULL)
return (ENOMEM);
if (ctx->ct_fullserver)
ctx->ct_fullserver = p;
return (0);
}
int
{
sizeof (ctx->ct_srvname));
return (0);
}
int
{
"user name '%s' too long"), 0, name);
return (ENAMETOOLONG);
}
/*
* Don't overwrite a value from the command line
* with one from anywhere else.
*/
return (0);
/* Mark this as "from the command line". */
if (from_cmd)
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
{
"workgroup name '%s' too long"), 0, name);
return (ENAMETOOLONG);
}
/*
* Don't overwrite a value from the command line
* with one from anywhere else.
*/
return (0);
/* Mark this as "from the command line". */
if (from_cmd)
return (0);
}
int
{
int err;
return (EINVAL);
return (ENAMETOOLONG);
}
/*
* If called again after comand line parsing,
* don't overwrite a value from the command line
* with one from any stored config.
*/
return (0);
else
sizeof (ctx->ct_password));
/*
* Compute LM hash, NT hash.
*/
if (ctx->ct_password[0]) {
if (err != 0)
return (err);
if (err != 0)
return (err);
}
/* Mark this as "from the command line". */
if (from_cmd)
return (0);
}
/*
* Use this to set NTLM auth. info (hashes)
* when we don't have the password.
*/
int
{
/* Need ct_password to be non-null. */
sizeof (ctx->ct_password));
/*
* Compute LM hash, NT hash.
*/
/* The LM hash is optional */
if (lmhash) {
}
return (0);
}
int
{
"share name '%s' too long"), 0, share);
return (ENAMETOOLONG);
}
if (ctx->ct_origshare)
return (ENOMEM);
return (0);
}
int
{
return (EINVAL);
if (ctx->ct_srvaddr_s)
return (ENOMEM);
return (0);
}
/*
* API for library caller to set signing enabled, required
* Note: if not enable, ignore require
*/
int
{
if (enable) {
if (require)
}
return (0);
}
static int
{
char *cp;
if (cp) {
*cp++ = '\0';
} else
"Invalid group name %s, ignored"), 0, cp);
}
}
if (*pair) {
} else
"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
{
const char *p = arg;
int authflags = 0;
for (;;) {
/* skip separators */
p += tlen;
if (nlen == 0)
break;
/* Don't offer extended security. */
p += nlen;
continue;
}
/* This is rarely called, so not optimized. */
break;
}
"%s: invalid security options"), 0, p);
return (EINVAL);
}
p += nlen;
}
if (authflags)
return (0);
}
/*
* Commands use this with getopt. See:
* STDPARAM_OPT, STDPARAM_ARGS
* Called after smb_ctx_readrc().
*/
int
{
int error = 0;
char *p, *cp;
switch (opt) {
case 'A':
case 'U':
/* Handled in smb_ctx_init() */
break;
case 'I':
break;
case 'M':
/* share connect rights - ignored */
break;
case 'N':
break;
case 'O':
if (cp)
*cp = '\0';
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) */
break;
case 'T':
/* timeout - ignored */
break;
case 'D': /* domain */
case 'W': /* workgroup (legacy alias) */
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
{
int error = 0;
"can not setup kernel iconv table (%s:%s)"),
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
{
int error = 0;
#ifdef KICONV_SUPPORT
uint_t i;
#endif
if (smb_debug)
"no server name specified"), 0);
return (EINVAL);
}
"no share name specified for %s@%s"),
return (EINVAL);
}
if (error)
return (error);
#ifdef KICONV_SUPPORT
if (ssn->ioc_localcs[0] == 0)
if (error)
return (error);
if (error)
return (error);
if (ssn->ioc_servercs[0] != 0) {
for (i = 0; i < sizeof (cstbl); i++)
cstbl[i] = i;
cstbl);
if (error)
return (error);
for (i = 0; i < sizeof (cstbl); i++)
cstbl[i] = i;
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.
*/
if (error) {
"can't resolve name\"%s\", %s"),
return (ENODATA);
}
/*
* If we have a user name but no password,
* check for a keychain entry.
* XXX: Only for auth NTLM?
*/
/*
* Have a user name.
* If we don't have a p/w yet,
* try the keychain.
*/
(void) smb_get_keychain(ctx);
/*
* Mask out disallowed auth types.
*/
}
if (ctx->ct_authflags == 0) {
"no valid auth. types"), 0);
return (ENOTSUP);
}
if (smb_debug)
return (0);
}
int
{
int fd;
if (fd < 0) {
return (-1);
}
/* This handle controls per-process resources. */
return (fd);
}
int
{
}
fd = smb_open_driver();
if (fd < 0) {
"failed to open driver"), err);
return (err);
}
/*
* Check the driver version (paranoia)
*/
version = 0;
if (version != NSMB_VERSION) {
"incorrect driver version"), 0);
return (ENODEV);
}
return (0);
}
/*
* Find or create a connection + logon session
*/
int
{
int err = 0;
return (EINVAL);
return (err);
}
/*
* Check whether the driver already has a VC
* we can use. If so, we're done!
*/
if (err == 0) {
DPRINT("found an existing VC");
} else {
/*
* This calls the IOD to create a new session.
*/
DPRINT("setup a new VC");
if (err != 0)
return (err);
/*
* Call findvc again. The new VC sould be
* found in the driver this time.
*/
}
return (err);
}
/*
* Find or create a tree connection
*/
int
{
return (EINVAL);
}
return (ENOMEM);
/* The share name */
/* The share "use" type. */
/*
* Todo: share passwords for share-level security.
*
* The driver does the actual TCON call.
*/
goto out;
}
/*
* Check the returned share type
*/
"%s: incompatible share type"),
0, ctx->ct_origshare);
}
out:
return (err);
}
/*
* Return the hflags2 word for an smb_ctx.
*/
int
{
"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
{
if (len < SMBIOC_HASH_SZ)
return (EINVAL);
return (errno);
return (0);
}
/*
* RC file parsing stuff
*/
static struct nv
minauth_table[] = {
/* Allowed auth. types */
{ "kerberos", SMB_AT_KRB5 },
SMB_AT_ANON },
{ NULL }
};
/*
* level values:
* 0 - default
* 1 - server
* 2 - server:user
* 3 - server:user:share
*/
static int
{
char *p;
int error;
#ifdef KICONV_SUPPORT
if (level > 0) {
if (p) {
if (error)
"charset specification in the section '%s' ignored"),
}
}
#endif
if (level <= 1) {
/* Section is: [default] or [server] */
if (p) {
/*
* "minauth" was set in this section; override
* the current minimum authentication setting.
*/
break;
else {
/*
* Unknown minimum authentication level.
*/
"invalid minimum authentication level \"%s\" specified in the section %s"),
0, p, sname);
return (EINVAL);
}
}
if (p) {
/*
* "signing" was set in this section; override
* the current signing settings. Note:
* setsigning flags are: enable, require
*/
if (strcmp(p, "disabled") == 0) {
} else if (strcmp(p, "enabled") == 0) {
} else if (strcmp(p, "required") == 0) {
} else {
/*
* Unknown "signing" value.
*/
"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()
*/
if (p) {
if (error)
"workgroup specification in the "
}
if (p) {
if (error)
"domain specification in the "
}
if (p) {
if (error)
"user specification in the "
}
}
if (level == 1) {
/* Section is: [server] */
if (p) {
if (error) {
"invalid address specified in section %s"),
0, sname);
return (error);
}
}
}
if (p) {
if (error)
"password specification in the section '%s' ignored"),
}
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
{
int sname_max;
int err = 0;
/*
* If the user name is not specified some other way,
* use the current user name. Also save the homedir.
* NB: ct_home=NULL is allowed, and we don't want to
* bail out with an error for a missing ct_home.
*/
}
/* ignore any error here */
return (0);
}
goto done;
}
/*
* default parameters (level=0)
*/
/*
* If we don't have a server name, we can't read any of the
* [server...] sections.
*/
goto done;
/*
* SERVER parameters.
*/
/*
* If we don't have a user name, we can't read any of the
* [server:user...] sections.
*/
goto done;
/*
* SERVER:USER parameters
*/
/*
* If we don't have a share name, we can't read any of the
* [server:user:share] sections.
*/
goto done;
/*
* SERVER:USER:SHARE parameters
*/
ctx->ct_origshare);
done:
if (sname)
if (smb_debug)
if (err)
return (err);
}
void
{
}
void
{
}