2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A
2N/A/*
2N/A * Copyright (c) 2007, 2012, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A#include <ctype.h>
2N/A#include <stdio.h>
2N/A#include <stdarg.h>
2N/A#include <unistd.h>
2N/A#include <sys/fcntl.h>
2N/A#include <string.h>
2N/A#include <strings.h>
2N/A#include <stdlib.h>
2N/A#include <pthread.h>
2N/A#include <sys/varargs.h>
2N/A#include <sys/types.h>
2N/A#include <sys/mnttab.h>
2N/A#include <sys/mntent.h>
2N/A#include <tiuser.h>
2N/A#include <netconfig.h>
2N/A#include <netdir.h>
2N/A#include <sys/systeminfo.h>
2N/A#include <sys/utsname.h>
2N/A#include <libzfs.h>
2N/A#include <dlfcn.h>
2N/A#include <time.h>
2N/A#include <syslog.h>
2N/A#include <smbsrv/string.h>
2N/A#include <smbsrv/libsmb.h>
2N/A
2N/A#define SMB_LIB_ALT "/usr/lib/smbsrv/libsmbex.so"
2N/A
2N/A#define SMB_FLOCK_WAIT 15 /* in seconds */
2N/A
2N/A#define SMB_TIMEBUF_SZ 16
2N/A
2N/A#define SMB_LOG_FILE_FMT "/var/smb/%s_log.txt"
2N/A
2N/A#define SMB_ISCNTRL(c) ((((c) >= 0) && ((c) <= 0x1f)) || ((c) == 0x7f))
2N/A
2N/Atypedef struct smb_log_pri {
2N/A char *lp_name;
2N/A int lp_value;
2N/A} smb_log_pri_t;
2N/A
2N/Astatic smb_log_pri_t smb_log_pri[] = {
2N/A "panic", LOG_EMERG,
2N/A "emerg", LOG_EMERG,
2N/A "alert", LOG_ALERT,
2N/A "crit", LOG_CRIT,
2N/A "error", LOG_ERR,
2N/A "err", LOG_ERR,
2N/A "warn", LOG_WARNING,
2N/A "warning", LOG_WARNING,
2N/A "notice", LOG_NOTICE,
2N/A "info", LOG_INFO,
2N/A "debug", LOG_DEBUG
2N/A};
2N/A
2N/Astatic void smb_log_trace(int, const char *);
2N/Astatic smb_log_t *smb_log_get(smb_log_hdl_t);
2N/Astatic void smb_log_dump(smb_log_t *);
2N/A
2N/Astatic uint_t smb_make_mask(char *, uint_t);
2N/Astatic boolean_t smb_netmatch(struct netbuf *, char *);
2N/Astatic boolean_t smb_netgroup_match(struct nd_hostservlist *, char *, int);
2N/Astatic int smb_getzfsmount(const char *, struct mnttab *);
2N/A
2N/Aextern int __multi_innetgr();
2N/Aextern int __netdir_getbyaddr_nosrv(struct netconfig *,
2N/A struct nd_hostservlist **, struct netbuf *);
2N/A
2N/Astatic smb_loglist_t smb_loglist;
2N/A
2N/A#define C2H(c) "0123456789ABCDEF"[(c)]
2N/A#define H2C(c) (((c) >= '0' && (c) <= '9') ? ((c) - '0') : \
2N/A ((c) >= 'a' && (c) <= 'f') ? ((c) - 'a' + 10) : \
2N/A ((c) >= 'A' && (c) <= 'F') ? ((c) - 'A' + 10) : \
2N/A '\0')
2N/A#define DEFAULT_SBOX_SIZE 256
2N/A
2N/A/*
2N/A *
2N/A * hexdump
2N/A *
2N/A * Simple hex dump display function. Displays nbytes of buffer in hex and
2N/A * printable format. Non-printing characters are shown as '.'. It is safe
2N/A * to pass a null pointer. Each line begins with the offset. If nbytes is
2N/A * 0, the line will be blank except for the offset. Example output:
2N/A *
2N/A * 00000000 54 68 69 73 20 69 73 20 61 20 70 72 6F 67 72 61 This is a progra
2N/A * 00000010 6D 20 74 65 73 74 2E 00 m test..
2N/A *
2N/A */
2N/Avoid
2N/Ahexdump_offset(unsigned char *buffer, int nbytes, unsigned long *start)
2N/A{
2N/A static char *hex = "0123456789ABCDEF";
2N/A int i, count;
2N/A int offset;
2N/A unsigned char *p;
2N/A char ascbuf[64];
2N/A char hexbuf[64];
2N/A char *ap = ascbuf;
2N/A char *hp = hexbuf;
2N/A
2N/A if ((p = buffer) == NULL)
2N/A return;
2N/A
2N/A offset = *start;
2N/A
2N/A *ap = '\0';
2N/A *hp = '\0';
2N/A count = 0;
2N/A
2N/A for (i = 0; i < nbytes; ++i) {
2N/A if (i && (i % 16) == 0) {
2N/A smb_tracef("%06X %s %s", offset, hexbuf, ascbuf);
2N/A ap = ascbuf;
2N/A hp = hexbuf;
2N/A count = 0;
2N/A offset += 16;
2N/A }
2N/A
2N/A ap += sprintf(ap, "%c",
2N/A (*p >= 0x20 && *p < 0x7F) ? *p : '.');
2N/A hp += sprintf(hp, " %c%c",
2N/A hex[(*p >> 4) & 0x0F], hex[(*p & 0x0F)]);
2N/A ++p;
2N/A ++count;
2N/A }
2N/A
2N/A if (count) {
2N/A smb_tracef("%06X %-48s %s", offset, hexbuf, ascbuf);
2N/A offset += count;
2N/A }
2N/A
2N/A *start = offset;
2N/A}
2N/A
2N/Avoid
2N/Ahexdump(unsigned char *buffer, int nbytes)
2N/A{
2N/A unsigned long start = 0;
2N/A
2N/A hexdump_offset(buffer, nbytes, &start);
2N/A}
2N/A
2N/A/*
2N/A * bintohex
2N/A *
2N/A * Converts the given binary data (srcbuf) to
2N/A * its equivalent hex chars (hexbuf).
2N/A *
2N/A * hexlen should be at least twice as srclen.
2N/A * if hexbuf is not big enough returns 0.
2N/A * otherwise returns number of valid chars in
2N/A * hexbuf which is srclen * 2.
2N/A */
2N/Asize_t
2N/Abintohex(const char *srcbuf, size_t srclen,
2N/A char *hexbuf, size_t hexlen)
2N/A{
2N/A size_t outlen;
2N/A char c;
2N/A
2N/A outlen = srclen << 1;
2N/A
2N/A if (hexlen < outlen)
2N/A return (0);
2N/A
2N/A while (srclen-- > 0) {
2N/A c = *srcbuf++;
2N/A *hexbuf++ = C2H(c & 0xF);
2N/A *hexbuf++ = C2H((c >> 4) & 0xF);
2N/A }
2N/A
2N/A return (outlen);
2N/A}
2N/A
2N/A/*
2N/A * hextobin
2N/A *
2N/A * Converts hex to binary.
2N/A *
2N/A * Assuming hexbuf only contains hex digits (chars)
2N/A * this function convert every two bytes of hexbuf
2N/A * to one byte and put it in dstbuf.
2N/A *
2N/A * hexlen should be an even number.
2N/A * dstlen should be at least half of hexlen.
2N/A *
2N/A * Returns 0 if sizes are not correct, otherwise
2N/A * returns the number of converted bytes in dstbuf
2N/A * which is half of hexlen.
2N/A */
2N/Asize_t
2N/Ahextobin(const char *hexbuf, size_t hexlen,
2N/A char *dstbuf, size_t dstlen)
2N/A{
2N/A size_t outlen;
2N/A
2N/A if ((hexlen % 2) != 0)
2N/A return (0);
2N/A
2N/A outlen = hexlen >> 1;
2N/A if (dstlen < outlen)
2N/A return (0);
2N/A
2N/A while (hexlen > 0) {
2N/A *dstbuf = H2C(*hexbuf) & 0x0F;
2N/A hexbuf++;
2N/A *dstbuf++ |= (H2C(*hexbuf) << 4) & 0xF0;
2N/A hexbuf++;
2N/A
2N/A hexlen -= 2;
2N/A }
2N/A
2N/A return (outlen);
2N/A}
2N/A
2N/A/*
2N/A * Trim leading and trailing characters in the set defined by class
2N/A * from a buffer containing a null-terminated string.
2N/A * For example, if the input buffer contained "ABtext23" and class
2N/A * contains "ABC123", the buffer will contain "text" on return.
2N/A *
2N/A * This function modifies the contents of buf in place and returns
2N/A * a pointer to buf.
2N/A */
2N/Achar *
2N/Astrtrim(char *buf, const char *class)
2N/A{
2N/A char *p = buf;
2N/A char *q = buf;
2N/A
2N/A if (buf == NULL)
2N/A return (NULL);
2N/A
2N/A p += strspn(p, class);
2N/A
2N/A if (p != buf) {
2N/A while ((*q = *p++) != '\0')
2N/A ++q;
2N/A }
2N/A
2N/A while (q != buf) {
2N/A --q;
2N/A if (strspn(q, class) == 0)
2N/A return (buf);
2N/A *q = '\0';
2N/A }
2N/A
2N/A return (buf);
2N/A}
2N/A
2N/A/*
2N/A * Strip the characters in the set defined by class from a buffer
2N/A * containing a null-terminated string.
2N/A * For example, if the input buffer contained "XYA 1textZ string3"
2N/A * and class contains "123XYZ", the buffer will contain "A text string"
2N/A * on return.
2N/A *
2N/A * This function modifies the contents of buf in place and returns
2N/A * a pointer to buf.
2N/A */
2N/Achar *
2N/Astrstrip(char *buf, const char *class)
2N/A{
2N/A char *p = buf;
2N/A char *q = buf;
2N/A
2N/A if (buf == NULL)
2N/A return (NULL);
2N/A
2N/A while (*p) {
2N/A p += strspn(p, class);
2N/A *q++ = *p++;
2N/A }
2N/A
2N/A *q = '\0';
2N/A return (buf);
2N/A}
2N/A
2N/A/*
2N/A * trim_whitespace
2N/A *
2N/A * Trim leading and trailing whitespace chars (as defined by isspace)
2N/A * from a buffer. Example; if the input buffer contained " text ",
2N/A * it will contain "text", when we return. We assume that the buffer
2N/A * contains a null terminated string. A pointer to the buffer is
2N/A * returned.
2N/A */
2N/Achar *
2N/Atrim_whitespace(char *buf)
2N/A{
2N/A char *p = buf;
2N/A char *q = buf;
2N/A
2N/A if (buf == NULL)
2N/A return (NULL);
2N/A
2N/A while (*p && isspace(*p))
2N/A ++p;
2N/A
2N/A while ((*q = *p++) != 0)
2N/A ++q;
2N/A
2N/A if (q != buf) {
2N/A while ((--q, isspace(*q)) != 0)
2N/A *q = '\0';
2N/A }
2N/A
2N/A return (buf);
2N/A}
2N/A
2N/A/*
2N/A * randomize
2N/A *
2N/A * Randomize the contents of the specified buffer.
2N/A */
2N/Avoid
2N/Arandomize(char *data, unsigned len)
2N/A{
2N/A unsigned dwlen = len / 4;
2N/A unsigned remlen = len % 4;
2N/A unsigned tmp;
2N/A unsigned i; /*LINTED E_BAD_PTR_CAST_ALIGN*/
2N/A unsigned *p = (unsigned *)data;
2N/A
2N/A for (i = 0; i < dwlen; ++i)
2N/A *p++ = random();
2N/A
2N/A if (remlen) {
2N/A tmp = random();
2N/A (void) memcpy(p, &tmp, remlen);
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * This is the hash mechanism used to encrypt passwords for commands like
2N/A * SamrSetUserInformation. It uses a 256 byte s-box.
2N/A */
2N/Avoid
2N/Arand_hash(
2N/A unsigned char *data,
2N/A size_t datalen,
2N/A unsigned char *key,
2N/A size_t keylen)
2N/A{
2N/A unsigned char sbox[DEFAULT_SBOX_SIZE];
2N/A unsigned char tmp;
2N/A unsigned char index_i = 0;
2N/A unsigned char index_j = 0;
2N/A unsigned char j = 0;
2N/A int i;
2N/A
2N/A for (i = 0; i < DEFAULT_SBOX_SIZE; ++i)
2N/A sbox[i] = (unsigned char)i;
2N/A
2N/A for (i = 0; i < DEFAULT_SBOX_SIZE; ++i) {
2N/A j += (sbox[i] + key[i % keylen]);
2N/A
2N/A tmp = sbox[i];
2N/A sbox[i] = sbox[j];
2N/A sbox[j] = tmp;
2N/A }
2N/A
2N/A for (i = 0; i < datalen; ++i) {
2N/A index_i++;
2N/A index_j += sbox[index_i];
2N/A
2N/A tmp = sbox[index_i];
2N/A sbox[index_i] = sbox[index_j];
2N/A sbox[index_j] = tmp;
2N/A
2N/A tmp = sbox[index_i] + sbox[index_j];
2N/A data[i] = data[i] ^ sbox[tmp];
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * smb_chk_hostaccess
2N/A *
2N/A * Determines whether the specified host is in the given access list.
2N/A *
2N/A * We match on aliases of the hostname as well as on the canonical name.
2N/A * Names in the access list may be either hosts or netgroups; they're
2N/A * not distinguished syntactically. We check for hosts first because
2N/A * it's cheaper (just M*N strcmp()s), then try netgroups.
2N/A *
2N/A * Function returns:
2N/A * -1 for "all" (list is empty "" or "*")
2N/A * 0 not found (host is not in the list or list is NULL)
2N/A * 1 found
2N/A *
2N/A */
2N/Aint
2N/Asmb_chk_hostaccess(smb_inaddr_t *ipaddr, char *access_list)
2N/A{
2N/A int nentries;
2N/A char *gr;
2N/A char *lasts;
2N/A char *host;
2N/A int off;
2N/A int i;
2N/A int netgroup_match;
2N/A int response;
2N/A struct nd_hostservlist *clnames;
2N/A struct in_addr inaddr;
2N/A struct sockaddr_in sa;
2N/A struct netbuf buf;
2N/A struct netconfig *config;
2N/A
2N/A if (access_list == NULL)
2N/A return (0);
2N/A
2N/A inaddr.s_addr = ipaddr->a_ipv4;
2N/A
2N/A /*
2N/A * If access list is empty or "*" - then it's "all"
2N/A */
2N/A if (*access_list == '\0' || strcmp(access_list, "*") == 0)
2N/A return (-1);
2N/A
2N/A nentries = 0;
2N/A
2N/A sa.sin_family = AF_INET;
2N/A sa.sin_port = 0;
2N/A sa.sin_addr = inaddr;
2N/A
2N/A buf.len = buf.maxlen = sizeof (sa);
2N/A buf.buf = (char *)&sa;
2N/A
2N/A config = getnetconfigent("tcp");
2N/A if (config == NULL)
2N/A return (1);
2N/A
2N/A if (__netdir_getbyaddr_nosrv(config, &clnames, &buf)) {
2N/A freenetconfigent(config);
2N/A return (0);
2N/A }
2N/A freenetconfigent(config);
2N/A
2N/A for (gr = strtok_r(access_list, ":", &lasts);
2N/A gr != NULL; gr = strtok_r(NULL, ":", &lasts)) {
2N/A
2N/A /*
2N/A * If the list name has a '-' prepended
2N/A * then a match of the following name
2N/A * implies failure instead of success.
2N/A */
2N/A if (*gr == '-') {
2N/A response = 0;
2N/A gr++;
2N/A } else {
2N/A response = 1;
2N/A }
2N/A
2N/A /*
2N/A * The following loops through all the
2N/A * client's aliases. Usually it's just one name.
2N/A */
2N/A for (i = 0; i < clnames->h_cnt; i++) {
2N/A host = clnames->h_hostservs[i].h_host;
2N/A /*
2N/A * If the list name begins with a dot then
2N/A * do a domain name suffix comparison.
2N/A * A single dot matches any name with no
2N/A * suffix.
2N/A */
2N/A if (*gr == '.') {
2N/A if (*(gr + 1) == '\0') { /* single dot */
2N/A if (strchr(host, '.') == NULL)
2N/A return (response);
2N/A } else {
2N/A off = strlen(host) - strlen(gr);
2N/A if (off > 0 &&
2N/A strcasecmp(host + off, gr) == 0) {
2N/A return (response);
2N/A }
2N/A }
2N/A } else {
2N/A
2N/A /*
2N/A * If the list name begins with an at
2N/A * sign then do a network comparison.
2N/A */
2N/A if (*gr == '@') {
2N/A if (smb_netmatch(&buf, gr + 1))
2N/A return (response);
2N/A } else {
2N/A /*
2N/A * Just do a hostname match
2N/A */
2N/A if (strcasecmp(gr, host) == 0)
2N/A return (response);
2N/A }
2N/A }
2N/A }
2N/A
2N/A nentries++;
2N/A }
2N/A
2N/A netgroup_match = smb_netgroup_match(clnames, access_list, nentries);
2N/A
2N/A return (netgroup_match);
2N/A}
2N/A
2N/A/*
2N/A * smb_make_mask
2N/A *
2N/A * Construct a mask for an IPv4 address using the @<dotted-ip>/<len>
2N/A * syntax or use the default mask for the IP address.
2N/A */
2N/Astatic uint_t
2N/Asmb_make_mask(char *maskstr, uint_t addr)
2N/A{
2N/A uint_t mask;
2N/A uint_t bits;
2N/A
2N/A /*
2N/A * If the mask is specified explicitly then
2N/A * use that value, e.g.
2N/A *
2N/A * @109.104.56/28
2N/A *
2N/A * otherwise assume a mask from the zero octets
2N/A * in the least significant bits of the address, e.g.
2N/A *
2N/A * @109.104 or @109.104.0.0
2N/A */
2N/A if (maskstr) {
2N/A bits = atoi(maskstr);
2N/A mask = bits ? ~0 << ((sizeof (struct in_addr) * NBBY) - bits)
2N/A : 0;
2N/A addr &= mask;
2N/A } else {
2N/A if ((addr & IN_CLASSA_HOST) == 0)
2N/A mask = IN_CLASSA_NET;
2N/A else if ((addr & IN_CLASSB_HOST) == 0)
2N/A mask = IN_CLASSB_NET;
2N/A else if ((addr & IN_CLASSC_HOST) == 0)
2N/A mask = IN_CLASSC_NET;
2N/A else
2N/A mask = IN_CLASSE_NET;
2N/A }
2N/A
2N/A return (mask);
2N/A}
2N/A
2N/A/*
2N/A * smb_netmatch
2N/A *
2N/A * Check to see if the address in the netbuf matches the "net"
2N/A * specified by name. The format of "name" can be:
2N/A * fully qualified domain name
2N/A * dotted IP address
2N/A * dotted IP address followed by '/<len>'
2N/A * See sharen_nfs(1M) for details.
2N/A */
2N/A
2N/Astatic boolean_t
2N/Asmb_netmatch(struct netbuf *nb, char *name)
2N/A{
2N/A uint_t claddr;
2N/A struct netent n, *np;
2N/A char *mp, *p;
2N/A uint_t addr, mask;
2N/A int i;
2N/A char buff[256];
2N/A
2N/A /*
2N/A * Check if it's an IPv4 addr
2N/A */
2N/A if (nb->len != sizeof (struct sockaddr_in))
2N/A return (B_FALSE);
2N/A
2N/A (void) memcpy(&claddr,
2N/A &((struct sockaddr_in *)nb->buf)->sin_addr.s_addr,
2N/A sizeof (struct in_addr));
2N/A claddr = ntohl(claddr);
2N/A
2N/A mp = strchr(name, '/');
2N/A if (mp)
2N/A *mp++ = '\0';
2N/A
2N/A if (isdigit(*name)) {
2N/A /*
2N/A * Convert a dotted IP address
2N/A * to an IP address. The conversion
2N/A * is not the same as that in inet_addr().
2N/A */
2N/A p = name;
2N/A addr = 0;
2N/A for (i = 0; i < 4; i++) {
2N/A addr |= atoi(p) << ((3-i) * 8);
2N/A p = strchr(p, '.');
2N/A if (p == NULL)
2N/A break;
2N/A p++;
2N/A }
2N/A } else {
2N/A /*
2N/A * Turn the netname into
2N/A * an IP address.
2N/A */
2N/A np = getnetbyname_r(name, &n, buff, sizeof (buff));
2N/A if (np == NULL) {
2N/A return (B_FALSE);
2N/A }
2N/A addr = np->n_net;
2N/A }
2N/A
2N/A mask = smb_make_mask(mp, addr);
2N/A return ((claddr & mask) == addr);
2N/A}
2N/A
2N/A/*
2N/A * smb_netgroup_match
2N/A *
2N/A * Check whether any of the hostnames in clnames are
2N/A * members (or non-members) of the netgroups in glist.
2N/A * Since the innetgr lookup is rather expensive, the
2N/A * result is cached. The cached entry is valid only
2N/A * for VALID_TIME seconds. This works well because
2N/A * typically these lookups occur in clusters when
2N/A * a client is mounting.
2N/A *
2N/A * Note that this routine establishes a host membership
2N/A * in a list of netgroups - we've no idea just which
2N/A * netgroup in the list it is a member of.
2N/A *
2N/A * glist is a character array containing grc strings
2N/A * representing netgroup names (optionally prefixed
2N/A * with '-'). Each string is ended with '\0' and
2N/A * followed immediately by the next string.
2N/A */
2N/Astatic boolean_t
2N/Asmb_netgroup_match(struct nd_hostservlist *clnames, char *glist, int grc)
2N/A{
2N/A char **grl;
2N/A char *gr;
2N/A int nhosts = clnames->h_cnt;
2N/A char *host;
2N/A int i, j, n;
2N/A boolean_t response;
2N/A boolean_t belong = B_FALSE;
2N/A static char *domain = NULL;
2N/A
2N/A if (domain == NULL) {
2N/A int ssize;
2N/A
2N/A domain = malloc(SYS_NMLN);
2N/A if (domain == NULL)
2N/A return (B_FALSE);
2N/A
2N/A ssize = sysinfo(SI_SRPC_DOMAIN, domain, SYS_NMLN);
2N/A if (ssize > SYS_NMLN) {
2N/A free(domain);
2N/A domain = malloc(ssize);
2N/A if (domain == NULL)
2N/A return (B_FALSE);
2N/A ssize = sysinfo(SI_SRPC_DOMAIN, domain, ssize);
2N/A }
2N/A /* Check for error in syscall or NULL domain name */
2N/A if (ssize <= 1)
2N/A return (B_FALSE);
2N/A }
2N/A
2N/A grl = calloc(grc, sizeof (char *));
2N/A if (grl == NULL)
2N/A return (B_FALSE);
2N/A
2N/A for (i = 0, gr = glist; i < grc && !belong; ) {
2N/A /*
2N/A * If the netgroup name has a '-' prepended
2N/A * then a match of this name implies a failure
2N/A * instead of success.
2N/A */
2N/A response = (*gr != '-') ? B_TRUE : B_FALSE;
2N/A
2N/A /*
2N/A * Subsequent names with or without a '-' (but no mix)
2N/A * can be grouped together for a single check.
2N/A */
2N/A for (n = 0; i < grc; i++, n++, gr += strlen(gr) + 1) {
2N/A if ((response && *gr == '-') ||
2N/A (!response && *gr != '-'))
2N/A break;
2N/A
2N/A grl[n] = response ? gr : gr + 1;
2N/A }
2N/A
2N/A /*
2N/A * Check the netgroup for each
2N/A * of the hosts names (usually just one).
2N/A */
2N/A for (j = 0; j < nhosts && !belong; j++) {
2N/A host = clnames->h_hostservs[j].h_host;
2N/A if (__multi_innetgr(n, grl, 1, &host, 0, NULL,
2N/A 1, &domain))
2N/A belong = B_TRUE;
2N/A }
2N/A }
2N/A
2N/A free(grl);
2N/A return (belong ? response : B_FALSE);
2N/A}
2N/A
2N/A/*
2N/A * Resolve the ZFS dataset from a path.
2N/A * Ensure that there are no leading slashes (required for zfs_open).
2N/A */
2N/Aint
2N/Asmb_getdataset(const char *path, char *dataset, size_t len)
2N/A{
2N/A struct mnttab mnttab;
2N/A char *resource;
2N/A int rc;
2N/A
2N/A if ((rc = smb_getzfsmount(path, &mnttab)) == 0) {
2N/A resource = mnttab.mnt_special;
2N/A resource += strspn(resource, "/");
2N/A (void) strlcpy(dataset, resource, len);
2N/A }
2N/A
2N/A return (rc);
2N/A}
2N/A
2N/A/*
2N/A * Resolve the ZFS mountpoint from a path. The ZFS mountpoint property
2N/A * is not reliable: it is not always updated and can remain pointing at
2N/A * a previously used temporary mount point. For example, even though a
2N/A * dataset is mounted on / the mountpoint property may still contain some
2N/A * temporary path such as /tmp/onu.swaWuc.
2N/A *
2N/A * So we have to search the MNTTAB and obtain the mount point from the
2N/A * mnttab entry.
2N/A */
2N/Aint
2N/Asmb_getmountpoint(const char *path, char *mountpoint, size_t len)
2N/A{
2N/A struct mnttab mnttab;
2N/A int rc;
2N/A
2N/A if ((rc = smb_getzfsmount(path, &mnttab)) == 0)
2N/A (void) strlcpy(mountpoint, mnttab.mnt_mountp, len);
2N/A
2N/A return (rc);
2N/A}
2N/A
2N/A/*
2N/A * Get the ZFS mnttab entry from a path. If the specified path isn't a ZFS
2N/A * mount point, repeatedly remove the last component of the path until it
2N/A * matches a ZFS mnttab entry.
2N/A *
2N/A * Returns: 0 on success, -1 on failure.
2N/A */
2N/Astatic int
2N/Asmb_getzfsmount(const char *path, struct mnttab *mnttab)
2N/A{
2N/A char tmppath[MAXPATHLEN];
2N/A char *cp;
2N/A FILE *fp;
2N/A struct mnttab mntpref;
2N/A int rc = -1;
2N/A
2N/A if ((fp = fopen(MNTTAB, "r")) == NULL)
2N/A return (-1);
2N/A
2N/A (void) memset(mnttab, '\0', sizeof (mnttab));
2N/A (void) strlcpy(tmppath, path, MAXPATHLEN);
2N/A cp = tmppath;
2N/A
2N/A while (*cp != '\0') {
2N/A resetmnttab(fp);
2N/A (void) memset(&mntpref, '\0', sizeof (mntpref));
2N/A mntpref.mnt_mountp = tmppath;
2N/A
2N/A if (getmntany(fp, mnttab, &mntpref) == 0) {
2N/A if (mnttab->mnt_fstype == NULL)
2N/A break;
2N/A
2N/A if (strcmp(mnttab->mnt_fstype, MNTTYPE_ZFS) == 0)
2N/A rc = 0;
2N/A break;
2N/A }
2N/A
2N/A if (strcmp(tmppath, "/") == 0)
2N/A break;
2N/A
2N/A if ((cp = strrchr(tmppath, '/')) == NULL)
2N/A break;
2N/A
2N/A /*
2N/A * The path has multiple components.
2N/A * Remove the last component and try again.
2N/A */
2N/A *cp = '\0';
2N/A if (tmppath[0] == '\0')
2N/A (void) strcpy(tmppath, "/");
2N/A
2N/A cp = tmppath;
2N/A }
2N/A
2N/A (void) fclose(fp);
2N/A return (rc);
2N/A}
2N/A
2N/A/*
2N/A * smb_dlopen
2N/A *
2N/A * Check to see if an interposer library exists. If it exists
2N/A * and reports a valid version number and key (UUID), return
2N/A * a handle to the library. Otherwise, return NULL.
2N/A */
2N/Avoid *
2N/Asmb_dlopen(void)
2N/A{
2N/A uuid_t uuid;
2N/A void *interposer_hdl;
2N/A typedef int (*smbex_versionfn_t)(smbex_version_t *);
2N/A smbex_versionfn_t getversion;
2N/A smbex_version_t *version;
2N/A
2N/A bzero(&uuid, sizeof (uuid_t));
2N/A if (uuid_parse(SMBEX_KEY, uuid) < 0)
2N/A return (NULL);
2N/A
2N/A interposer_hdl = dlopen(SMB_LIB_ALT, RTLD_NOW | RTLD_LOCAL);
2N/A if (interposer_hdl == NULL)
2N/A return (NULL);
2N/A
2N/A bzero(&getversion, sizeof (smbex_versionfn_t));
2N/A getversion = (smbex_versionfn_t)dlsym(interposer_hdl,
2N/A "smbex_get_version");
2N/A if ((getversion == NULL) ||
2N/A (version = malloc(sizeof (smbex_version_t))) == NULL) {
2N/A (void) dlclose(interposer_hdl);
2N/A return (NULL);
2N/A }
2N/A bzero(version, sizeof (smbex_version_t));
2N/A
2N/A if ((getversion(version) != 0) ||
2N/A (version->v_version != SMBEX_VERSION) ||
2N/A (uuid_compare(version->v_uuid, uuid) != 0)) {
2N/A free(version);
2N/A (void) dlclose(interposer_hdl);
2N/A return (NULL);
2N/A }
2N/A
2N/A free(version);
2N/A return (interposer_hdl);
2N/A}
2N/A
2N/A/*
2N/A * smb_dlclose
2N/A *
2N/A * Closes handle to the interposed library.
2N/A */
2N/Avoid
2N/Asmb_dlclose(void *handle)
2N/A{
2N/A if (handle)
2N/A (void) dlclose(handle);
2N/A}
2N/A
2N/A/*
2N/A * This function is a wrapper for getnameinfo() to look up a hostname given an
2N/A * IP address. The hostname returned by this function is used for constructing
2N/A * the service principal name field of KRB AP-REQs. Hence, it should be
2N/A * converted to lowercase for RFC 4120 section 6.2.1 conformance.
2N/A */
2N/Aint
2N/Asmb_getnameinfo(smb_inaddr_t *ip, char *hostname, int hostlen, int flags)
2N/A{
2N/A socklen_t salen;
2N/A struct sockaddr_in6 sin6;
2N/A struct sockaddr_in sin;
2N/A void *sp;
2N/A int rc;
2N/A
2N/A if (ip->a_family == AF_INET) {
2N/A salen = sizeof (struct sockaddr_in);
2N/A sin.sin_family = ip->a_family;
2N/A sin.sin_port = 0;
2N/A sin.sin_addr.s_addr = ip->a_ipv4;
2N/A sp = &sin;
2N/A } else {
2N/A salen = sizeof (struct sockaddr_in6);
2N/A sin6.sin6_family = ip->a_family;
2N/A sin6.sin6_port = 0;
2N/A (void) memcpy(&sin6.sin6_addr.s6_addr, &ip->a_ipv6,
2N/A sizeof (sin6.sin6_addr.s6_addr));
2N/A sp = &sin6;
2N/A }
2N/A
2N/A if ((rc = (getnameinfo((struct sockaddr *)sp, salen,
2N/A hostname, hostlen, NULL, 0, flags))) == 0)
2N/A (void) smb_strlwr(hostname);
2N/A
2N/A return (rc);
2N/A}
2N/A
2N/A/*
2N/A * A share name is considered invalid if it contains control
2N/A * characters or any of the following characters (MSDN 236388).
2N/A *
2N/A * " / \ [ ] : | < > + ; , ? * =
2N/A *
2N/A * Control characters are defined as 0x00 - 0x1F inclusiv eand 0x7f.
2N/A * Using SMB_ISCNTRL here to restrict test to these characters.
2N/A * The behavior of iscntrl() is affected by the current locale
2N/A * and may contain additional characters (ie 0x80-0x9f).
2N/A */
2N/Auint32_t
2N/Asmb_name_validate_share(const char *sharename)
2N/A{
2N/A const char *invalid = "\"/\\[]:|<>+;,?*=";
2N/A const char *p;
2N/A
2N/A if (sharename == NULL)
2N/A return (ERROR_INVALID_PARAMETER);
2N/A
2N/A if (strlen(sharename) > SMB_SHARE_NTNAME_MAX)
2N/A return (ERROR_INVALID_NAME);
2N/A
2N/A if (strpbrk(sharename, invalid) != NULL)
2N/A return (ERROR_INVALID_NAME);
2N/A
2N/A for (p = sharename; *p != '\0'; p++) {
2N/A if (SMB_ISCNTRL(*p))
2N/A return (ERROR_INVALID_NAME);
2N/A }
2N/A
2N/A return (ERROR_SUCCESS);
2N/A}
2N/A
2N/A/*
2N/A * User and group names are limited to 256 characters, cannot be terminated
2N/A * by '.' and must not contain control characters or any of the following
2N/A * characters.
2N/A *
2N/A * " / \ [ ] < > + ; , ? * = @
2N/A */
2N/Auint32_t
2N/Asmb_name_validate_account(const char *name)
2N/A{
2N/A const char *invalid = "\"/\\[]<>+;,?*=@";
2N/A const char *p;
2N/A int len;
2N/A
2N/A if ((name == NULL) || (*name == '\0'))
2N/A return (ERROR_INVALID_PARAMETER);
2N/A
2N/A len = strlen(name);
2N/A if ((len > MAXNAMELEN) || (name[len - 1] == '.'))
2N/A return (ERROR_INVALID_NAME);
2N/A
2N/A if (strpbrk(name, invalid) != NULL)
2N/A return (ERROR_INVALID_NAME);
2N/A
2N/A for (p = name; *p != '\0'; p++) {
2N/A if (iscntrl(*p))
2N/A return (ERROR_INVALID_NAME);
2N/A }
2N/A
2N/A return (ERROR_SUCCESS);
2N/A}
2N/A
2N/A/*
2N/A * Check a domain name for RFC 1035 and 1123 compliance. Domain names may
2N/A * contain alphanumeric characters, hyphens and dots. The first and last
2N/A * character of a label must be alphanumeric. Interior characters may be
2N/A * alphanumeric or hypens.
2N/A *
2N/A * Domain names should not contain underscores but we allow them because
2N/A * Windows names are often in non-compliance with this rule.
2N/A */
2N/Auint32_t
2N/Asmb_name_validate_domain(const char *domain)
2N/A{
2N/A boolean_t new_label = B_TRUE;
2N/A const char *p;
2N/A char label_terminator;
2N/A
2N/A if (domain == NULL)
2N/A return (ERROR_INVALID_PARAMETER);
2N/A
2N/A if (*domain == '\0')
2N/A return (ERROR_INVALID_NAME);
2N/A
2N/A label_terminator = *domain;
2N/A
2N/A for (p = domain; *p != '\0'; ++p) {
2N/A if (new_label) {
2N/A if (!isalnum(*p))
2N/A return (ERROR_INVALID_NAME);
2N/A new_label = B_FALSE;
2N/A label_terminator = *p;
2N/A continue;
2N/A }
2N/A
2N/A if (*p == '.') {
2N/A if (!isalnum(label_terminator))
2N/A return (ERROR_INVALID_NAME);
2N/A new_label = B_TRUE;
2N/A label_terminator = *p;
2N/A continue;
2N/A }
2N/A
2N/A label_terminator = *p;
2N/A
2N/A if (isalnum(*p) || *p == '-' || *p == '_')
2N/A continue;
2N/A
2N/A return (ERROR_INVALID_NAME);
2N/A }
2N/A
2N/A if (!isalnum(label_terminator))
2N/A return (ERROR_INVALID_NAME);
2N/A
2N/A return (ERROR_SUCCESS);
2N/A}
2N/A
2N/A/*
2N/A * A NetBIOS domain name can contain letters (a-zA-Z), numbers (0-9) and
2N/A * hyphens.
2N/A *
2N/A * It cannot:
2N/A * - be blank or longer than 15 chracters
2N/A * - contain all numbers
2N/A * - be the same as the computer name
2N/A */
2N/Auint32_t
2N/Asmb_name_validate_nbdomain(const char *name)
2N/A{
2N/A char netbiosname[NETBIOS_NAME_SZ];
2N/A const char *p;
2N/A int len;
2N/A
2N/A if (name == NULL)
2N/A return (ERROR_INVALID_PARAMETER);
2N/A
2N/A len = strlen(name);
2N/A if (len == 0 || len >= NETBIOS_NAME_SZ)
2N/A return (ERROR_INVALID_NAME);
2N/A
2N/A if (strspn(name, "0123456789") == len)
2N/A return (ERROR_INVALID_NAME);
2N/A
2N/A if (smb_getnetbiosname(netbiosname, NETBIOS_NAME_SZ) == 0) {
2N/A if (smb_strcasecmp(name, netbiosname, 0) == 0)
2N/A return (ERROR_INVALID_NAME);
2N/A }
2N/A
2N/A for (p = name; *p != '\0'; ++p) {
2N/A if (isalnum(*p) || *p == '-' || *p == '_')
2N/A continue;
2N/A
2N/A return (ERROR_INVALID_NAME);
2N/A }
2N/A
2N/A return (ERROR_SUCCESS);
2N/A}
2N/A
2N/A/*
2N/A * A workgroup name can contain 1 to 15 characters but cannot be the same
2N/A * as the NetBIOS name. The name must begin with a letter or number.
2N/A *
2N/A * The name cannot consist entirely of spaces or dots, which is covered
2N/A * by the requirement that the name must begin with an alphanumeric
2N/A * character.
2N/A *
2N/A * The name must not contain control characters or any of the following
2N/A * characters.
2N/A *
2N/A * " / \ [ ] : | < > + = ; , ?
2N/A */
2N/Auint32_t
2N/Asmb_name_validate_workgroup(const char *workgroup)
2N/A{
2N/A char netbiosname[NETBIOS_NAME_SZ];
2N/A const char *invalid = "\"/\\[]:|<>+=;,?";
2N/A const char *p;
2N/A
2N/A if (workgroup == NULL)
2N/A return (ERROR_INVALID_PARAMETER);
2N/A
2N/A if (*workgroup == '\0' || (!isalnum(*workgroup)))
2N/A return (ERROR_INVALID_NAME);
2N/A
2N/A if (strlen(workgroup) >= NETBIOS_NAME_SZ)
2N/A return (ERROR_INVALID_NAME);
2N/A
2N/A if (smb_getnetbiosname(netbiosname, NETBIOS_NAME_SZ) == 0) {
2N/A if (smb_strcasecmp(workgroup, netbiosname, 0) == 0)
2N/A return (ERROR_INVALID_NAME);
2N/A }
2N/A
2N/A if (strpbrk(workgroup, invalid) != NULL)
2N/A return (ERROR_INVALID_NAME);
2N/A
2N/A for (p = workgroup; *p != '\0'; p++) {
2N/A if (iscntrl(*p))
2N/A return (ERROR_INVALID_NAME);
2N/A }
2N/A
2N/A return (ERROR_SUCCESS);
2N/A}
2N/A
2N/A/*
2N/A * Check for invalid characters in the given path. The list of invalid
2N/A * characters includes control characters and the following:
2N/A *
2N/A * " / \ [ ] : | < > + ; , ? * =
2N/A *
2N/A * Since this is checking a path not each component, '/' is accepted
2N/A * as separator not an invalid character, except as the first character
2N/A * since this is supposed to be a relative path.
2N/A */
2N/Auint32_t
2N/Asmb_name_validate_rpath(const char *relpath)
2N/A{
2N/A char *invalid = "\"\\[]:|<>+;,?*=";
2N/A char *cp;
2N/A
2N/A if ((relpath == NULL) || (*relpath == '\0') || (*relpath == '/'))
2N/A return (ERROR_INVALID_NAME);
2N/A
2N/A if (strpbrk(relpath, invalid))
2N/A return (ERROR_INVALID_NAME);
2N/A
2N/A for (cp = (char *)relpath; *cp != '\0'; cp++) {
2N/A if (iscntrl(*cp))
2N/A return (ERROR_INVALID_NAME);
2N/A }
2N/A
2N/A return (ERROR_SUCCESS);
2N/A}
2N/A
2N/A/*
2N/A * Parse a string to obtain the account and domain names as separate strings.
2N/A *
2N/A * Names containing a backslash ('\') are known as qualified or composite
2N/A * names. The string preceding the backslash should be the domain name
2N/A * and the string following the slash should be a name within that domain.
2N/A *
2N/A * Names that do not contain a backslash are known as isolated names.
2N/A * An isolated name may be a single label, such as john, or may be in
2N/A * user principal name (UPN) form, such as john@example.com.
2N/A *
2N/A * domain\name
2N/A * domain/name
2N/A * name
2N/A * name@domain
2N/A *
2N/A * If we encounter any of the forms above in arg, the @, / or \ separator
2N/A * is replaced by \0 and the name and domain pointers are set to point to
2N/A * the appropriate components in arg. Otherwise, name and domain pointers
2N/A * will be set to NULL.
2N/A */
2N/Avoid
2N/Asmb_name_parse(char *arg, char **account, char **domain)
2N/A{
2N/A char *p;
2N/A
2N/A *account = NULL;
2N/A *domain = NULL;
2N/A
2N/A if ((p = strpbrk(arg, "/\\@")) != NULL) {
2N/A if (*p == '@') {
2N/A *p = '\0';
2N/A ++p;
2N/A *domain = p;
2N/A *account = arg;
2N/A } else {
2N/A *p = '\0';
2N/A ++p;
2N/A *account = p;
2N/A *domain = arg;
2N/A }
2N/A } else {
2N/A *account = arg;
2N/A }
2N/A}
2N/A
2N/Avoid
2N/Asmb_joininfo_init_wkgrp(const char *wkgrp_name, smb_joininfo_t *jdi)
2N/A{
2N/A bzero(jdi, sizeof (smb_joininfo_t));
2N/A jdi->j_mode = SMB_SECMODE_WORKGRP;
2N/A (void) strlcpy(jdi->j_wkgrp, wkgrp_name, sizeof (jdi->j_wkgrp));
2N/A}
2N/A
2N/A/*
2N/A * Update the following fields of the passed smb_joininfo_t instance based on
2N/A * the DC discovery results:
2N/A *
2N/A * j_fqdomain, j_nbdomain, and j_dc
2N/A */
2N/Avoid
2N/Asmb_joininfo_update_domain(smb_joininfo_t *jdi, const smb_discovery_res_t *res)
2N/A{
2N/A (void) strlcpy(jdi->j_nbdomain, res->r_nbdomain,
2N/A sizeof (jdi->j_nbdomain));
2N/A (void) strlcpy(jdi->j_fqdomain, res->r_fqdomain,
2N/A sizeof (jdi->j_fqdomain));
2N/A (void) strlcpy(jdi->j_dc, res->r_dc, sizeof (jdi->j_dc));
2N/A}
2N/A
2N/A/*
2N/A * smb_join_errmsg
2N/A *
2N/A * Display error message for the specific adjoin error code.
2N/A */
2N/Avoid
2N/Asmb_join_errmsg(smb_join_status_t status, boolean_t to_stderr)
2N/A{
2N/A int i;
2N/A static const struct xlate_table {
2N/A smb_join_status_t status;
2N/A char *msg;
2N/A } adjoin_table[] = {
2N/A { SMB_JOIN_ERR_GET_USR_HANDLE,
2N/A "Unable to bind to an Active Directory server "
2N/A "(using the specified user credentials)" },
2N/A { SMB_JOIN_ERR_GET_SYS_HANDLE,
2N/A "Unable to bind to an Active Directory server "
2N/A "(using the local host credentials)" },
2N/A { SMB_JOIN_ERR_GET_DCLEVEL, "Unknown domain controller "
2N/A "functional level: The rootDSE attribute named "
2N/A "\"domainControllerFunctionality\" is missing from "
2N/A "Active Directory" },
2N/A { SMB_JOIN_ERR_NO_TRUST_ACCT,
2N/A "Workstation trust account doesn't exist" },
2N/A { SMB_JOIN_ERR_ADD_TRUST_ACCT,
2N/A "Workstation trust account creation failed" },
2N/A { SMB_JOIN_ERR_MOD_TRUST_ACCT,
2N/A "Workstation trust account update failed" },
2N/A { SMB_JOIN_ERR_REMOVE_TRUST_ACCT,
2N/A "Workstation trust account removal failed" },
2N/A { SMB_JOIN_ERR_DISABLE_TRUST_ACCT,
2N/A "Workstation trust account cannot be disabled" },
2N/A { SMB_JOIN_ERR_DUP_TRUST_ACCT,
2N/A "Workstation trust account update failed: "
2N/A "The name is in use" },
2N/A { SMB_JOIN_ERR_TRUST_ACCT,
2N/A "Workstation trust account query failed" },
2N/A { SMB_JOIN_ERR_KSETPWD, "Machine password update failed" },
2N/A { SMB_JOIN_ERR_INIT_CNTRL_ATTR,
2N/A "Workstation trust account userAccountControl attribute "
2N/A "initialization failed" },
2N/A { SMB_JOIN_ERR_UPDATE_CNTRL_ATTR,
2N/A "Workstation trust account userAccountControl attribute "
2N/A "update failed" },
2N/A { SMB_JOIN_ERR_WRITE_KEYTAB, "Keytab update failed" },
2N/A { SMB_JOIN_ERR_NOT_IMPLEMENTED, "Operation not implemented" },
2N/A { SMB_JOIN_ERR_INTERNAL, "Internal error" }
2N/A };
2N/A
2N/A for (i = 0; i < sizeof (adjoin_table) / sizeof (adjoin_table[0]); i++) {
2N/A if (adjoin_table[i].status == status) {
2N/A syslog(LOG_NOTICE, "%s", adjoin_table[i].msg);
2N/A if (to_stderr) {
2N/A (void) fprintf(stderr, "%s\n",
2N/A adjoin_table[i].msg);
2N/A }
2N/A }
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * The txid is an arbitrary transaction. A new txid is returned on each call.
2N/A *
2N/A * 0 or -1 are not assigned so that they can be used to detect
2N/A * invalid conditions.
2N/A */
2N/Auint32_t
2N/Asmb_get_txid(void)
2N/A{
2N/A static mutex_t txmutex;
2N/A static uint32_t txid;
2N/A uint32_t txid_ret;
2N/A
2N/A (void) mutex_lock(&txmutex);
2N/A
2N/A if (txid == 0)
2N/A txid = time(NULL);
2N/A
2N/A do {
2N/A ++txid;
2N/A } while (txid == 0 || txid == (uint32_t)-1);
2N/A
2N/A txid_ret = txid;
2N/A (void) mutex_unlock(&txmutex);
2N/A
2N/A return (txid_ret);
2N/A}
2N/A
2N/A/*
2N/A * Creates a log object and inserts it into a list of logs.
2N/A */
2N/Asmb_log_hdl_t
2N/Asmb_log_create(int max_cnt, char *name)
2N/A{
2N/A smb_loglist_item_t *log_node;
2N/A smb_log_t *log = NULL;
2N/A smb_log_hdl_t handle = 0;
2N/A
2N/A if (max_cnt <= 0 || name == NULL)
2N/A return (0);
2N/A
2N/A (void) mutex_lock(&smb_loglist.ll_mtx);
2N/A
2N/A log_node = malloc(sizeof (smb_loglist_item_t));
2N/A
2N/A if (log_node != NULL) {
2N/A log = &log_node->lli_log;
2N/A
2N/A bzero(log, sizeof (smb_log_t));
2N/A
2N/A handle = log->l_handle = smb_get_txid();
2N/A log->l_max_cnt = max_cnt;
2N/A (void) snprintf(log->l_file, sizeof (log->l_file),
2N/A SMB_LOG_FILE_FMT, name);
2N/A
2N/A list_create(&log->l_list, sizeof (smb_log_item_t),
2N/A offsetof(smb_log_item_t, li_lnd));
2N/A
2N/A if (smb_loglist.ll_list.list_size == 0)
2N/A list_create(&smb_loglist.ll_list,
2N/A sizeof (smb_loglist_item_t),
2N/A offsetof(smb_loglist_item_t, lli_lnd));
2N/A
2N/A list_insert_tail(&smb_loglist.ll_list, log_node);
2N/A }
2N/A
2N/A (void) mutex_unlock(&smb_loglist.ll_mtx);
2N/A
2N/A return (handle);
2N/A}
2N/A
2N/A/*
2N/A * Keep the most recent log entries, based on max count.
2N/A * If the priority is LOG_ERR or higher then the entire log is
2N/A * dumped to a file.
2N/A *
2N/A * The date format for each message is the same as a syslog entry.
2N/A *
2N/A * The log is also added to syslog via smb_log_trace().
2N/A */
2N/Avoid
2N/Asmb_log(smb_log_hdl_t hdl, int priority, const char *fmt, ...)
2N/A{
2N/A va_list ap;
2N/A smb_log_t *log;
2N/A smb_log_item_t *msg;
2N/A time_t now;
2N/A struct tm *tm;
2N/A char timebuf[SMB_TIMEBUF_SZ];
2N/A char buf[SMB_LOG_LINE_SZ];
2N/A char netbiosname[NETBIOS_NAME_SZ];
2N/A char *pri_name;
2N/A int i;
2N/A
2N/A va_start(ap, fmt);
2N/A (void) vsnprintf(buf, SMB_LOG_LINE_SZ, fmt, ap);
2N/A va_end(ap);
2N/A
2N/A priority &= LOG_PRIMASK;
2N/A smb_log_trace(priority, buf);
2N/A
2N/A if ((log = smb_log_get(hdl)) == NULL)
2N/A return;
2N/A
2N/A (void) mutex_lock(&log->l_mtx);
2N/A
2N/A (void) time(&now);
2N/A tm = localtime(&now);
2N/A (void) strftime(timebuf, SMB_TIMEBUF_SZ, "%b %d %H:%M:%S", tm);
2N/A
2N/A if (smb_getnetbiosname(netbiosname, NETBIOS_NAME_SZ) != 0)
2N/A (void) strlcpy(netbiosname, "unknown", NETBIOS_NAME_SZ);
2N/A
2N/A if (log->l_cnt == log->l_max_cnt) {
2N/A msg = list_head(&log->l_list);
2N/A list_remove(&log->l_list, msg);
2N/A } else {
2N/A if ((msg = malloc(sizeof (smb_log_item_t))) == NULL) {
2N/A (void) mutex_unlock(&log->l_mtx);
2N/A return;
2N/A }
2N/A log->l_cnt++;
2N/A }
2N/A
2N/A pri_name = "info";
2N/A for (i = 0; i < sizeof (smb_log_pri) / sizeof (smb_log_pri[0]); i++) {
2N/A if (priority == smb_log_pri[i].lp_value) {
2N/A pri_name = smb_log_pri[i].lp_name;
2N/A break;
2N/A }
2N/A }
2N/A
2N/A (void) snprintf(msg->li_msg, SMB_LOG_LINE_SZ,
2N/A "%s %s smb[%d]: [ID 0 daemon.%s] %s",
2N/A timebuf, netbiosname, getpid(), pri_name, buf);
2N/A list_insert_tail(&log->l_list, msg);
2N/A
2N/A if (priority <= LOG_ERR)
2N/A smb_log_dump(log);
2N/A
2N/A (void) mutex_unlock(&log->l_mtx);
2N/A}
2N/A
2N/A/*
2N/A * Dumps all the logs in the log list.
2N/A */
2N/Avoid
2N/Asmb_log_dumpall()
2N/A{
2N/A smb_loglist_item_t *log_node;
2N/A
2N/A (void) mutex_lock(&smb_loglist.ll_mtx);
2N/A
2N/A log_node = list_head(&smb_loglist.ll_list);
2N/A
2N/A while (log_node != NULL) {
2N/A smb_log_dump(&log_node->lli_log);
2N/A log_node = list_next(&smb_loglist.ll_list, log_node);
2N/A }
2N/A
2N/A (void) mutex_unlock(&smb_loglist.ll_mtx);
2N/A}
2N/A
2N/Astatic void
2N/Asmb_log_trace(int priority, const char *s)
2N/A{
2N/A syslog(priority, "%s", s);
2N/A}
2N/A
2N/Astatic smb_log_t *
2N/Asmb_log_get(smb_log_hdl_t hdl)
2N/A{
2N/A smb_loglist_item_t *log_node;
2N/A smb_log_t *log;
2N/A
2N/A (void) mutex_lock(&smb_loglist.ll_mtx);
2N/A
2N/A log_node = list_head(&smb_loglist.ll_list);
2N/A
2N/A while (log_node != NULL) {
2N/A if (log_node->lli_log.l_handle == hdl) {
2N/A log = &log_node->lli_log;
2N/A (void) mutex_unlock(&smb_loglist.ll_mtx);
2N/A return (log);
2N/A }
2N/A log_node = list_next(&smb_loglist.ll_list, log_node);
2N/A }
2N/A
2N/A (void) mutex_unlock(&smb_loglist.ll_mtx);
2N/A return (NULL);
2N/A}
2N/A
2N/A/*
2N/A * Dumps the log to a file.
2N/A */
2N/Astatic void
2N/Asmb_log_dump(smb_log_t *log)
2N/A{
2N/A smb_log_item_t *msg;
2N/A FILE *fp;
2N/A
2N/A if ((fp = fopen(log->l_file, "w")) == NULL)
2N/A return;
2N/A
2N/A msg = list_head(&log->l_list);
2N/A
2N/A while (msg != NULL) {
2N/A (void) fprintf(fp, "%s\n", msg->li_msg);
2N/A msg = list_next(&log->l_list, msg);
2N/A }
2N/A
2N/A (void) fclose(fp);
2N/A}
2N/A
2N/A/*
2N/A * Creates a lock file and grabs an exclusive (write) lock on it.
2N/A */
2N/Aint
2N/Asmb_file_lock(char *lock_file, smb_lockinfo_t *lockinfo)
2N/A{
2N/A int seconds = 0;
2N/A
2N/A (void) mutex_lock(&lockinfo->l_mtx);
2N/A for (;;) {
2N/A if (lockinfo->l_pid != 0 && lockinfo->l_pid != getpid()) {
2N/A /* somebody forked */
2N/A lockinfo->l_pid = 0;
2N/A lockinfo->l_tid = 0;
2N/A }
2N/A
2N/A if (lockinfo->l_tid == 0) {
2N/A if ((lockinfo->l_fildes = creat(lock_file, 0600)) == -1)
2N/A break;
2N/A lockinfo->l_flock.l_type = F_WRLCK;
2N/A if (fcntl(lockinfo->l_fildes,
2N/A F_SETLK, &lockinfo->l_flock) != -1) {
2N/A lockinfo->l_pid = getpid();
2N/A lockinfo->l_tid = thr_self();
2N/A (void) mutex_unlock(&lockinfo->l_mtx);
2N/A return (0);
2N/A }
2N/A (void) close(lockinfo->l_fildes);
2N/A lockinfo->l_fildes = -1;
2N/A }
2N/A
2N/A if (seconds++ >= SMB_FLOCK_WAIT) {
2N/A /*
2N/A * For compatibility with the past, pretend
2N/A * that we were interrupted by SIGALRM.
2N/A */
2N/A errno = EINTR;
2N/A break;
2N/A }
2N/A
2N/A (void) mutex_unlock(&lockinfo->l_mtx);
2N/A (void) sleep(1);
2N/A (void) mutex_lock(&lockinfo->l_mtx);
2N/A }
2N/A (void) mutex_unlock(&lockinfo->l_mtx);
2N/A
2N/A return (-1);
2N/A}
2N/A
2N/A/*
2N/A * Unlocks file for operations done via this library APIs.
2N/A */
2N/Aint
2N/Asmb_file_unlock(smb_lockinfo_t *lockinfo)
2N/A{
2N/A (void) mutex_lock(&lockinfo->l_mtx);
2N/A if (lockinfo->l_tid == thr_self() && lockinfo->l_fildes >= 0) {
2N/A lockinfo->l_flock.l_type = F_UNLCK;
2N/A (void) fcntl(lockinfo->l_fildes, F_SETLK, &lockinfo->l_flock);
2N/A (void) close(lockinfo->l_fildes);
2N/A lockinfo->l_fildes = -1;
2N/A lockinfo->l_pid = 0;
2N/A lockinfo->l_tid = 0;
2N/A (void) mutex_unlock(&lockinfo->l_mtx);
2N/A return (0);
2N/A }
2N/A (void) mutex_unlock(&lockinfo->l_mtx);
2N/A return (-1);
2N/A}