summaryrefslogtreecommitdiff
path: root/usr/src/lib/smbsrv/libsmb/common/smb_lgrp.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/lib/smbsrv/libsmb/common/smb_lgrp.c')
-rw-r--r--usr/src/lib/smbsrv/libsmb/common/smb_lgrp.c2319
1 files changed, 2319 insertions, 0 deletions
diff --git a/usr/src/lib/smbsrv/libsmb/common/smb_lgrp.c b/usr/src/lib/smbsrv/libsmb/common/smb_lgrp.c
new file mode 100644
index 0000000000..934b804dcd
--- /dev/null
+++ b/usr/src/lib/smbsrv/libsmb/common/smb_lgrp.c
@@ -0,0 +1,2319 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdlib.h>
+#include <strings.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <thread.h>
+#include <synch.h>
+#include <grp.h>
+#include <assert.h>
+#include <libintl.h>
+#include <smbsrv/libsmb.h>
+
+#define SMB_LGRP_LOCAL_IDX 0
+#define SMB_LGRP_BUILTIN_IDX 1
+
+#define SMB_LGRP_DB_NAME "/var/smb/smbgroup.db"
+#define SMB_LGRP_DB_TIMEOUT 3000 /* in millisecond */
+#define SMB_LGRP_DB_VERMAJOR 1
+#define SMB_LGRP_DB_VERMINOR 0
+#define SMB_LGRP_DB_MAGIC 0x4C475250 /* LGRP */
+
+#define SMB_LGRP_DB_ORD 1 /* open read-only */
+#define SMB_LGRP_DB_ORW 2 /* open read/write */
+
+#define SMB_LGRP_DB_ADDMEMBER 1
+#define SMB_LGRP_DB_DELMEMBER 2
+
+/*
+ * members column of the groups table is an array of
+ * member structure smb_lgmid_t defined below.
+ *
+ * privs column of the groups table is an array of bytes
+ * where each byte is the id of an enable privilege
+ */
+#define SMB_LGRP_DB_SQL \
+ "CREATE TABLE db_info (" \
+ " ver_major INTEGER," \
+ " ver_minor INTEGER," \
+ " magic INTEGER" \
+ ");" \
+ "" \
+ "CREATE TABLE domains (" \
+ " dom_idx INTEGER PRIMARY KEY," \
+ " dom_sid TEXT UNIQUE," \
+ " dom_cnt INTEGER" \
+ ");" \
+ "" \
+ "CREATE UNIQUE INDEX domsid_idx ON domains (dom_sid);" \
+ "" \
+ "CREATE TABLE groups (" \
+ " name TEXT PRIMARY KEY," \
+ " sid_idx INTEGER," \
+ " sid_rid INTEGER," \
+ " sid_type INTEGER," \
+ " sid_attrs INTEGER," \
+ " comment TEXT," \
+ " n_privs INTEGER," \
+ " privs BLOB," \
+ " n_members INTEGER," \
+ " members BLOB" \
+ ");" \
+ "" \
+ "CREATE INDEX grprid_idx ON groups (sid_rid);"
+
+/*
+ * Number of groups table columns
+ */
+#define SMB_LGRP_GTBL_NCOL 10
+
+#define SMB_LGRP_GTBL_NAME 0
+#define SMB_LGRP_GTBL_SIDIDX 1
+#define SMB_LGRP_GTBL_SIDRID 2
+#define SMB_LGRP_GTBL_SIDTYP 3
+#define SMB_LGRP_GTBL_SIDATR 4
+#define SMB_LGRP_GTBL_CMNT 5
+#define SMB_LGRP_GTBL_NPRIVS 6
+#define SMB_LGRP_GTBL_PRIVS 7
+#define SMB_LGRP_GTBL_NMEMBS 8
+#define SMB_LGRP_GTBL_MEMBS 9
+
+#define SMB_LGRP_INFO_NONE 0x00
+#define SMB_LGRP_INFO_NAME 0x01
+#define SMB_LGRP_INFO_CMNT 0x02
+#define SMB_LGRP_INFO_SID 0x04
+#define SMB_LGRP_INFO_PRIV 0x08
+#define SMB_LGRP_INFO_MEMB 0x10
+#define SMB_LGRP_INFO_ALL 0x1F
+
+#define NULL_MSGCHK(msg) ((msg) ? (msg) : "NULL")
+
+/* Member ID */
+typedef struct smb_lgmid {
+ uint32_t m_idx;
+ uint32_t m_rid;
+ uint16_t m_type;
+} smb_lgmid_t;
+
+#define SMB_LGRP_MID_HEXSZ 32
+
+/* Member list */
+typedef struct smb_lgmlist {
+ uint32_t m_cnt;
+ char *m_ids;
+} smb_lgmlist_t;
+
+/* Privilege ID */
+typedef uint8_t smb_lgpid_t;
+
+/* Privilege list */
+typedef struct smb_lgplist {
+ uint32_t p_cnt;
+ smb_lgpid_t *p_ids;
+} smb_lgplist_t;
+
+static mutex_t smb_lgrp_lsid_mtx;
+static nt_sid_t *smb_lgrp_lsid;
+
+static int smb_lgrp_db_init(void);
+static sqlite *smb_lgrp_db_open(int);
+static void smb_lgrp_db_close(sqlite *);
+static int smb_lgrp_db_setinfo(sqlite *);
+
+static boolean_t smb_lgrp_gtbl_exists(sqlite *, char *);
+static int smb_lgrp_gtbl_lookup(sqlite *, int, smb_group_t *, int, ...);
+static int smb_lgrp_gtbl_insert(sqlite *, smb_group_t *);
+static int smb_lgrp_gtbl_update(sqlite *, char *, smb_group_t *, int);
+static int smb_lgrp_gtbl_delete(sqlite *, char *);
+static int smb_lgrp_gtbl_update_mlist(sqlite *, char *, smb_gsid_t *, int);
+static int smb_lgrp_gtbl_update_plist(sqlite *, char *, uint8_t, boolean_t);
+static int smb_lgrp_gtbl_count(sqlite *, int, int *);
+
+static int smb_lgrp_dtbl_insert(sqlite *, char *, uint32_t *);
+static int smb_lgrp_dtbl_getidx(sqlite *, nt_sid_t *, uint16_t,
+ uint32_t *, uint32_t *);
+static int smb_lgrp_dtbl_getsid(sqlite *, uint32_t, nt_sid_t **);
+
+static int smb_lgrp_mlist_add(smb_lgmlist_t *, smb_lgmid_t *, smb_lgmlist_t *);
+static int smb_lgrp_mlist_del(smb_lgmlist_t *, smb_lgmid_t *, smb_lgmlist_t *);
+
+static int smb_lgrp_plist_add(smb_lgplist_t *, smb_lgpid_t, smb_lgplist_t *);
+static int smb_lgrp_plist_del(smb_lgplist_t *, smb_lgpid_t, smb_lgplist_t *);
+
+static void smb_lgrp_encode_privset(smb_group_t *, smb_lgplist_t *);
+
+static int smb_lgrp_decode(smb_group_t *, char **, int, sqlite *);
+static int smb_lgrp_decode_privset(smb_group_t *, char *, char *);
+static int smb_lgrp_decode_members(smb_group_t *, char *, char *, sqlite *);
+
+static void smb_lgrp_set_default_privs(smb_group_t *);
+static boolean_t smb_lgrp_chkname(char *);
+static boolean_t smb_lgrp_chkmember(uint16_t);
+static int smb_lgrp_getsid(int, uint32_t *, uint16_t, sqlite *, nt_sid_t **);
+
+#ifdef _LP64
+/*
+ * We cannot make 64-bit version of libsqlite because the code
+ * has some problems.
+ */
+
+/*ARGSUSED*/
+sqlite *
+sqlite_open(const char *filename, int mode, char **errmsg)
+{
+ return (NULL);
+}
+
+/*ARGSUSED*/
+void
+sqlite_close(sqlite *db)
+{
+}
+
+/*ARGSUSED*/
+char *
+sqlite_mprintf(const char *fmt, ...)
+{
+ return (NULL);
+}
+
+/*ARGSUSED*/
+void
+sqlite_freemem(void *p)
+{
+}
+
+/*ARGSUSED*/
+int
+sqlite_compile(sqlite *db, const char *zSql, const char **pzTail,
+ sqlite_vm **ppVm, char **pzErrmsg)
+{
+ return (SQLITE_ERROR);
+}
+
+/*ARGSUSED*/
+void
+sqlite_free_table(char **res)
+{
+}
+
+/*ARGSUSED*/
+int
+sqlite_last_insert_rowid(sqlite *db)
+{
+ return (-1);
+}
+
+/*ARGSUSED*/
+void
+sqlite_busy_timeout(sqlite *db, int ms)
+{
+}
+
+/*ARGSUSED*/
+int
+sqlite_get_table(sqlite *db, const char *zSql, char ***pazResult, int *pnRow,
+ int *pnColumn, char **pzErrMsg)
+{
+ return (SQLITE_ERROR);
+}
+
+/*ARGSUSED*/
+int
+sqlite_step(sqlite_vm *pVm, int *pN, const char ***pazValue,
+ const char ***pazColName)
+{
+ return (SQLITE_ERROR);
+}
+
+/*ARGSUSED*/
+int
+sqlite_exec(sqlite *db, const char *zSql, sqlite_callback xCallback, void *pArg,
+ char **pzErrMsg)
+{
+ return (SQLITE_ERROR);
+}
+
+/*ARGSUSED*/
+int
+sqlite_finalize(sqlite_vm *pVm, char **pzErrMsg)
+{
+ return (SQLITE_ERROR);
+}
+#endif /* _LP64 */
+
+/*
+ * smb_lgrp_add
+ *
+ * Create a local group with the given name and comment.
+ * This new group doesn't have any members and no enabled
+ * privileges.
+ *
+ * No well-known accounts can be added other than Administators,
+ * Backup Operators and Power Users. These built-in groups
+ * won't have any members when created but a set of default
+ * privileges will be enabled for them.
+ */
+int
+smb_lgrp_add(char *gname, char *cmnt)
+{
+ well_known_account_t *wk_acct;
+ struct group *pxgrp;
+ smb_group_t grp;
+ nt_sid_t *sid = NULL;
+ sqlite *db;
+ int rc;
+
+ (void) trim_whitespace(gname);
+ if (!smb_lgrp_chkname(gname))
+ return (SMB_LGRP_INVALID_NAME);
+
+ if (cmnt && (strlen(cmnt) > SMB_LGRP_COMMENT_MAX))
+ return (SMB_LGRP_INVALID_ARG);
+
+ bzero(&grp, sizeof (grp));
+ grp.sg_name = utf8_strlwr(gname);
+ grp.sg_cmnt = cmnt;
+
+ wk_acct = nt_builtin_lookup(gname);
+ if (wk_acct == NULL) {
+ if ((pxgrp = getgrnam(gname)) == NULL)
+ return (SMB_LGRP_NOT_FOUND);
+
+ /*
+ * Make sure a local SID can be obtained
+ */
+ if (smb_idmap_getsid(pxgrp->gr_gid, SMB_IDMAP_GROUP, &sid)
+ != IDMAP_SUCCESS)
+ return (SMB_LGRP_NO_SID);
+
+ if (!nt_sid_is_indomain(smb_lgrp_lsid, sid)) {
+ free(sid);
+ return (SMB_LGRP_SID_NOTLOCAL);
+ }
+
+ grp.sg_id.gs_type = SidTypeAlias;
+ grp.sg_domain = SMB_LGRP_LOCAL;
+ grp.sg_rid = pxgrp->gr_gid;
+ } else {
+ if (wk_acct->flags & LGF_HIDDEN) {
+ /* cannot add well-known accounts */
+ return (SMB_LGRP_WKSID);
+ }
+
+ grp.sg_id.gs_type = wk_acct->sid_name_use;
+ if ((sid = nt_sid_strtosid(wk_acct->sid)) == NULL)
+ return (SMB_LGRP_NO_MEMORY);
+ (void) nt_sid_get_rid(sid, &grp.sg_rid);
+ free(sid);
+ grp.sg_domain = SMB_LGRP_BUILTIN;
+
+ grp.sg_privs = smb_privset_new();
+ smb_lgrp_set_default_privs(&grp);
+ }
+
+
+ grp.sg_attr = SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT |
+ SE_GROUP_ENABLED;
+
+ db = smb_lgrp_db_open(SMB_LGRP_DB_ORW);
+ rc = smb_lgrp_gtbl_insert(db, &grp);
+ smb_lgrp_db_close(db);
+
+ smb_privset_free(grp.sg_privs);
+ return (rc);
+}
+
+/*
+ * smb_lgrp_rename
+ *
+ * Renames the given group
+ */
+int
+smb_lgrp_rename(char *gname, char *new_gname)
+{
+ smb_group_t grp;
+ sqlite *db;
+ int rc;
+
+ (void) trim_whitespace(gname);
+ if (!smb_lgrp_chkname(gname))
+ return (SMB_LGRP_INVALID_NAME);
+
+ (void) trim_whitespace(new_gname);
+ if (!smb_lgrp_chkname(gname))
+ return (SMB_LGRP_INVALID_NAME);
+
+ if (utf8_strcasecmp(gname, new_gname) == 0)
+ return (SMB_LGRP_SUCCESS);
+
+ /* Cannot rename well-known groups */
+ if (nt_builtin_is_wellknown(gname))
+ return (SMB_LGRP_WKSID);
+
+ /* Cannot rename to a well-known groups */
+ if (nt_builtin_is_wellknown(new_gname))
+ return (SMB_LGRP_WKSID);
+
+ grp.sg_name = new_gname;
+
+ db = smb_lgrp_db_open(SMB_LGRP_DB_ORW);
+ rc = smb_lgrp_gtbl_update(db, gname, &grp, SMB_LGRP_GTBL_NAME);
+ smb_lgrp_db_close(db);
+
+ return (rc);
+}
+
+/*
+ * smb_lgrp_delete
+ *
+ * Deletes the specified local group.
+ */
+int
+smb_lgrp_delete(char *gname)
+{
+ sqlite *db;
+ int rc;
+
+ (void) trim_whitespace(gname);
+ if (!smb_lgrp_chkname(gname))
+ return (SMB_LGRP_INVALID_NAME);
+
+ /* Cannot remove a built-in group */
+ if (nt_builtin_is_wellknown(gname))
+ return (SMB_LGRP_WKSID);
+
+ db = smb_lgrp_db_open(SMB_LGRP_DB_ORW);
+ rc = smb_lgrp_gtbl_delete(db, gname);
+ smb_lgrp_db_close(db);
+
+ return (rc);
+}
+
+/*
+ * smb_lgrp_setcmnt
+ *
+ * Sets the description for the given group
+ */
+int
+smb_lgrp_setcmnt(char *gname, char *cmnt)
+{
+ smb_group_t grp;
+ sqlite *db;
+ int rc;
+
+ (void) trim_whitespace(gname);
+ if (!smb_lgrp_chkname(gname))
+ return (SMB_LGRP_INVALID_NAME);
+
+ if (cmnt && (strlen(cmnt) > SMB_LGRP_COMMENT_MAX))
+ return (SMB_LGRP_INVALID_ARG);
+
+ grp.sg_cmnt = cmnt;
+
+ db = smb_lgrp_db_open(SMB_LGRP_DB_ORW);
+ rc = smb_lgrp_gtbl_update(db, gname, &grp, SMB_LGRP_GTBL_CMNT);
+ smb_lgrp_db_close(db);
+
+ return (rc);
+}
+
+/*
+ * smb_lgrp_getcmnt
+ *
+ * Obtain the description of the specified group
+ */
+int
+smb_lgrp_getcmnt(char *gname, char **cmnt)
+{
+ smb_group_t grp;
+ sqlite *db;
+ int rc;
+
+ (void) trim_whitespace(gname);
+ if (!smb_lgrp_chkname(gname))
+ return (SMB_LGRP_INVALID_NAME);
+
+ if (cmnt == NULL)
+ return (SMB_LGRP_INVALID_ARG);
+
+ db = smb_lgrp_db_open(SMB_LGRP_DB_ORD);
+ rc = smb_lgrp_gtbl_lookup(db, SMB_LGRP_GTBL_NAME, &grp,
+ SMB_LGRP_INFO_CMNT, gname);
+ smb_lgrp_db_close(db);
+
+ if (rc == SMB_LGRP_SUCCESS) {
+ *cmnt = grp.sg_cmnt;
+ grp.sg_cmnt = NULL;
+ smb_lgrp_free(&grp);
+ }
+
+ return (rc);
+}
+
+
+/*
+ * smb_lgrp_setpriv
+ *
+ * Enable/disable the specified privilge for the group
+ */
+int
+smb_lgrp_setpriv(char *gname, uint8_t priv_lid, boolean_t enable)
+{
+ sqlite *db;
+ int rc;
+
+ (void) trim_whitespace(gname);
+ if (!smb_lgrp_chkname(gname))
+ return (SMB_LGRP_INVALID_NAME);
+
+ if ((priv_lid < SE_MIN_LUID) || (priv_lid > SE_MAX_LUID))
+ return (SMB_LGRP_NO_SUCH_PRIV);
+
+ db = smb_lgrp_db_open(SMB_LGRP_DB_ORW);
+ rc = smb_lgrp_gtbl_update_plist(db, gname, priv_lid, enable);
+ smb_lgrp_db_close(db);
+
+ if (enable) {
+ if (rc == SMB_LGRP_PRIV_HELD)
+ rc = SMB_LGRP_SUCCESS;
+ } else {
+ if (rc == SMB_LGRP_PRIV_NOT_HELD)
+ rc = SMB_LGRP_SUCCESS;
+ }
+
+ return (rc);
+}
+
+/*
+ * smb_lgrp_getpriv
+ *
+ * Obtain the status of the specified privilge for the group
+ */
+int
+smb_lgrp_getpriv(char *gname, uint8_t priv_lid, boolean_t *enable)
+{
+ sqlite *db;
+ smb_group_t grp;
+ int rc;
+
+ (void) trim_whitespace(gname);
+ if (!smb_lgrp_chkname(gname))
+ return (SMB_LGRP_INVALID_NAME);
+
+ if ((priv_lid < SE_MIN_LUID) || (priv_lid > SE_MAX_LUID))
+ return (SMB_LGRP_NO_SUCH_PRIV);
+
+ db = smb_lgrp_db_open(SMB_LGRP_DB_ORD);
+ rc = smb_lgrp_gtbl_lookup(db, SMB_LGRP_GTBL_NAME, &grp,
+ SMB_LGRP_INFO_PRIV, gname);
+ smb_lgrp_db_close(db);
+
+ if (rc == SMB_LGRP_SUCCESS) {
+ *enable = (smb_privset_query(grp.sg_privs, priv_lid) == 1);
+ smb_lgrp_free(&grp);
+ }
+
+ return (rc);
+}
+
+/*
+ * smb_lgrp_add_member
+ *
+ * Add the given account to the specified group as its member.
+ */
+int
+smb_lgrp_add_member(char *gname, nt_sid_t *msid, uint16_t sid_type)
+{
+ sqlite *db;
+ smb_gsid_t mid;
+ int rc;
+
+ (void) trim_whitespace(gname);
+ if (!smb_lgrp_chkname(gname))
+ return (SMB_LGRP_INVALID_NAME);
+
+ if (!nt_sid_is_valid(msid))
+ return (SMB_LGRP_INVALID_ARG);
+
+ if (!smb_lgrp_chkmember(sid_type))
+ return (SMB_LGRP_INVALID_MEMBER);
+
+ mid.gs_sid = msid;
+ mid.gs_type = sid_type;
+
+ db = smb_lgrp_db_open(SMB_LGRP_DB_ORW);
+ rc = smb_lgrp_gtbl_update_mlist(db, gname, &mid, SMB_LGRP_DB_ADDMEMBER);
+ smb_lgrp_db_close(db);
+
+ return (rc);
+}
+
+/*
+ * smb_lgrp_del_member
+ *
+ * Delete the specified member from the given group.
+ */
+int
+smb_lgrp_del_member(char *gname, nt_sid_t *msid, uint16_t sid_type)
+{
+ sqlite *db;
+ smb_gsid_t mid;
+ int rc;
+
+ (void) trim_whitespace(gname);
+ if (!smb_lgrp_chkname(gname))
+ return (SMB_LGRP_INVALID_NAME);
+
+ if (!nt_sid_is_valid(msid))
+ return (SMB_LGRP_INVALID_ARG);
+
+ mid.gs_sid = msid;
+ mid.gs_type = sid_type;
+
+ db = smb_lgrp_db_open(SMB_LGRP_DB_ORW);
+ rc = smb_lgrp_gtbl_update_mlist(db, gname, &mid, SMB_LGRP_DB_DELMEMBER);
+ smb_lgrp_db_close(db);
+
+ return (rc);
+}
+
+/*
+ * smb_lgrp_getbyname
+ *
+ * Retrieves the information of the group specified by
+ * the given name.
+ *
+ * Note that this function doesn't allocate the group
+ * structure itself only the fields, so the given grp
+ * pointer has to point to a group structure.
+ * Caller must free the allocated memories for the fields
+ * by calling smb_lgrp_free().
+ */
+int
+smb_lgrp_getbyname(char *gname, smb_group_t *grp)
+{
+ sqlite *db;
+ int rc;
+
+ (void) trim_whitespace(gname);
+ if (!smb_lgrp_chkname(gname))
+ return (SMB_LGRP_INVALID_NAME);
+
+ db = smb_lgrp_db_open(SMB_LGRP_DB_ORD);
+ rc = smb_lgrp_gtbl_lookup(db, SMB_LGRP_GTBL_NAME, grp,
+ SMB_LGRP_INFO_ALL, gname);
+ smb_lgrp_db_close(db);
+
+ return (rc);
+}
+
+/*
+ * smb_lgrp_getbyrid
+ *
+ * Retrieves the information of the group specified by
+ * the given RID and domain type.
+ *
+ * Note that this function doesn't allocate the group
+ * structure itself only the fields, so the given grp
+ * pointer has to point to a group structure.
+ * Caller must free the allocated memories for the fields
+ * by calling smb_lgrp_free().
+ *
+ * If grp is NULL no information would be returned. The
+ * return value of SMB_LGRP_SUCCESS will indicate that a
+ * group with the given information exists.
+ */
+int
+smb_lgrp_getbyrid(uint32_t rid, smb_gdomain_t domtype, smb_group_t *grp)
+{
+ smb_group_t tmpgrp;
+ sqlite *db;
+ int infolvl = SMB_LGRP_INFO_ALL;
+ int rc;
+
+ if (grp == NULL) {
+ grp = &tmpgrp;
+ infolvl = SMB_LGRP_INFO_NONE;
+ }
+
+ db = smb_lgrp_db_open(SMB_LGRP_DB_ORD);
+ rc = smb_lgrp_gtbl_lookup(db, SMB_LGRP_GTBL_SIDRID, grp, infolvl,
+ rid, domtype);
+ smb_lgrp_db_close(db);
+
+ return (rc);
+}
+
+/*
+ * smb_lgrp_numbydomain
+ *
+ * Returns the number of groups in the given domain in the
+ * arg 'count'
+ */
+int
+smb_lgrp_numbydomain(smb_gdomain_t dom_type, int *count)
+{
+ sqlite *db;
+ int dom_idx;
+ int rc;
+
+ switch (dom_type) {
+ case SMB_LGRP_LOCAL:
+ dom_idx = SMB_LGRP_LOCAL_IDX;
+ break;
+ case SMB_LGRP_BUILTIN:
+ dom_idx = SMB_LGRP_BUILTIN_IDX;
+ break;
+ default:
+ *count = 0;
+ return (SMB_LGRP_INVALID_ARG);
+ }
+
+ db = smb_lgrp_db_open(SMB_LGRP_DB_ORD);
+ rc = smb_lgrp_gtbl_count(db, dom_idx, count);
+ smb_lgrp_db_close(db);
+
+ return (rc);
+}
+
+/*
+ * smb_lgrp_numbydomain
+ *
+ * Returns the number of groups which have the given SID
+ * as a member.
+ */
+int
+smb_lgrp_numbymember(nt_sid_t *msid, int *count)
+{
+ smb_giter_t gi;
+ smb_group_t grp;
+ int rc;
+
+ *count = 0;
+ rc = smb_lgrp_iteropen(&gi);
+ if (rc != SMB_LGRP_SUCCESS)
+ return (rc);
+
+ while (smb_lgrp_iterate(&gi, &grp) == SMB_LGRP_SUCCESS) {
+ if (smb_lgrp_is_member(&grp, msid))
+ (*count)++;
+ smb_lgrp_free(&grp);
+ }
+ smb_lgrp_iterclose(&gi);
+ return (SMB_LGRP_SUCCESS);
+}
+
+/*
+ * smb_lgrp_free
+ *
+ * Frees the allocated memory for the fields of the given
+ * group structure. Note that this function doesn't free
+ * the group itself.
+ */
+void
+smb_lgrp_free(smb_group_t *grp)
+{
+ int i;
+
+ if (grp == NULL)
+ return;
+
+ free(grp->sg_name);
+ free(grp->sg_cmnt);
+ free(grp->sg_id.gs_sid);
+ smb_privset_free(grp->sg_privs);
+
+ for (i = 0; i < grp->sg_nmembers; i++)
+ free(grp->sg_members[i].gs_sid);
+ free(grp->sg_members);
+}
+
+/*
+ * smb_lgrp_iteropen
+ *
+ * Initializes the given group iterator by opening
+ * the group database and creating a virtual machine
+ * for iteration.
+ */
+int
+smb_lgrp_iteropen(smb_giter_t *iter)
+{
+ char *sql;
+ char *errmsg = NULL;
+ int rc = SMB_LGRP_SUCCESS;
+
+ assert(iter);
+
+ bzero(iter, sizeof (smb_giter_t));
+
+ sql = sqlite_mprintf("SELECT * FROM groups");
+ if (sql == NULL)
+ return (SMB_LGRP_NO_MEMORY);
+
+ iter->sgi_db = smb_lgrp_db_open(SMB_LGRP_DB_ORD);
+ if (iter->sgi_db == NULL) {
+ sqlite_freemem(sql);
+ return (SMB_LGRP_DBOPEN_FAILED);
+ }
+
+ rc = sqlite_compile(iter->sgi_db, sql, NULL, &iter->sgi_vm, &errmsg);
+ sqlite_freemem(sql);
+
+ if (rc != SQLITE_OK) {
+ syslog(LOG_DEBUG, "failed to create a VM (%s)",
+ NULL_MSGCHK(errmsg));
+ rc = SMB_LGRP_DB_ERROR;
+ }
+
+ return (rc);
+}
+
+/*
+ * smb_lgrp_iterclose
+ *
+ * Closes the given group iterator.
+ */
+void
+smb_lgrp_iterclose(smb_giter_t *iter)
+{
+ char *errmsg = NULL;
+ int rc;
+
+ assert(iter);
+
+ rc = sqlite_finalize(iter->sgi_vm, &errmsg);
+ if (rc != SQLITE_OK) {
+ syslog(LOG_DEBUG, "failed to destroy a VM (%s)",
+ NULL_MSGCHK(errmsg));
+ }
+
+ smb_lgrp_db_close(iter->sgi_db);
+}
+
+/*
+ * smb_lgrp_iterate
+ *
+ * Iterate through group database
+ * Group information is returned in provided group structure.
+ *
+ * Note that this function doesn't allocate the group
+ * structure itself only the fields, so the given grp
+ * pointer has to point to a group structure.
+ * Caller must free the allocated memories for the fields
+ * by calling smb_lgrp_free().
+ */
+int
+smb_lgrp_iterate(smb_giter_t *iter, smb_group_t *grp)
+{
+ const char **values;
+ int ncol;
+ int rc;
+ int i;
+
+ if (iter->sgi_vm == NULL || iter->sgi_db == NULL)
+ return (SMB_LGRP_INVALID_ARG);
+
+ bzero(grp, sizeof (smb_group_t));
+ rc = sqlite_step(iter->sgi_vm, &ncol, &values, NULL);
+ if (rc == SQLITE_DONE)
+ return (SMB_LGRP_NO_MORE);
+
+ if (rc != SQLITE_ROW)
+ return (SMB_LGRP_DBEXEC_FAILED);
+
+ if (ncol != SMB_LGRP_GTBL_NCOL)
+ return (SMB_LGRP_DB_ERROR);
+
+ for (i = 0; i < ncol; i++) {
+ if (values[i] == NULL)
+ return (SMB_LGRP_DB_ERROR);
+ }
+
+ return (smb_lgrp_decode(grp, (char **)values, SMB_LGRP_INFO_ALL,
+ iter->sgi_db));
+}
+
+/*
+ * smb_lgrp_is_member
+ *
+ * Check to see if the specified account is a member of
+ * the given group.
+ */
+boolean_t
+smb_lgrp_is_member(smb_group_t *grp, nt_sid_t *sid)
+{
+ int i;
+
+ if (grp == NULL || grp->sg_members == NULL || sid == NULL)
+ return (B_FALSE);
+
+ for (i = 0; i < grp->sg_nmembers; i++) {
+ if (nt_sid_is_equal(grp->sg_members[i].gs_sid, sid))
+ return (B_TRUE);
+ }
+
+ return (B_FALSE);
+}
+
+/*
+ * smb_lgrp_strerror
+ *
+ * Returns a text for the given group error code.
+ */
+char *
+smb_lgrp_strerror(int errno)
+{
+ switch (errno) {
+ case SMB_LGRP_SUCCESS:
+ return (dgettext(TEXT_DOMAIN, "success"));
+ case SMB_LGRP_INVALID_ARG:
+ return (dgettext(TEXT_DOMAIN, "invalid argument"));
+ case SMB_LGRP_INVALID_MEMBER:
+ return (dgettext(TEXT_DOMAIN, "invalid member type"));
+ case SMB_LGRP_INVALID_NAME:
+ return (dgettext(TEXT_DOMAIN, "invalid name"));
+ case SMB_LGRP_NOT_FOUND:
+ return (dgettext(TEXT_DOMAIN, "group not found"));
+ case SMB_LGRP_EXISTS:
+ return (dgettext(TEXT_DOMAIN, "group exists"));
+ case SMB_LGRP_NO_SID:
+ return (dgettext(TEXT_DOMAIN, "cannot obtain a SID"));
+ case SMB_LGRP_NO_LOCAL_SID:
+ return (dgettext(TEXT_DOMAIN, "cannot get the machine SID"));
+ case SMB_LGRP_SID_NOTLOCAL:
+ return (dgettext(TEXT_DOMAIN, "not a local SID"));
+ case SMB_LGRP_WKSID:
+ return (dgettext(TEXT_DOMAIN,
+ "operation not permitted on well-known accounts"));
+ case SMB_LGRP_NO_MEMORY:
+ return (dgettext(TEXT_DOMAIN, "not enough memory"));
+ case SMB_LGRP_DB_ERROR:
+ return (dgettext(TEXT_DOMAIN, "database operation error"));
+ case SMB_LGRP_DBINIT_ERROR:
+ return (dgettext(TEXT_DOMAIN, "database initialization error"));
+ case SMB_LGRP_INTERNAL_ERROR:
+ return (dgettext(TEXT_DOMAIN, "internal error"));
+ case SMB_LGRP_MEMBER_IN_GROUP:
+ return (dgettext(TEXT_DOMAIN, "member already in the group"));
+ case SMB_LGRP_MEMBER_NOT_IN_GROUP:
+ return (dgettext(TEXT_DOMAIN, "not a member"));
+ case SMB_LGRP_NO_SUCH_PRIV:
+ return (dgettext(TEXT_DOMAIN, "no such privilege"));
+ case SMB_LGRP_NO_SUCH_DOMAIN:
+ return (dgettext(TEXT_DOMAIN, "no such domain SID"));
+ case SMB_LGRP_PRIV_HELD:
+ return (dgettext(TEXT_DOMAIN, "already holds the privilege"));
+ case SMB_LGRP_PRIV_NOT_HELD:
+ return (dgettext(TEXT_DOMAIN, "privilege not held"));
+ case SMB_LGRP_BAD_DATA:
+ return (dgettext(TEXT_DOMAIN, "bad data"));
+ case SMB_LGRP_NO_MORE:
+ return (dgettext(TEXT_DOMAIN, "no more groups"));
+ case SMB_LGRP_DBOPEN_FAILED:
+ return (dgettext(TEXT_DOMAIN, "failed openning database"));
+ case SMB_LGRP_DBEXEC_FAILED:
+ return (dgettext(TEXT_DOMAIN,
+ "failed executing database operation"));
+ case SMB_LGRP_DBINIT_FAILED:
+ return (dgettext(TEXT_DOMAIN, "failed initializing database"));
+ case SMB_LGRP_DOMLKP_FAILED:
+ return (dgettext(TEXT_DOMAIN, "failed getting the domain SID"));
+ case SMB_LGRP_DOMINS_FAILED:
+ return (dgettext(TEXT_DOMAIN,
+ "failed inserting the domain SID"));
+ case SMB_LGRP_INSERT_FAILED:
+ return (dgettext(TEXT_DOMAIN, "failed inserting the group"));
+ case SMB_LGRP_DELETE_FAILED:
+ return (dgettext(TEXT_DOMAIN, "failed deleting the group"));
+ case SMB_LGRP_UPDATE_FAILED:
+ return (dgettext(TEXT_DOMAIN, "failed updating the group"));
+ case SMB_LGRP_LOOKUP_FAILED:
+ return (dgettext(TEXT_DOMAIN, "failed looking up the group"));
+ }
+
+ return (dgettext(TEXT_DOMAIN, "unknown error code"));
+}
+
+/*
+ * smb_lgrp_chkmember
+ *
+ * Determines valid account types for being member of
+ * a local group.
+ *
+ * Currently, we just support users as valid members.
+ */
+static boolean_t
+smb_lgrp_chkmember(uint16_t sid_type)
+{
+ return (sid_type == SidTypeUser);
+}
+
+/*
+ * smb_lgrp_start
+ *
+ * Initializes the library private global variables.
+ * If the database doesn't exist, it'll create it and adds the
+ * predefined builtin groups.
+ */
+int
+smb_lgrp_start(void)
+{
+ char *supported_bg[] =
+ {"Administrators", "Backup Operators", "Power Users"};
+ well_known_account_t *wka;
+ int rc, i, ngrp;
+ char *lsid_str;
+
+ (void) mutex_init(&smb_lgrp_lsid_mtx, USYNC_THREAD, NULL);
+ (void) mutex_lock(&smb_lgrp_lsid_mtx);
+ lsid_str = smb_config_get_localsid();
+ if (lsid_str == NULL) {
+ (void) mutex_unlock(&smb_lgrp_lsid_mtx);
+ return (SMB_LGRP_NO_LOCAL_SID);
+ }
+
+ smb_lgrp_lsid = nt_sid_strtosid(lsid_str);
+ free(lsid_str);
+ if (!nt_sid_is_valid(smb_lgrp_lsid)) {
+ free(smb_lgrp_lsid);
+ smb_lgrp_lsid = NULL;
+ (void) mutex_unlock(&smb_lgrp_lsid_mtx);
+ return (SMB_LGRP_NO_LOCAL_SID);
+ }
+
+ rc = smb_lgrp_db_init();
+ if (rc != SMB_LGRP_SUCCESS) {
+ free(smb_lgrp_lsid);
+ smb_lgrp_lsid = NULL;
+ (void) mutex_unlock(&smb_lgrp_lsid_mtx);
+ return (rc);
+ }
+ (void) mutex_unlock(&smb_lgrp_lsid_mtx);
+
+ ngrp = sizeof (supported_bg) / sizeof (supported_bg[0]);
+ for (i = 0; i < ngrp; i++) {
+ wka = nt_builtin_lookup(supported_bg[i]);
+ if (wka == NULL)
+ continue;
+ rc = smb_lgrp_add(wka->name, wka->desc);
+ if (rc != SMB_LGRP_SUCCESS)
+ syslog(LOG_DEBUG, "failed to add %s", wka->name);
+ }
+
+ return (SMB_LGRP_SUCCESS);
+}
+
+/*
+ * smb_lgrp_stop
+ *
+ * Unintialize the library global private variables.
+ */
+void
+smb_lgrp_stop(void)
+{
+ (void) mutex_lock(&smb_lgrp_lsid_mtx);
+ free(smb_lgrp_lsid);
+ smb_lgrp_lsid = NULL;
+ (void) mutex_unlock(&smb_lgrp_lsid_mtx);
+ (void) mutex_destroy(&smb_lgrp_lsid_mtx);
+}
+
+/*
+ * smb_lgrp_db_open
+ *
+ * Opens group database with the given mode.
+ */
+static sqlite *
+smb_lgrp_db_open(int mode)
+{
+ sqlite *db;
+ char *errmsg = NULL;
+
+ db = sqlite_open(SMB_LGRP_DB_NAME, mode, &errmsg);
+ if (db == NULL) {
+ syslog(LOG_ERR, "failed to open group database (%s)",
+ NULL_MSGCHK(errmsg));
+ sqlite_freemem(errmsg);
+ }
+
+ return (db);
+}
+
+/*
+ * smb_lgrp_db_close
+ *
+ * Closes the given database handle
+ */
+static void
+smb_lgrp_db_close(sqlite *db)
+{
+ if (db) {
+ sqlite_close(db);
+ }
+}
+
+/*
+ * smb_lgrp_db_init
+ *
+ * Creates the group database based on the defined SQL statement.
+ * It also initializes db_info and domain tables.
+ */
+static int
+smb_lgrp_db_init(void)
+{
+ int dbrc = SQLITE_OK;
+ int rc = SMB_LGRP_SUCCESS;
+ sqlite *db = NULL;
+ char *errmsg = NULL;
+
+ db = sqlite_open(SMB_LGRP_DB_NAME, 0600, &errmsg);
+ if (db == NULL) {
+ syslog(LOG_ERR, "failed to create group database (%s)",
+ NULL_MSGCHK(errmsg));
+ sqlite_freemem(errmsg);
+ return (SMB_LGRP_DBOPEN_FAILED);
+ }
+
+ sqlite_busy_timeout(db, SMB_LGRP_DB_TIMEOUT);
+ dbrc = sqlite_exec(db, "BEGIN TRANSACTION;", NULL, NULL, &errmsg);
+ if (dbrc != SQLITE_OK) {
+ syslog(LOG_DEBUG, "failed to begin database transaction (%s)",
+ NULL_MSGCHK(errmsg));
+ sqlite_freemem(errmsg);
+ sqlite_close(db);
+ return (SMB_LGRP_DBEXEC_FAILED);
+ }
+
+ switch (sqlite_exec(db, SMB_LGRP_DB_SQL, NULL, NULL, &errmsg)) {
+ case SQLITE_ERROR:
+ /*
+ * This is the normal situation: CREATE probably failed because
+ * tables already exist. It may indicate an error in SQL as well
+ * but we cannot tell.
+ */
+ sqlite_freemem(errmsg);
+ dbrc = sqlite_exec(db, "ROLLBACK TRANSACTION", NULL, NULL,
+ &errmsg);
+ rc = SMB_LGRP_SUCCESS;
+ break;
+
+ case SQLITE_OK:
+ dbrc = sqlite_exec(db, "COMMIT TRANSACTION", NULL, NULL,
+ &errmsg);
+ if (dbrc != SQLITE_OK)
+ break;
+ rc = smb_lgrp_dtbl_insert(db, NT_BUILTIN_DOMAIN_SIDSTR,
+ NULL);
+ if (rc == SMB_LGRP_SUCCESS)
+ rc = smb_lgrp_db_setinfo(db);
+ if (rc != SMB_LGRP_SUCCESS) {
+ (void) sqlite_close(db);
+ (void) unlink(SMB_LGRP_DB_NAME);
+ return (rc);
+ }
+ break;
+
+ default:
+ syslog(LOG_ERR,
+ "failed to initialize group database (%s)", errmsg);
+ sqlite_freemem(errmsg);
+ dbrc = sqlite_exec(db, "ROLLBACK TRANSACTION", NULL, NULL,
+ &errmsg);
+ rc = SMB_LGRP_DBINIT_FAILED;
+ break;
+ }
+
+ if (dbrc != SQLITE_OK) {
+ /* this is bad - database may be left in a locked state */
+ syslog(LOG_DEBUG, "failed to close a transaction (%s)",
+ NULL_MSGCHK(errmsg));
+ sqlite_freemem(errmsg);
+ }
+
+ (void) sqlite_close(db);
+ return (rc);
+}
+
+/*
+ * smb_lgrp_gtbl_lookup
+ *
+ * This is a flexible lookup function for the group database.
+ * The key type can be specified by the 'key' arg and the actual key
+ * values can be passed after the 'infolvl' arg. 'infolvl' arg specifies
+ * what information items for the specified group is needed.
+ *
+ * Note that the function assumes the given key is unique and only
+ * specifies one or 0 group. The keys that are supported now are
+ * the group name and the group SID
+ *
+ * Note that this function doesn't allocate the group
+ * structure itself only the fields, so the given grp
+ * pointer has to point to a group structure.
+ * Caller must free the allocated memories for the fields
+ * by calling smb_lgrp_free().
+ */
+static int
+smb_lgrp_gtbl_lookup(sqlite *db, int key, smb_group_t *grp, int infolvl, ...)
+{
+ char *errmsg = NULL;
+ char *sql;
+ char **result;
+ int nrow, ncol;
+ int rc, dom_idx;
+ smb_group_t kgrp;
+ va_list ap;
+
+ if (db == NULL)
+ return (SMB_LGRP_DBOPEN_FAILED);
+
+ bzero(grp, sizeof (smb_group_t));
+ va_start(ap, infolvl);
+
+ switch (key) {
+ case SMB_LGRP_GTBL_NAME:
+ kgrp.sg_name = va_arg(ap, char *);
+ sql = sqlite_mprintf("SELECT * FROM groups WHERE name = '%s'",
+ kgrp.sg_name);
+ break;
+
+ case SMB_LGRP_GTBL_SIDRID:
+ kgrp.sg_rid = va_arg(ap, uint32_t);
+ kgrp.sg_domain = va_arg(ap, smb_gdomain_t);
+ dom_idx = (kgrp.sg_domain == SMB_LGRP_LOCAL)
+ ? SMB_LGRP_LOCAL_IDX : SMB_LGRP_BUILTIN_IDX;
+ sql = sqlite_mprintf("SELECT * FROM groups"
+ "WHERE (sid_idx = %d) AND (sid_rid = %u)",
+ dom_idx, kgrp.sg_rid);
+ break;
+
+ default:
+ va_end(ap);
+ return (SMB_LGRP_INVALID_ARG);
+ }
+
+ va_end(ap);
+ if (sql == NULL)
+ return (SMB_LGRP_NO_MEMORY);
+
+ rc = sqlite_get_table(db, sql, &result, &nrow, &ncol, &errmsg);
+ sqlite_freemem(sql);
+
+ if (rc != SQLITE_OK) {
+ syslog(LOG_DEBUG, "failed to lookup (%s)", NULL_MSGCHK(errmsg));
+ sqlite_freemem(errmsg);
+ return (SMB_LGRP_LOOKUP_FAILED);
+ }
+
+ if (nrow == 0) {
+ /* group not found */
+ sqlite_free_table(result);
+ return (SMB_LGRP_NOT_FOUND);
+ }
+
+ if (nrow != 1 || ncol != SMB_LGRP_GTBL_NCOL) {
+ sqlite_free_table(result);
+ return (SMB_LGRP_DB_ERROR);
+ }
+
+ rc = smb_lgrp_decode(grp, &result[SMB_LGRP_GTBL_NCOL], infolvl, db);
+ sqlite_free_table(result);
+ return (rc);
+}
+
+/*
+ * smb_lgrp_gtbl_exists
+ *
+ * Checks to see if the given group exists or not.
+ */
+static boolean_t
+smb_lgrp_gtbl_exists(sqlite *db, char *gname)
+{
+ char *errmsg = NULL;
+ char *sql;
+ char **result;
+ int nrow, ncol;
+ int rc;
+
+ if (db == NULL)
+ return (NULL);
+
+ sql = sqlite_mprintf("SELECT name FROM groups WHERE name = '%s'",
+ gname);
+ rc = sqlite_get_table(db, sql, &result, &nrow, &ncol, &errmsg);
+ sqlite_freemem(sql);
+
+ if (rc != SQLITE_OK) {
+ syslog(LOG_DEBUG, "failed to lookup %s (%s)",
+ gname, NULL_MSGCHK(errmsg));
+ sqlite_freemem(errmsg);
+ return (B_FALSE);
+ }
+
+ sqlite_free_table(result);
+ return (nrow != 0);
+}
+
+/*
+ * smb_lgrp_gtbl_count
+ *
+ * Counts the number of groups in the domain specified by
+ * 'dom_idx'
+ */
+static int
+smb_lgrp_gtbl_count(sqlite *db, int dom_idx, int *count)
+{
+ char *errmsg = NULL;
+ char *sql;
+ char **result;
+ int nrow, ncol;
+ int rc;
+
+ *count = 0;
+ if (db == NULL)
+ return (SMB_LGRP_DBOPEN_FAILED);
+
+ sql = sqlite_mprintf("SELECT sid_idx FROM groups WHERE sid_idx = %d",
+ dom_idx);
+ rc = sqlite_get_table(db, sql, &result, &nrow, &ncol, &errmsg);
+ sqlite_freemem(sql);
+
+ if (rc != SQLITE_OK) {
+ syslog(LOG_DEBUG, "failed to count (%s)", NULL_MSGCHK(errmsg));
+ sqlite_freemem(errmsg);
+ return (SMB_LGRP_LOOKUP_FAILED);
+ }
+
+ sqlite_free_table(result);
+ if (ncol != 1)
+ return (SMB_LGRP_DB_ERROR);
+
+ *count = nrow;
+ return (SMB_LGRP_SUCCESS);
+}
+
+/*
+ * smb_lgrp_gtbl_insert
+ *
+ * Insert a record for the given group in the group database.
+ *
+ * NOTE: this function assumes that this group has no members
+ * at this time.
+ */
+static int
+smb_lgrp_gtbl_insert(sqlite *db, smb_group_t *grp)
+{
+ smb_lgpid_t privs[SE_MAX_LUID + 1];
+ smb_lgplist_t plist;
+ char *errmsg = NULL;
+ char *sql;
+ int dom_idx;
+ int rc;
+
+ if (db == NULL)
+ return (SMB_LGRP_DBOPEN_FAILED);
+
+ dom_idx = (grp->sg_domain == SMB_LGRP_LOCAL)
+ ? SMB_LGRP_LOCAL_IDX : SMB_LGRP_BUILTIN_IDX;
+
+ plist.p_cnt = SE_MAX_LUID;
+ plist.p_ids = privs;
+ smb_lgrp_encode_privset(grp, &plist);
+
+ sql = sqlite_mprintf("INSERT INTO groups"
+ "(name, sid_idx, sid_rid, sid_type, sid_attrs, comment, "
+ "n_privs, privs, n_members, members) "
+ "VALUES('%s', %u, %u, %u, %u, '%q', %u, '%q', %u, '%q')",
+ grp->sg_name, dom_idx, grp->sg_rid, grp->sg_id.gs_type,
+ grp->sg_attr, (grp->sg_cmnt) ? grp->sg_cmnt : "",
+ plist.p_cnt, (char *)plist.p_ids, 0, "");
+
+ if (sql == NULL)
+ return (SMB_LGRP_NO_MEMORY);
+
+ rc = sqlite_exec(db, sql, NULL, NULL, &errmsg);
+ sqlite_freemem(sql);
+
+ if (rc != SQLITE_OK) {
+ syslog(LOG_DEBUG, "failed to insert %s (%s)",
+ grp->sg_name, NULL_MSGCHK(errmsg));
+ sqlite_freemem(errmsg);
+ rc = SMB_LGRP_INSERT_FAILED;
+ } else {
+ rc = SMB_LGRP_SUCCESS;
+ }
+
+ return (rc);
+}
+
+/*
+ * smb_lgrp_gtbl_delete
+ *
+ * Removes the specified group from the database
+ */
+static int
+smb_lgrp_gtbl_delete(sqlite *db, char *gname)
+{
+ char *errmsg = NULL;
+ char *sql;
+ int rc;
+
+ if (db == NULL)
+ return (SMB_LGRP_DBOPEN_FAILED);
+
+ sql = sqlite_mprintf("DELETE FROM groups WHERE name = '%s'", gname);
+ if (sql == NULL)
+ return (SMB_LGRP_NO_MEMORY);
+
+ rc = sqlite_exec(db, sql, NULL, NULL, &errmsg);
+ sqlite_freemem(sql);
+
+ if (rc != SQLITE_OK) {
+ syslog(LOG_DEBUG, "failed to delete %s (%s)",
+ gname, NULL_MSGCHK(errmsg));
+ sqlite_freemem(errmsg);
+ rc = SMB_LGRP_DELETE_FAILED;
+ } else {
+ rc = SMB_LGRP_SUCCESS;
+ }
+
+ return (rc);
+}
+
+/*
+ * smb_lgrp_gtbl_update
+ *
+ * Updates the specified group information, the supported items
+ * are group name and comment
+ */
+static int
+smb_lgrp_gtbl_update(sqlite *db, char *gname, smb_group_t *grp, int col_id)
+{
+ char *errmsg = NULL;
+ char *sql;
+ int rc;
+
+ if (db == NULL)
+ return (SMB_LGRP_DBOPEN_FAILED);
+
+ /* UPDATE doesn't fail if gname doesn't exist */
+ if (!smb_lgrp_gtbl_exists(db, gname))
+ return (SMB_LGRP_NOT_FOUND);
+
+ switch (col_id) {
+ case SMB_LGRP_GTBL_NAME:
+ if (smb_lgrp_gtbl_exists(db, grp->sg_name))
+ return (SMB_LGRP_EXISTS);
+ sql = sqlite_mprintf("UPDATE groups SET name = '%s' "
+ "WHERE name = '%s'", grp->sg_name, gname);
+ break;
+
+ case SMB_LGRP_GTBL_CMNT:
+ sql = sqlite_mprintf("UPDATE groups SET comment = '%q' "
+ "WHERE name = '%s'", grp->sg_cmnt, gname);
+ break;
+
+ default:
+ return (SMB_LGRP_INVALID_ARG);
+ }
+
+ if (sql == NULL)
+ return (SMB_LGRP_NO_MEMORY);
+
+ rc = sqlite_exec(db, sql, NULL, NULL, &errmsg);
+ sqlite_freemem(sql);
+
+ if (rc != SQLITE_OK) {
+ syslog(LOG_DEBUG, "failed to update %s (%s)",
+ gname, NULL_MSGCHK(errmsg));
+ sqlite_freemem(errmsg);
+ rc = SMB_LGRP_UPDATE_FAILED;
+ } else {
+ rc = SMB_LGRP_SUCCESS;
+ }
+
+ return (rc);
+}
+
+/*
+ * smb_lgrp_gtbl_update_mlist
+ *
+ * Adds/removes the specified member from the member list of the
+ * given group
+ */
+static int
+smb_lgrp_gtbl_update_mlist(sqlite *db, char *gname, smb_gsid_t *member,
+ int flags)
+{
+ smb_lgmlist_t new_members;
+ smb_lgmlist_t members;
+ smb_lgmid_t mid;
+ char *errmsg = NULL;
+ char *sql;
+ char **result;
+ int nrow, ncol;
+ int rc;
+
+ if (db == NULL)
+ return (SMB_LGRP_DBOPEN_FAILED);
+
+ sql = sqlite_mprintf("SELECT n_members, members FROM groups "
+ "WHERE name = '%s'", gname);
+
+ if (sql == NULL)
+ return (SMB_LGRP_NO_MEMORY);
+
+ rc = sqlite_get_table(db, sql, &result, &nrow, &ncol, &errmsg);
+ sqlite_freemem(sql);
+
+ if (rc != SQLITE_OK) {
+ syslog(LOG_DEBUG, "failed to lookup %s (%s)",
+ gname, NULL_MSGCHK(errmsg));
+ sqlite_freemem(errmsg);
+ return (SMB_LGRP_LOOKUP_FAILED);
+ }
+
+ if (nrow == 0) {
+ /* group not found */
+ sqlite_free_table(result);
+ return (SMB_LGRP_NOT_FOUND);
+ }
+
+ if (nrow != 1 || ncol != 2) {
+ sqlite_free_table(result);
+ return (SMB_LGRP_DB_ERROR);
+ }
+
+ bzero(&mid, sizeof (mid));
+ mid.m_type = member->gs_type;
+ rc = smb_lgrp_dtbl_getidx(db, member->gs_sid, mid.m_type,
+ &mid.m_idx, &mid.m_rid);
+ if (rc != SMB_LGRP_SUCCESS) {
+ sqlite_free_table(result);
+ return (rc);
+ }
+
+ members.m_cnt = atoi(result[2]);
+ members.m_ids = result[3];
+
+ switch (flags) {
+ case SMB_LGRP_DB_ADDMEMBER:
+ rc = smb_lgrp_mlist_add(&members, &mid, &new_members);
+ break;
+ case SMB_LGRP_DB_DELMEMBER:
+ rc = smb_lgrp_mlist_del(&members, &mid, &new_members);
+ break;
+ default:
+ rc = SMB_LGRP_INVALID_ARG;
+ }
+
+ sqlite_free_table(result);
+ if (rc != SMB_LGRP_SUCCESS)
+ return (rc);
+
+ sql = sqlite_mprintf("UPDATE groups SET n_members = %u, members = '%s'"
+ " WHERE name = '%s'", new_members.m_cnt, new_members.m_ids, gname);
+
+ free(new_members.m_ids);
+
+ if (sql == NULL)
+ return (SMB_LGRP_NO_MEMORY);
+
+ rc = sqlite_exec(db, sql, NULL, NULL, &errmsg);
+ sqlite_freemem(sql);
+
+ if (rc != SQLITE_OK) {
+ syslog(LOG_DEBUG, "failed to update %s (%s)", gname,
+ NULL_MSGCHK(errmsg));
+ sqlite_freemem(errmsg);
+ rc = SMB_LGRP_UPDATE_FAILED;
+ } else {
+ rc = SMB_LGRP_SUCCESS;
+ }
+
+ return (rc);
+}
+
+/*
+ * smb_lgrp_gtbl_update_plist
+ *
+ * Adds/removes the specified privilege from the privilege list of the
+ * given group
+ */
+static int
+smb_lgrp_gtbl_update_plist(sqlite *db, char *gname, uint8_t priv_id,
+ boolean_t enable)
+{
+ char *sql;
+ char *errmsg = NULL;
+ char **result;
+ int nrow, ncol;
+ int rc;
+ smb_lgplist_t privs;
+ smb_lgplist_t new_privs;
+
+ if (db == NULL)
+ return (SMB_LGRP_DBOPEN_FAILED);
+
+ sql = sqlite_mprintf("SELECT n_privs, privs FROM groups "
+ "WHERE name = '%s'", gname);
+
+ if (sql == NULL)
+ return (SMB_LGRP_NO_MEMORY);
+
+ rc = sqlite_get_table(db, sql, &result, &nrow, &ncol, &errmsg);
+ sqlite_freemem(sql);
+
+ if (rc != SQLITE_OK) {
+ syslog(LOG_DEBUG, "failed to lookup %s (%s)",
+ gname, NULL_MSGCHK(errmsg));
+ sqlite_freemem(errmsg);
+ return (SMB_LGRP_LOOKUP_FAILED);
+ }
+
+ if (nrow == 0) {
+ /* group not found */
+ sqlite_free_table(result);
+ return (SMB_LGRP_NOT_FOUND);
+ }
+
+ if (nrow != 1 || ncol != 2) {
+ sqlite_free_table(result);
+ return (SMB_LGRP_DB_ERROR);
+ }
+
+ privs.p_cnt = atoi(result[2]);
+ privs.p_ids = (smb_lgpid_t *)result[3];
+
+ if (enable)
+ rc = smb_lgrp_plist_add(&privs, priv_id, &new_privs);
+ else
+ rc = smb_lgrp_plist_del(&privs, priv_id, &new_privs);
+
+ sqlite_free_table(result);
+ if (rc != SMB_LGRP_SUCCESS)
+ return (rc);
+
+ sql = sqlite_mprintf("UPDATE groups SET n_privs = %u, privs = '%q'"
+ " WHERE name = '%s'", new_privs.p_cnt, (char *)new_privs.p_ids,
+ gname);
+
+ free(new_privs.p_ids);
+
+ if (sql == NULL)
+ return (SMB_LGRP_NO_MEMORY);
+
+ rc = sqlite_exec(db, sql, NULL, NULL, &errmsg);
+ sqlite_freemem(sql);
+
+ if (rc != SQLITE_OK) {
+ syslog(LOG_DEBUG, "failed to update %s (%s)",
+ gname, NULL_MSGCHK(errmsg));
+ sqlite_freemem(errmsg);
+ rc = SMB_LGRP_UPDATE_FAILED;
+ } else {
+ rc = SMB_LGRP_SUCCESS;
+ }
+
+ return (rc);
+}
+
+/*
+ * smb_lgrp_dtbl_insert
+ *
+ * Inserts the specified domain SID in the dmain table.
+ * Upon successful insert the index will be returned in
+ * 'dom_idx' arg.
+ */
+static int
+smb_lgrp_dtbl_insert(sqlite *db, char *dom_sid, uint32_t *dom_idx)
+{
+ char *errmsg = NULL;
+ char *sql;
+ int rc;
+
+ sql = sqlite_mprintf("INSERT INTO domains (dom_sid, dom_cnt)"
+ " VALUES('%s', 1);", dom_sid);
+ if (sql == NULL)
+ return (SMB_LGRP_NO_MEMORY);
+
+ rc = sqlite_exec(db, sql, NULL, NULL, &errmsg);
+ sqlite_freemem(sql);
+
+ if (rc != SQLITE_OK) {
+ syslog(LOG_DEBUG, "failed to insert domain SID (%s)",
+ NULL_MSGCHK(errmsg));
+ sqlite_freemem(errmsg);
+ return (SMB_LGRP_DOMINS_FAILED);
+ }
+
+ if (dom_idx)
+ *dom_idx = sqlite_last_insert_rowid(db);
+ return (SMB_LGRP_SUCCESS);
+}
+
+/*
+ * smb_lgrp_dtbl_getidx
+ *
+ * Searches the domain table for the domain SID of the
+ * given member SID. If it finds the domain SID it'll
+ * return the index and the RID, otherwise it'll insert
+ * it in the domain table as a new SID.
+ */
+static int
+smb_lgrp_dtbl_getidx(sqlite *db, nt_sid_t *sid, uint16_t sid_type,
+ uint32_t *dom_idx, uint32_t *rid)
+{
+ char sidstr[NT_SID_FMTBUF_SIZE];
+ nt_sid_t *dom_sid;
+ char **result;
+ int nrow, ncol;
+ char *errmsg = NULL;
+ char *sql;
+ int rc;
+
+ if (nt_sid_is_indomain(smb_lgrp_lsid, sid)) {
+ /* This is a local SID */
+ int id_type = (sid_type == SidTypeUser)
+ ? SMB_IDMAP_USER : SMB_IDMAP_GROUP;
+ *dom_idx = SMB_LGRP_LOCAL_IDX;
+ if (smb_idmap_getid(sid, rid, &id_type) != IDMAP_SUCCESS)
+ return (SMB_LGRP_INTERNAL_ERROR);
+ }
+
+ dom_sid = nt_sid_dup(sid);
+ if (dom_sid == NULL)
+ return (SMB_LGRP_NO_MEMORY);
+
+ (void) nt_sid_split(dom_sid, rid);
+ nt_sid_format2(dom_sid, sidstr);
+ free(dom_sid);
+
+ sql = sqlite_mprintf("SELECT dom_idx FROM domains WHERE dom_sid = '%s'",
+ sidstr);
+ if (sql == NULL)
+ return (SMB_LGRP_NO_MEMORY);
+
+ rc = sqlite_get_table(db, sql, &result, &nrow, &ncol, &errmsg);
+ sqlite_freemem(sql);
+
+ if (rc != SQLITE_OK) {
+ syslog(LOG_DEBUG, "failed to lookup domain SID (%s)",
+ NULL_MSGCHK(errmsg));
+ sqlite_freemem(errmsg);
+ return (SMB_LGRP_DOMLKP_FAILED);
+ }
+
+ switch (nrow) {
+ case 0:
+ /* new domain SID; insert it into the domains table */
+ sqlite_free_table(result);
+ return (smb_lgrp_dtbl_insert(db, sidstr, dom_idx));
+
+ case 1:
+ *dom_idx = atoi(result[1]);
+ sqlite_free_table(result);
+ return (SMB_LGRP_SUCCESS);
+ }
+
+ sqlite_free_table(result);
+ return (SMB_LGRP_DB_ERROR);
+}
+
+/*
+ * smb_lgrp_dtbl_getsid
+ *
+ * Searchs the domain table for the given domain index.
+ * Converts the found domain SID to binary format and
+ * returns it in the 'sid' arg.
+ *
+ * Caller must free the returned SID by calling free().
+ */
+static int
+smb_lgrp_dtbl_getsid(sqlite *db, uint32_t dom_idx, nt_sid_t **sid)
+{
+ char **result;
+ int nrow, ncol;
+ char *errmsg = NULL;
+ char *sql;
+ int rc;
+
+ sql = sqlite_mprintf("SELECT dom_sid FROM domains WHERE dom_idx = %u",
+ dom_idx);
+ if (sql == NULL)
+ return (SMB_LGRP_NO_MEMORY);
+
+ rc = sqlite_get_table(db, sql, &result, &nrow, &ncol, &errmsg);
+ sqlite_freemem(sql);
+
+ if (rc != SQLITE_OK) {
+ syslog(LOG_DEBUG, "failed to lookup domain index (%s)",
+ NULL_MSGCHK(errmsg));
+ sqlite_freemem(errmsg);
+ return (SMB_LGRP_DOMLKP_FAILED);
+ }
+
+ switch (nrow) {
+ case 0:
+ rc = SMB_LGRP_NO_SUCH_DOMAIN;
+ break;
+
+ case 1:
+ *sid = nt_sid_strtosid(result[1]);
+ rc = (*sid == NULL)
+ ? SMB_LGRP_INTERNAL_ERROR : SMB_LGRP_SUCCESS;
+ break;
+
+ default:
+ rc = SMB_LGRP_DB_ERROR;
+ break;
+ }
+
+ sqlite_free_table(result);
+ return (rc);
+}
+
+/*
+ * smb_lgrp_db_setinfo
+ *
+ * Initializes the db_info table upon database creation.
+ */
+static int
+smb_lgrp_db_setinfo(sqlite *db)
+{
+ char *errmsg = NULL;
+ char *sql;
+ int rc;
+
+ sql = sqlite_mprintf("INSERT INTO db_info (ver_major, ver_minor,"
+ " magic) VALUES (%d, %d, %u)", SMB_LGRP_DB_VERMAJOR,
+ SMB_LGRP_DB_VERMINOR, SMB_LGRP_DB_MAGIC);
+
+ if (sql == NULL)
+ return (SMB_LGRP_NO_MEMORY);
+
+ rc = sqlite_exec(db, sql, NULL, NULL, &errmsg);
+ sqlite_freemem(sql);
+ if (rc != SQLITE_OK) {
+ syslog(LOG_DEBUG, "failed to insert database information (%s)",
+ NULL_MSGCHK(errmsg));
+ sqlite_freemem(errmsg);
+ rc = SMB_LGRP_DBINIT_ERROR;
+ } else {
+ rc = SMB_LGRP_SUCCESS;
+ }
+
+ return (rc);
+}
+
+/*
+ * smb_lgrp_mlist_add
+ *
+ * Adds the given member (newm) to the input member list (in_members)
+ * if it's not already there. The result list will be returned in
+ * out_members. The caller must free the allocated memory for
+ * out_members by calling free().
+ *
+ * in_members and out_members are hex strings.
+ */
+static int
+smb_lgrp_mlist_add(smb_lgmlist_t *in_members, smb_lgmid_t *newm,
+ smb_lgmlist_t *out_members)
+{
+ char mid_hex[SMB_LGRP_MID_HEXSZ];
+ char *in_list;
+ char *out_list;
+ int in_size;
+ int out_size;
+ int mid_hexsz;
+ int i;
+
+ out_members->m_cnt = 0;
+ out_members->m_ids = NULL;
+
+ bzero(mid_hex, sizeof (mid_hex));
+ mid_hexsz = bintohex((const char *)newm, sizeof (smb_lgmid_t),
+ mid_hex, sizeof (mid_hex));
+
+ /*
+ * Check to see if this is already a group member
+ */
+ in_list = in_members->m_ids;
+ for (i = 0; i < in_members->m_cnt; i++) {
+ if (strncmp(in_list, mid_hex, mid_hexsz) == 0)
+ return (SMB_LGRP_MEMBER_IN_GROUP);
+ in_list += mid_hexsz;
+ }
+
+ in_size = (in_members->m_ids) ? strlen(in_members->m_ids) : 0;
+ out_size = in_size + sizeof (mid_hex) + 1;
+ out_list = malloc(out_size);
+ if (out_list == NULL)
+ return (SMB_LGRP_NO_MEMORY);
+
+ bzero(out_list, out_size);
+ if (in_members->m_ids)
+ (void) strlcpy(out_list, in_members->m_ids, out_size);
+ (void) strcat(out_list, mid_hex);
+
+ out_members->m_cnt = in_members->m_cnt + 1;
+ out_members->m_ids = out_list;
+
+ return (SMB_LGRP_SUCCESS);
+}
+
+/*
+ * smb_lgrp_mlist_del
+ *
+ * Removes the given member (msid) from the input member list
+ * (in_members) if it's already there. The result list will b
+ * returned in out_members. The caller must free the allocated
+ * memory for out_members by calling free().
+ *
+ * in_members and out_members are hex strings.
+ */
+static int
+smb_lgrp_mlist_del(smb_lgmlist_t *in_members, smb_lgmid_t *mid,
+ smb_lgmlist_t *out_members)
+{
+ char mid_hex[SMB_LGRP_MID_HEXSZ];
+ char *in_list;
+ char *out_list;
+ int in_size;
+ int out_size;
+ int mid_hexsz;
+ int out_cnt;
+ int i;
+
+ out_members->m_cnt = 0;
+ out_members->m_ids = NULL;
+
+ if ((in_members == NULL) || (in_members->m_cnt == 0))
+ return (SMB_LGRP_MEMBER_NOT_IN_GROUP);
+
+ in_size = strlen(in_members->m_ids);
+ out_size = in_size + sizeof (mid_hex) + 1;
+ out_list = malloc(out_size);
+ if (out_list == NULL)
+ return (SMB_LGRP_NO_MEMORY);
+
+ *out_list = '\0';
+
+ bzero(mid_hex, sizeof (mid_hex));
+ mid_hexsz = bintohex((const char *)mid, sizeof (smb_lgmid_t),
+ mid_hex, sizeof (mid_hex));
+
+ in_list = in_members->m_ids;
+ for (i = 0, out_cnt = 0; i < in_members->m_cnt; i++) {
+ if (strncmp(in_list, mid_hex, mid_hexsz)) {
+ (void) strncat(out_list, in_list, mid_hexsz);
+ out_cnt++;
+ }
+ in_list += mid_hexsz;
+ }
+
+ if (out_cnt == in_members->m_cnt) {
+ free(out_list);
+ return (SMB_LGRP_MEMBER_NOT_IN_GROUP);
+ }
+
+ out_members->m_cnt = out_cnt;
+ out_members->m_ids = out_list;
+ return (SMB_LGRP_SUCCESS);
+}
+
+/*
+ * smb_lgrp_plist_add
+ *
+ * Adds the given privilege to the input list (in_privs)
+ * if it's not already there. The result list is returned
+ * in out_privs. The caller must free the allocated memory
+ * for out_privs by calling free().
+ */
+static int
+smb_lgrp_plist_add(smb_lgplist_t *in_privs, smb_lgpid_t priv_id,
+ smb_lgplist_t *out_privs)
+{
+ int i, size;
+ smb_lgpid_t *pbuf;
+
+ out_privs->p_cnt = 0;
+ out_privs->p_ids = NULL;
+
+ for (i = 0; i < in_privs->p_cnt; i++) {
+ if (in_privs->p_ids[i] == priv_id)
+ return (SMB_LGRP_PRIV_HELD);
+ }
+
+ size = (in_privs->p_cnt + 1) * sizeof (smb_lgpid_t) + 1;
+ pbuf = malloc(size);
+ if (pbuf == NULL)
+ return (SMB_LGRP_NO_MEMORY);
+
+ bzero(pbuf, size);
+ bcopy(in_privs->p_ids, pbuf, in_privs->p_cnt * sizeof (smb_lgpid_t));
+ pbuf[in_privs->p_cnt] = priv_id;
+
+ out_privs->p_cnt = in_privs->p_cnt + 1;
+ out_privs->p_ids = pbuf;
+
+ return (SMB_LGRP_SUCCESS);
+}
+
+/*
+ * smb_lgrp_plist_del
+ *
+ * Removes the given privilege from the input list (in_privs)
+ * if it's already there. The result list is returned
+ * in out_privs. The caller must free the allocated memory
+ * for out_privs by calling free().
+ */
+static int
+smb_lgrp_plist_del(smb_lgplist_t *in_privs, smb_lgpid_t priv_id,
+ smb_lgplist_t *out_privs)
+{
+ int i, size;
+
+ out_privs->p_cnt = 0;
+ out_privs->p_ids = NULL;
+
+ if ((in_privs == NULL) || (in_privs->p_cnt == 0))
+ return (SMB_LGRP_PRIV_NOT_HELD);
+
+ size = (in_privs->p_cnt - 1) * sizeof (smb_lgpid_t) + 1;
+ out_privs->p_ids = malloc(size);
+ if (out_privs->p_ids == NULL)
+ return (SMB_LGRP_NO_MEMORY);
+
+ bzero(out_privs->p_ids, size);
+
+ for (i = 0; i < in_privs->p_cnt; i++) {
+ if (in_privs->p_ids[i] != priv_id)
+ out_privs->p_ids[out_privs->p_cnt++] =
+ in_privs->p_ids[i];
+ }
+
+ if (out_privs->p_cnt == in_privs->p_cnt) {
+ free(out_privs->p_ids);
+ out_privs->p_cnt = 0;
+ out_privs->p_ids = NULL;
+ return (SMB_LGRP_PRIV_NOT_HELD);
+ }
+
+ return (SMB_LGRP_SUCCESS);
+}
+
+/*
+ * smb_lgrp_encode_privset
+ *
+ * Encodes given privilege set into a buffer to be stored in the group
+ * database. Each entry of the encoded buffer contains the privilege ID
+ * of an enable privilege. The returned buffer is null-terminated.
+ */
+static void
+smb_lgrp_encode_privset(smb_group_t *grp, smb_lgplist_t *plist)
+{
+ smb_privset_t *privs;
+ uint32_t pcnt = plist->p_cnt;
+ int i;
+
+ bzero(plist->p_ids, sizeof (smb_lgpid_t) * plist->p_cnt);
+ plist->p_cnt = 0;
+
+ privs = grp->sg_privs;
+ if ((privs == NULL) || (privs->priv_cnt == 0))
+ return;
+
+ if (pcnt < privs->priv_cnt) {
+ assert(0);
+ }
+
+ for (i = 0; i < privs->priv_cnt; i++) {
+ if (privs->priv[i].attrs == SE_PRIVILEGE_ENABLED) {
+ plist->p_ids[plist->p_cnt++] =
+ (uint8_t)privs->priv[i].luid.lo_part;
+ }
+ }
+}
+
+/*
+ * smb_lgrp_decode_privset
+ *
+ * Decodes the privilege information read from group table
+ * (nprivs, privs) into a binray format specified by the
+ * privilege field of smb_group_t
+ */
+static int
+smb_lgrp_decode_privset(smb_group_t *grp, char *nprivs, char *privs)
+{
+ smb_lgplist_t plist;
+ int i;
+
+ plist.p_cnt = atoi(nprivs);
+ if (strlen(privs) != plist.p_cnt)
+ return (SMB_LGRP_BAD_DATA);
+
+ plist.p_ids = (smb_lgpid_t *)privs;
+ grp->sg_privs = smb_privset_new();
+ if (grp->sg_privs == NULL)
+ return (SMB_LGRP_NO_MEMORY);
+
+ for (i = 0; i < plist.p_cnt; i++)
+ smb_privset_enable(grp->sg_privs, plist.p_ids[i]);
+
+ return (SMB_LGRP_SUCCESS);
+}
+
+/*
+ * smb_lgrp_decode_members
+ *
+ * Decodes the members information read from group table
+ * (nmembers, members) into a binray format specified by the
+ * member fields of smb_group_t
+ */
+static int
+smb_lgrp_decode_members(smb_group_t *grp, char *nmembers, char *members,
+ sqlite *db)
+{
+ smb_lgmid_t *m_ids;
+ smb_lgmid_t *mid;
+ smb_gsid_t *member;
+ int mids_size;
+ int i, rc;
+
+ grp->sg_nmembers = atoi(nmembers);
+ mids_size = grp->sg_nmembers * sizeof (smb_lgmid_t);
+ m_ids = malloc(mids_size);
+ if (m_ids == NULL)
+ return (SMB_LGRP_NO_MEMORY);
+
+ grp->sg_members = malloc(grp->sg_nmembers * sizeof (smb_gsid_t));
+ if (grp->sg_members == NULL) {
+ free(m_ids);
+ return (SMB_LGRP_NO_MEMORY);
+ }
+
+ (void) hextobin(members, strlen(members), (char *)m_ids, mids_size);
+
+ mid = m_ids;
+ member = grp->sg_members;
+ for (i = 0; i < grp->sg_nmembers; i++, mid++, member++) {
+ rc = smb_lgrp_getsid(mid->m_idx, &mid->m_rid, mid->m_type, db,
+ &member->gs_sid);
+ if (rc != SMB_LGRP_SUCCESS) {
+ free(m_ids);
+ return (SMB_LGRP_DB_ERROR);
+ }
+
+ member->gs_type = mid->m_type;
+ }
+
+ free(m_ids);
+ return (SMB_LGRP_SUCCESS);
+}
+
+/*
+ * smb_lgrp_decode
+ *
+ * Fills out the fields of the given group (grp) based in the
+ * string information read from the group table. infolvl determines
+ * which fields are requested and need to be decoded.
+ *
+ * Allocated memories must be freed by calling smb_lgrp_free()
+ * upon successful return.
+ */
+static int
+smb_lgrp_decode(smb_group_t *grp, char **values, int infolvl, sqlite *db)
+{
+ uint32_t sid_idx;
+ int rc;
+
+ if (infolvl == SMB_LGRP_INFO_NONE)
+ return (SMB_LGRP_SUCCESS);
+
+ if (infolvl & SMB_LGRP_INFO_NAME) {
+ grp->sg_name = strdup(values[SMB_LGRP_GTBL_NAME]);
+ if (grp->sg_name == NULL)
+ return (SMB_LGRP_NO_MEMORY);
+ }
+
+ if (infolvl & SMB_LGRP_INFO_CMNT) {
+ grp->sg_cmnt = strdup(values[SMB_LGRP_GTBL_CMNT]);
+ if (grp->sg_cmnt == NULL) {
+ smb_lgrp_free(grp);
+ return (SMB_LGRP_NO_MEMORY);
+ }
+ }
+
+
+ if (infolvl & SMB_LGRP_INFO_SID) {
+ sid_idx = atoi(values[SMB_LGRP_GTBL_SIDIDX]);
+ grp->sg_rid = atoi(values[SMB_LGRP_GTBL_SIDRID]);
+ grp->sg_attr = atoi(values[SMB_LGRP_GTBL_SIDATR]);
+ grp->sg_id.gs_type = atoi(values[SMB_LGRP_GTBL_SIDTYP]);
+ rc = smb_lgrp_getsid(sid_idx, &grp->sg_rid, grp->sg_id.gs_type,
+ db, &grp->sg_id.gs_sid);
+ if (rc != SMB_LGRP_SUCCESS) {
+ smb_lgrp_free(grp);
+ return (SMB_LGRP_NO_MEMORY);
+ }
+ grp->sg_domain = (sid_idx == SMB_LGRP_LOCAL_IDX)
+ ? SMB_LGRP_LOCAL : SMB_LGRP_BUILTIN;
+ }
+
+ if (infolvl & SMB_LGRP_INFO_PRIV) {
+ rc = smb_lgrp_decode_privset(grp, values[SMB_LGRP_GTBL_NPRIVS],
+ values[SMB_LGRP_GTBL_PRIVS]);
+
+ if (rc != SMB_LGRP_SUCCESS) {
+ smb_lgrp_free(grp);
+ return (rc);
+ }
+ }
+
+ if (infolvl & SMB_LGRP_INFO_MEMB) {
+ rc = smb_lgrp_decode_members(grp, values[SMB_LGRP_GTBL_NMEMBS],
+ values[SMB_LGRP_GTBL_MEMBS], db);
+ if (rc != SMB_LGRP_SUCCESS) {
+ smb_lgrp_free(grp);
+ return (rc);
+ }
+ }
+
+ return (SMB_LGRP_SUCCESS);
+}
+
+/*
+ * smb_lgrp_chkname
+ *
+ * User account names are limited to 20 characters and group names are
+ * limited to 256 characters. In addition, account names cannot be terminated
+ * by a period and they cannot include commas or any of the following printable
+ * characters: ", /, \, [, ], :, |, <, >, +, =, ;, ?, *.
+ * Names also cannot include characters in the range 1-31, which are
+ * nonprintable.
+ *
+ * Source: MSDN, description of NetLocalGroupAdd function.
+ */
+static boolean_t
+smb_lgrp_chkname(char *name)
+{
+ static char *invalid_chars =
+ "\001\002\003\004\005\006\007\010\011\012\013\014\015\016\017"
+ "\020\021\022\023\024\025\026\027\030\031"
+ "\"/\\[]:|<>+=;,*?";
+ int len, i;
+
+ if (name == NULL || *name == '\0')
+ return (B_FALSE);
+
+ len = strlen(name);
+ if (len > SMB_LGRP_NAME_MAX)
+ return (B_FALSE);
+
+ if (name[len - 1] == '.')
+ return (B_FALSE);
+
+ for (i = 0; i < len; i++)
+ if (strchr(invalid_chars, name[i]))
+ return (B_FALSE);
+
+ (void) utf8_strlwr(name);
+ return (B_TRUE);
+}
+
+/*
+ * smb_lgrp_set_default_privs
+ *
+ * set default privileges for Administrators and Backup Operators
+ */
+static void
+smb_lgrp_set_default_privs(smb_group_t *grp)
+{
+ if (utf8_strcasecmp(grp->sg_name, "Administrators") == 0) {
+ smb_privset_enable(grp->sg_privs, SE_TAKE_OWNERSHIP_LUID);
+ return;
+ }
+
+ if (utf8_strcasecmp(grp->sg_name, "Backup Operators") == 0) {
+ smb_privset_enable(grp->sg_privs, SE_BACKUP_LUID);
+ smb_privset_enable(grp->sg_privs, SE_RESTORE_LUID);
+ return;
+ }
+}
+
+/*
+ * smb_lgrp_getsid
+ *
+ * Returns a SID based on the provided information
+ * If dom_idx is 0, it means 'rid' contains a UID/GID and the
+ * returned SID will be a local SID. If dom_idx is not 0 then
+ * the domain SID will be fetched from the domain table.
+ */
+static int
+smb_lgrp_getsid(int dom_idx, uint32_t *rid, uint16_t sid_type,
+ sqlite *db, nt_sid_t **sid)
+{
+ nt_sid_t *dom_sid = NULL;
+ nt_sid_t *res_sid = NULL;
+ int id_type;
+ int rc;
+
+ *sid = NULL;
+ if (dom_idx == SMB_LGRP_LOCAL_IDX) {
+ id_type = (sid_type == SidTypeUser)
+ ? SMB_IDMAP_USER : SMB_IDMAP_GROUP;
+ if (smb_idmap_getsid(*rid, id_type, &res_sid) != IDMAP_SUCCESS)
+ return (SMB_LGRP_NO_SID);
+
+ /*
+ * Make sure the returned SID is local
+ */
+ if (!nt_sid_is_indomain(smb_lgrp_lsid, res_sid)) {
+ free(res_sid);
+ return (SMB_LGRP_SID_NOTLOCAL);
+ }
+
+ (void) nt_sid_get_rid(res_sid, rid);
+ *sid = res_sid;
+ return (SMB_LGRP_SUCCESS);
+ }
+
+ rc = smb_lgrp_dtbl_getsid(db, dom_idx, &dom_sid);
+ if (rc != SMB_LGRP_SUCCESS)
+ return (SMB_LGRP_DB_ERROR);
+
+ res_sid = nt_sid_splice(dom_sid, *rid);
+ free(dom_sid);
+ if (res_sid == NULL)
+ return (SMB_LGRP_NO_MEMORY);
+
+ *sid = res_sid;
+ return (SMB_LGRP_SUCCESS);
+}