#include <sys/param.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <pwd.h>
#include <nss_dbdefs.h>
#include <assert.h>
#include <strings.h>
#include <sys/stat.h>
#include <sys/idmap.h>
#include <smbsrv/libsmb.h>
#include <smbsrv/libmlsvc.h>
#include <smbsrv/smbinfo.h>
#define SMB_AUTOHOME_KEYSIZ 128
#define SMB_AUTOHOME_MAXARG 4
#define SMB_AUTOHOME_BUFSIZ 2048
typedef struct smb_autohome_info {
struct smb_autohome_info *magic1;
FILE *fp;
smb_autohome_t autohome;
char buf[SMB_AUTOHOME_BUFSIZ];
char *argv[SMB_AUTOHOME_MAXARG];
int lineno;
struct smb_autohome_info *magic2;
} smb_autohome_info_t;
static smb_autohome_info_t smb_ai;
static smb_autohome_t *smb_autohome_make_entry(smb_autohome_info_t *);
static char *smb_autohome_keysub(const char *, char *, int);
static smb_autohome_info_t *smb_autohome_getinfo(void);
static smb_autohome_t *smb_autohome_lookup(const char *);
static void smb_autohome_setent(void);
static void smb_autohome_endent(void);
static smb_autohome_t *smb_autohome_getent(const char *);
static void smb_autohome_parse_options(smb_share_t *);
static int smb_autohome_add_private(const char *, uid_t, gid_t);
void
smb_autohome_add(const smb_token_t *token)
{
char *username;
struct passwd pw;
char buf[NSS_LINELEN_PASSWD];
uid_t uid;
gid_t gid;
if (token->tkn_flags & SMB_ATF_ANON)
return;
uid = token->tkn_user.i_id;
gid = token->tkn_primary_grp.i_id;
if (IDMAP_ID_IS_EPHEMERAL(uid)) {
username = token->tkn_account_name;
assert(username);
} else {
if (getpwuid_r(uid, &pw, buf, sizeof (buf)) == NULL) {
syslog(LOG_DEBUG, "unable to determine name for " \
"UID: %u\n", uid);
return;
}
username = pw.pw_name;
}
if (smb_autohome_add_private(username, uid, gid) != NERR_Success) {
if (!smb_isstrlwr(username)) {
(void) smb_strlwr(username);
(void) smb_autohome_add_private(username, uid, gid);
}
}
}
void
smb_autohome_remove(const char *username)
{
smb_share_t si;
assert(username);
if (smb_shr_get((char *)username, &si) == NERR_Success) {
if (si.shr_flags & SMB_SHRF_AUTOHOME)
(void) smb_shr_remove((char *)username);
}
}
static int
smb_autohome_add_private(const char *username, uid_t uid, gid_t gid)
{
static mutex_t autohome_mutex;
smb_share_t si;
smb_autohome_t *ai;
char shr_name[MAXNAMELEN];
(void) strlcpy(shr_name, username, sizeof (shr_name));
if (smb_shr_get(shr_name, &si) == NERR_Success) {
if ((si.shr_flags & SMB_SHRF_AUTOHOME) == 0)
return (NERR_Success);
(void) smb_shr_add(&si);
return (NERR_Success);
}
(void) mutex_lock(&autohome_mutex);
if ((ai = smb_autohome_lookup(username)) == NULL) {
(void) mutex_unlock(&autohome_mutex);
return (NERR_ItemNotFound);
}
bzero(&si, sizeof (smb_share_t));
(void) strlcpy(si.shr_path, ai->ah_path, MAXPATHLEN);
(void) strsubst(si.shr_path, '\\', '/');
(void) strlcpy(si.shr_name, username, MAXNAMELEN);
(void) strlcpy(si.shr_container, ai->ah_container, MAXPATHLEN);
(void) strlcpy(si.shr_cmnt, "Autohome", SMB_SHARE_CMNT_MAX);
smb_autohome_parse_options(&si);
si.shr_flags |= SMB_SHRF_TRANS | SMB_SHRF_AUTOHOME;
si.shr_uid = uid;
si.shr_gid = gid;
(void) mutex_unlock(&autohome_mutex);
return (smb_shr_add(&si));
}
static smb_autohome_t *
smb_autohome_lookup(const char *name)
{
struct passwd *pw;
smb_autohome_t *ah = NULL;
if (name == NULL)
return (NULL);
if (*name == '\0' || *name == '*' || *name == '+')
return (NULL);
smb_autohome_setent();
while ((ah = smb_autohome_getent(name)) != NULL) {
if (strcasecmp(ah->ah_name, name) == 0)
break;
}
if (ah == NULL) {
smb_autohome_setent();
while ((ah = smb_autohome_getent(name)) != NULL) {
if (strcasecmp(ah->ah_name, "*") == 0) {
ah->ah_name = (char *)name;
break;
}
}
}
if (ah == NULL) {
smb_autohome_setent();
while ((ah = smb_autohome_getent("+nsswitch")) != NULL) {
if (strcasecmp("+nsswitch", ah->ah_name) != 0)
continue;
if ((pw = getpwnam(name)) == NULL) {
ah = NULL;
break;
}
ah->ah_name = pw->pw_name;
if (ah->ah_path)
ah->ah_container = ah->ah_path;
ah->ah_path = pw->pw_dir;
break;
}
}
smb_autohome_endent();
return (ah);
}
static void
smb_autohome_setent(void)
{
smb_autohome_info_t *si;
char path[MAXNAMELEN];
char filename[MAXNAMELEN];
int rc;
if ((si = smb_autohome_getinfo()) != 0) {
(void) fseek(si->fp, 0L, SEEK_SET);
si->lineno = 0;
return;
}
if ((si = &smb_ai) == 0)
return;
rc = smb_config_getstr(SMB_CI_AUTOHOME_MAP, path, sizeof (path));
if (rc != SMBD_SMF_OK)
return;
(void) snprintf(filename, MAXNAMELEN, "%s/%s", path,
SMB_AUTOHOME_FILE);
if ((si->fp = fopen(filename, "r")) == NULL)
return;
si->magic1 = si;
si->magic2 = si;
si->lineno = 0;
}
static void
smb_autohome_endent(void)
{
smb_autohome_info_t *si;
if ((si = smb_autohome_getinfo()) != 0) {
(void) fclose(si->fp);
si->fp = 0;
si->magic1 = 0;
si->magic2 = 0;
}
}
static smb_autohome_t *
smb_autohome_getent(const char *name)
{
smb_autohome_info_t *si;
char *bp;
if ((si = smb_autohome_getinfo()) == 0) {
smb_autohome_setent();
if ((si = smb_autohome_getinfo()) == 0)
return (0);
}
do {
if (fgets(si->buf, SMB_AUTOHOME_BUFSIZ, si->fp) == 0)
return (0);
++si->lineno;
if ((bp = strpbrk(si->buf, "#\r\n")) != 0)
*bp = '\0';
(void) trim_whitespace(si->buf);
bp = strcanon(si->buf, " \t");
} while (*bp == '\0');
(void) smb_autohome_keysub(name, si->buf, SMB_AUTOHOME_BUFSIZ);
return (smb_autohome_make_entry(si));
}
static smb_autohome_t *
smb_autohome_make_entry(smb_autohome_info_t *si)
{
char *bp;
int i;
bp = si->buf;
for (i = 0; i < SMB_AUTOHOME_MAXARG; ++i)
si->argv[i] = NULL;
for (i = 0; i < SMB_AUTOHOME_MAXARG; ++i) {
do {
if ((si->argv[i] = strsep(&bp, " \t")) == NULL)
break;
} while (*(si->argv[i]) == '\0');
if (si->argv[i] == NULL)
break;
}
if ((si->autohome.ah_name = si->argv[0]) == NULL) {
return (0);
}
if ((si->autohome.ah_path = si->argv[1]) == NULL)
si->autohome.ah_path = "";
if ((si->autohome.ah_container = si->argv[2]) == NULL)
si->autohome.ah_container = "";
return (&si->autohome);
}
static char *
smb_autohome_keysub(const char *name, char *buf, int buflen)
{
char key[SMB_AUTOHOME_KEYSIZ];
char *ampersand;
char *tmp;
int bufsize = buflen;
(void) strlcpy(key, buf, SMB_AUTOHOME_KEYSIZ);
if ((tmp = strpbrk(key, " \t")) == NULL)
return (NULL);
*tmp = '\0';
if (strpbrk(key, "?&") != NULL)
return (NULL);
if (strcmp(key, "*") == 0 && name != NULL)
(void) strlcpy(key, name, SMB_AUTOHOME_KEYSIZ);
(void) strsubst(buf, '?', *key);
while ((ampersand = strchr(buf, '&')) != NULL) {
if ((tmp = strdup(ampersand + 1)) == NULL)
return (0);
bufsize = buflen - (ampersand - buf);
(void) strlcpy(ampersand, key, bufsize);
(void) strlcat(ampersand, tmp, bufsize);
free(tmp);
}
return (buf);
}
static smb_autohome_info_t *
smb_autohome_getinfo(void)
{
smb_autohome_info_t *si;
if ((si = &smb_ai) == 0)
return (0);
if ((si->magic1 == si) && (si->magic2 == si) && (si->fp != NULL))
return (si);
return (0);
}
static void
smb_autohome_parse_options(smb_share_t *si)
{
char buf[MAXPATHLEN];
char **argv;
char **ap;
char *bp;
char *value;
boolean_t separator = B_FALSE;
int argc;
int i;
if (strlcpy(buf, si->shr_container, MAXPATHLEN) == 0)
return;
for (argc = 1, bp = si->shr_container; *bp != '\0'; ++bp)
if (*bp == ',')
++argc;
if ((argv = calloc(argc + 1, sizeof (char *))) == NULL)
return;
ap = argv;
for (bp = buf, i = 0; i < argc; ++i) {
do {
if ((value = strsep(&bp, ",")) == NULL)
break;
} while (*value == '\0');
if (value == NULL)
break;
*ap++ = value;
}
*ap = NULL;
si->shr_container[0] = '\0';
bp = si->shr_container;
for (ap = argv; *ap != NULL; ++ap) {
value = *ap;
if (strncasecmp(value, "catia=", 6) == 0) {
smb_shr_sa_setflag((value + 6), si, SMB_SHRF_CATIA);
continue;
}
if (strncasecmp(value, "csc=", 4) == 0) {
smb_shr_sa_csc_option((value + 4), si);
continue;
}
if (strncasecmp(value, "abe=", 4) == 0) {
smb_shr_sa_setflag((value + 4), si, SMB_SHRF_ABE);
continue;
}
if (strncasecmp(value, "description=", 12) == 0) {
(void) strlcpy(si->shr_cmnt, (value + 12),
SMB_SHARE_CMNT_MAX);
continue;
}
if (strncasecmp(value, "rw=", 3) == 0) {
(void) strlcpy(si->shr_access_rw, (value + 3),
sizeof (si->shr_access_rw));
continue;
}
if (strncasecmp(value, "ro=", 3) == 0) {
(void) strlcpy(si->shr_access_ro, (value + 3),
sizeof (si->shr_access_ro));
continue;
}
if (strncasecmp(value, "none=", 5) == 0) {
(void) strlcpy(si->shr_access_none, (value + 5),
sizeof (si->shr_access_none));
continue;
}
if (separator)
(void) strlcat(bp, ",", MAXPATHLEN);
(void) strlcat(bp, value, MAXPATHLEN);
separator = B_TRUE;
}
free(argv);
}