xref: /illumos-gate/usr/src/lib/smbsrv/libsmb/common/smb_lgrp.c (revision 1fa2a66491e7d8ae0be84e7da4da8e812480c710)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
24  * Copyright (c) 2013 RackTop Systems.
25  * Copyright 2019 Nexenta by DDN, Inc. All rights reserved.
26  */
27 
28 #include <stdlib.h>
29 #include <strings.h>
30 #include <unistd.h>
31 #include <syslog.h>
32 #include <thread.h>
33 #include <synch.h>
34 #include <grp.h>
35 #include <assert.h>
36 #include <libintl.h>
37 #include <smbsrv/libsmb.h>
38 #include <smb_sqlite.h>
39 #include <sys/types.h>
40 #include <sys/stat.h>
41 #include <sys/param.h>
42 #include <libcmdutils.h>
43 
44 /*
45  * Local domain SID (aka machine SID) is not stored in the domain table
46  * therefore the index is 0
47  */
48 #define	SMB_LGRP_LOCAL_IDX	0
49 #define	SMB_LGRP_BUILTIN_IDX	1
50 
51 #define	SMB_LGRP_DB_NAME	"/var/smb/smbgroup.db"
52 #define	SMB_LGRP_DB_TIMEOUT	3000		/* in millisecond */
53 #define	SMB_LGRP_DB_VERMAJOR	1
54 #define	SMB_LGRP_DB_VERMINOR	0
55 #define	SMB_LGRP_DB_MAGIC	0x4C475250	/* LGRP */
56 
57 #define	SMB_LGRP_DB_ORD		1		/* open read-only */
58 #define	SMB_LGRP_DB_ORW		2		/* open read/write */
59 
60 #define	SMB_LGRP_DB_ADDMEMBER	1
61 #define	SMB_LGRP_DB_DELMEMBER	2
62 
63 /*
64  * members column of the groups table is an array of
65  * member structure smb_lgmid_t defined below.
66  *
67  * privs column of the groups table is an array of bytes
68  * where each byte is the id of an enable privilege
69  */
70 #define	SMB_LGRP_DB_SQL \
71 	"CREATE TABLE db_info ("				\
72 	"	ver_major INTEGER,"				\
73 	"	ver_minor INTEGER,"				\
74 	"	magic     INTEGER"				\
75 	");"							\
76 	""							\
77 	"CREATE TABLE domains ("				\
78 	"	dom_idx INTEGER PRIMARY KEY,"			\
79 	"	dom_sid TEXT UNIQUE,"				\
80 	"       dom_cnt INTEGER"				\
81 	");"							\
82 	""							\
83 	"CREATE UNIQUE INDEX domsid_idx ON domains (dom_sid);"	\
84 	""							\
85 	"CREATE TABLE groups ("					\
86 	"	name      TEXT PRIMARY KEY,"			\
87 	"	sid_idx   INTEGER,"				\
88 	"	sid_rid   INTEGER,"				\
89 	"	sid_type  INTEGER,"				\
90 	"	sid_attrs INTEGER,"				\
91 	"	comment   TEXT,"				\
92 	"	n_privs   INTEGER,"				\
93 	"	privs     BLOB,"				\
94 	"	n_members INTEGER,"				\
95 	"	members   BLOB"					\
96 	");"							\
97 	""							\
98 	"CREATE INDEX grprid_idx ON groups (sid_rid);"
99 
100 /*
101  * Number of groups table columns
102  */
103 #define	SMB_LGRP_GTBL_NCOL	10
104 
105 #define	SMB_LGRP_GTBL_NAME	0
106 #define	SMB_LGRP_GTBL_SIDIDX	1
107 #define	SMB_LGRP_GTBL_SIDRID	2
108 #define	SMB_LGRP_GTBL_SIDTYP	3
109 #define	SMB_LGRP_GTBL_SIDATR	4
110 #define	SMB_LGRP_GTBL_CMNT	5
111 #define	SMB_LGRP_GTBL_NPRIVS	6
112 #define	SMB_LGRP_GTBL_PRIVS	7
113 #define	SMB_LGRP_GTBL_NMEMBS	8
114 #define	SMB_LGRP_GTBL_MEMBS	9
115 
116 #define	SMB_LGRP_INFO_NONE	0x00
117 #define	SMB_LGRP_INFO_NAME	0x01
118 #define	SMB_LGRP_INFO_CMNT	0x02
119 #define	SMB_LGRP_INFO_SID	0x04
120 #define	SMB_LGRP_INFO_PRIV	0x08
121 #define	SMB_LGRP_INFO_MEMB	0x10
122 #define	SMB_LGRP_INFO_ALL	0x1F
123 
124 #define	SMB_LGRP_PGRP_GRPTMP	"/etc/gtmp"
125 #define	SMB_LGRP_PGRP_GRPBUFSIZ	5120
126 #define	SMB_LGRP_PGRP_GROUP	"/etc/group"
127 #define	SMB_LGRP_PGRP_MAXGLEN	9	/* max length of group name */
128 #define	SMB_LGRP_PGRP_DEFRID	1000	/* lowest cifs created gid */
129 
130 #define	SMB_LGRP_PGRP_NOTUNIQUE	0
131 #define	SMB_LGRP_PGRP_RESERVED	1
132 #define	SMB_LGRP_PGRP_UNIQUE	2
133 #define	SMB_LGRP_PGRP_TOOBIG	3
134 #define	SMB_LGRP_PGRP_INVALID	4
135 
136 #define	NULL_MSGCHK(msg)	((msg) ? (msg) : "NULL")
137 
138 /* Member ID */
139 typedef struct smb_lgmid {
140 	uint32_t m_idx;
141 	uint32_t m_rid;
142 	uint16_t m_type;
143 } smb_lgmid_t;
144 
145 /* Buffer size to hold hex form of the above (>24). */
146 #define	SMB_LGRP_MID_HEXSZ	32
147 
148 /* Size of idx,rid parts of above, in hex form. */
149 #define	SMB_LGRP_IDXRID_LEN	16
150 
151 /* Member list */
152 typedef struct smb_lgmlist {
153 	uint32_t	m_cnt;
154 	char		*m_ids;
155 } smb_lgmlist_t;
156 
157 /* Privilege ID */
158 typedef uint8_t smb_lgpid_t;
159 
160 /* Privilege list */
161 typedef struct smb_lgplist {
162 	uint32_t	p_cnt;
163 	smb_lgpid_t	*p_ids;
164 } smb_lgplist_t;
165 
166 static struct {
167 	int	errnum;
168 	char	*errmsg;
169 } errtab[] = {
170 	{ SMB_LGRP_SUCCESS,		"success" },
171 	{ SMB_LGRP_INVALID_ARG,		"invalid argument" },
172 	{ SMB_LGRP_INVALID_MEMBER,	"invalid member type" },
173 	{ SMB_LGRP_INVALID_NAME,	"invalid name" },
174 	{ SMB_LGRP_NOT_FOUND,		"group not found" },
175 	{ SMB_LGRP_EXISTS,		"group exists" },
176 	{ SMB_LGRP_NO_SID,		"cannot obtain a SID" },
177 	{ SMB_LGRP_NO_LOCAL_SID,	"cannot get the machine SID" },
178 	{ SMB_LGRP_SID_NOTLOCAL,	"local account has non-local SID" },
179 	{ SMB_LGRP_WKSID,
180 		"operation not permitted on well-known account" },
181 	{ SMB_LGRP_NO_MEMORY,		"not enough memory" },
182 	{ SMB_LGRP_DB_ERROR,		"database operation error" },
183 	{ SMB_LGRP_DBINIT_ERROR,	"database initialization error" },
184 	{ SMB_LGRP_INTERNAL_ERROR,	"internal error" },
185 	{ SMB_LGRP_MEMBER_IN_GROUP,	"member already in group" },
186 	{ SMB_LGRP_MEMBER_NOT_IN_GROUP,	"not a member" },
187 	{ SMB_LGRP_NO_SUCH_PRIV,	"no such privilege" },
188 	{ SMB_LGRP_NO_SUCH_DOMAIN,	"no such domain SID" },
189 	{ SMB_LGRP_PRIV_HELD,		"privilege already held" },
190 	{ SMB_LGRP_PRIV_NOT_HELD,	"privilege not held" },
191 	{ SMB_LGRP_BAD_DATA,		"bad data" },
192 	{ SMB_LGRP_NO_MORE,		"no more groups" },
193 	{ SMB_LGRP_DBOPEN_FAILED,	"database open failed" },
194 	{ SMB_LGRP_DBEXEC_FAILED,	"database operation failed" },
195 	{ SMB_LGRP_DBINIT_FAILED,	"database initialization failed" },
196 	{ SMB_LGRP_DOMLKP_FAILED,	"domain SID lookup failed" },
197 	{ SMB_LGRP_DOMINS_FAILED,	"domain SID insert failed" },
198 	{ SMB_LGRP_INSERT_FAILED,	"group insert failed" },
199 	{ SMB_LGRP_DELETE_FAILED,	"group delete failed" },
200 	{ SMB_LGRP_UPDATE_FAILED,	"group update failed" },
201 	{ SMB_LGRP_LOOKUP_FAILED,	"group lookup failed" },
202 	{ SMB_LGRP_OFFLINE,		"local group service is offline" },
203 	{ SMB_LGRP_POSIXCREATE_FAILED,	"posix group create failed" }
204 };
205 
206 /*
207  * Serialization for the local group API.
208  */
209 typedef struct {
210 	mutex_t		lg_mutex;
211 	cond_t		lg_cv;
212 	boolean_t	lg_online;
213 	uint32_t	lg_refcnt;
214 	smb_sid_t	*lg_machine_sid;
215 } smb_localgrp_t;
216 
217 static smb_localgrp_t smb_localgrp;
218 
219 static boolean_t smb_lgrp_enter(void);
220 static void smb_lgrp_exit(void);
221 static int smb_lgrp_db_init(void);
222 static sqlite *smb_lgrp_db_open(int);
223 static void smb_lgrp_db_close(sqlite *);
224 static int smb_lgrp_db_setinfo(sqlite *);
225 
226 static boolean_t smb_lgrp_gtbl_exists(sqlite *, char *);
227 static int smb_lgrp_gtbl_lookup(sqlite *, int, smb_group_t *, int, ...);
228 static int smb_lgrp_gtbl_insert(sqlite *, smb_group_t *);
229 static int smb_lgrp_gtbl_update(sqlite *, char *, smb_group_t *, int);
230 static int smb_lgrp_gtbl_delete(sqlite *, char *);
231 static int smb_lgrp_gtbl_update_mlist(sqlite *, char *, smb_gsid_t *, int);
232 static int smb_lgrp_gtbl_update_plist(sqlite *, char *, uint8_t, boolean_t);
233 static int smb_lgrp_gtbl_count(sqlite *, int, int *);
234 
235 static int smb_lgrp_dtbl_insert(sqlite *, char *, uint32_t *);
236 static int smb_lgrp_dtbl_getidx(sqlite *, smb_sid_t *, uint16_t,
237     uint32_t *, uint32_t *);
238 static int smb_lgrp_dtbl_getsid(sqlite *, uint32_t, smb_sid_t **);
239 
240 static int smb_lgrp_mlist_add(smb_lgmlist_t *, smb_lgmid_t *, smb_lgmlist_t *);
241 static int smb_lgrp_mlist_del(smb_lgmlist_t *, smb_lgmid_t *, smb_lgmlist_t *);
242 
243 static int smb_lgrp_plist_add(smb_lgplist_t *, smb_lgpid_t, smb_lgplist_t *);
244 static int smb_lgrp_plist_del(smb_lgplist_t *, smb_lgpid_t, smb_lgplist_t *);
245 
246 static void smb_lgrp_encode_privset(smb_group_t *, smb_lgplist_t *);
247 
248 static int smb_lgrp_decode(smb_group_t *, char **, int, sqlite *);
249 static int smb_lgrp_decode_privset(smb_group_t *, char *, char *);
250 static int smb_lgrp_decode_members(smb_group_t *, char *, char *, sqlite *);
251 
252 static void smb_lgrp_set_default_privs(smb_group_t *);
253 static boolean_t smb_lgrp_normalize_name(char *);
254 static boolean_t smb_lgrp_chkmember(uint16_t);
255 static int smb_lgrp_getsid(int, uint32_t *, uint16_t, sqlite *, smb_sid_t **);
256 static int smb_lgrp_getgid(uint32_t rid, gid_t *gid);
257 static boolean_t smb_lgrp_exists(char *);
258 static int smb_lgrp_pgrp_add(char *);
259 
260 /*
261  * smb_lgrp_add
262  *
263  * Create a local group with the given name and comment.
264  * This new group doesn't have any members and no enabled
265  * privileges.
266  *
267  * No well-known accounts can be added other than Administators,
268  * Backup Operators and Power Users. These built-in groups
269  * won't have any members when created but a set of default
270  * privileges will be enabled for them.
271  */
272 int
273 smb_lgrp_add(char *gname, char *cmnt)
274 {
275 	smb_wka_t *wka;
276 	struct group *pxgrp;
277 	smb_group_t grp;
278 	smb_sid_t *sid = NULL;
279 	sqlite *db;
280 	int rc;
281 
282 	if (!smb_lgrp_normalize_name(gname))
283 		return (SMB_LGRP_INVALID_NAME);
284 
285 	if (cmnt && (strlen(cmnt) > SMB_LGRP_COMMENT_MAX))
286 		return (SMB_LGRP_INVALID_ARG);
287 
288 	bzero(&grp, sizeof (grp));
289 	grp.sg_name = smb_strlwr(gname);
290 	grp.sg_cmnt = cmnt;
291 
292 	if (!smb_lgrp_enter())
293 		return (SMB_LGRP_OFFLINE);
294 
295 	wka = smb_wka_lookup_name(gname);
296 	if (wka == NULL) {
297 		if ((pxgrp = getgrnam(gname)) == NULL) {
298 			if (smb_lgrp_pgrp_add(gname) != 0) {
299 				smb_lgrp_exit();
300 				return (SMB_LGRP_POSIXCREATE_FAILED);
301 			}
302 
303 			if ((pxgrp = getgrnam(gname)) == NULL) {
304 				smb_lgrp_exit();
305 				return (SMB_LGRP_NOT_FOUND);
306 			}
307 		}
308 
309 		/*
310 		 * Make sure a local SID can be obtained
311 		 */
312 		if (smb_idmap_getsid(pxgrp->gr_gid, SMB_IDMAP_GROUP, &sid)
313 		    != IDMAP_SUCCESS) {
314 			smb_lgrp_exit();
315 			return (SMB_LGRP_NO_SID);
316 		}
317 
318 		if (!smb_sid_indomain(smb_localgrp.lg_machine_sid, sid)) {
319 			free(sid);
320 			smb_lgrp_exit();
321 			return (SMB_LGRP_SID_NOTLOCAL);
322 		}
323 
324 		free(sid);
325 		grp.sg_id.gs_type = SidTypeAlias;
326 		grp.sg_domain = SMB_DOMAIN_LOCAL;
327 		grp.sg_rid = pxgrp->gr_gid;
328 	} else {
329 		if ((wka->wka_flags & SMB_WKAFLG_LGRP_ENABLE) == 0) {
330 			/* cannot add well-known accounts */
331 			smb_lgrp_exit();
332 			return (SMB_LGRP_WKSID);
333 		}
334 
335 		grp.sg_id.gs_type = wka->wka_type;
336 		if ((sid = smb_sid_fromstr(wka->wka_sid)) == NULL) {
337 			smb_lgrp_exit();
338 			return (SMB_LGRP_NO_MEMORY);
339 		}
340 
341 		(void) smb_sid_getrid(sid, &grp.sg_rid);
342 		free(sid);
343 		grp.sg_domain = SMB_DOMAIN_BUILTIN;
344 		grp.sg_privs = smb_privset_new();
345 		smb_lgrp_set_default_privs(&grp);
346 	}
347 
348 	if (smb_lgrp_exists(grp.sg_name)) {
349 		smb_lgrp_exit();
350 		return (SMB_LGRP_EXISTS);
351 	}
352 
353 	grp.sg_attr = SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT |
354 	    SE_GROUP_ENABLED;
355 
356 	db = smb_lgrp_db_open(SMB_LGRP_DB_ORW);
357 	rc = smb_lgrp_gtbl_insert(db, &grp);
358 	smb_lgrp_db_close(db);
359 
360 	smb_privset_free(grp.sg_privs);
361 	smb_lgrp_exit();
362 	return (rc);
363 }
364 
365 /*
366  * smb_lgrp_rename
367  *
368  * Renames the given group
369  */
370 int
371 smb_lgrp_rename(char *gname, char *new_gname)
372 {
373 	smb_group_t grp;
374 	sqlite *db;
375 	int rc;
376 
377 	if (!smb_lgrp_normalize_name(gname))
378 		return (SMB_LGRP_INVALID_NAME);
379 
380 	if (!smb_lgrp_normalize_name(gname))
381 		return (SMB_LGRP_INVALID_NAME);
382 
383 	if (smb_strcasecmp(gname, new_gname, 0) == 0)
384 		return (SMB_LGRP_SUCCESS);
385 
386 	/* Cannot rename well-known groups */
387 	if (smb_wka_lookup_name(gname) != NULL)
388 		return (SMB_LGRP_WKSID);
389 
390 	/* Cannot rename to a well-known groups */
391 	if (smb_wka_lookup_name(new_gname) != NULL)
392 		return (SMB_LGRP_WKSID);
393 
394 	grp.sg_name = new_gname;
395 
396 	if (!smb_lgrp_enter())
397 		return (SMB_LGRP_OFFLINE);
398 
399 	if (getgrnam(new_gname) == NULL) {
400 		if (smb_lgrp_pgrp_add(new_gname) != 0) {
401 			smb_lgrp_exit();
402 			return (SMB_LGRP_POSIXCREATE_FAILED);
403 		}
404 
405 		if (getgrnam(new_gname) == NULL) {
406 			smb_lgrp_exit();
407 			return (SMB_LGRP_NOT_FOUND);
408 		}
409 	}
410 
411 	db = smb_lgrp_db_open(SMB_LGRP_DB_ORW);
412 	rc = smb_lgrp_gtbl_update(db, gname, &grp, SMB_LGRP_GTBL_NAME);
413 	smb_lgrp_db_close(db);
414 
415 	smb_lgrp_exit();
416 	return (rc);
417 }
418 
419 /*
420  * smb_lgrp_delete
421  *
422  * Deletes the specified local group.
423  */
424 int
425 smb_lgrp_delete(char *gname)
426 {
427 	sqlite *db;
428 	int rc;
429 
430 	if (!smb_lgrp_normalize_name(gname))
431 		return (SMB_LGRP_INVALID_NAME);
432 
433 	/* Cannot remove a built-in group */
434 	if (smb_wka_lookup_name(gname) != NULL)
435 		return (SMB_LGRP_WKSID);
436 
437 
438 	if (!smb_lgrp_exists(gname))
439 		return (SMB_LGRP_NOT_FOUND);
440 
441 	if (!smb_lgrp_enter())
442 		return (SMB_LGRP_OFFLINE);
443 
444 	db = smb_lgrp_db_open(SMB_LGRP_DB_ORW);
445 	rc = smb_lgrp_gtbl_delete(db, gname);
446 	smb_lgrp_db_close(db);
447 
448 	smb_lgrp_exit();
449 	return (rc);
450 }
451 
452 /*
453  * smb_lgrp_setcmnt
454  *
455  * Sets the description for the given group
456  */
457 int
458 smb_lgrp_setcmnt(char *gname, char *cmnt)
459 {
460 	smb_group_t grp;
461 	sqlite *db;
462 	int rc;
463 
464 	if (!smb_lgrp_normalize_name(gname))
465 		return (SMB_LGRP_INVALID_NAME);
466 
467 	if (cmnt && (strlen(cmnt) > SMB_LGRP_COMMENT_MAX))
468 		return (SMB_LGRP_INVALID_ARG);
469 
470 	grp.sg_cmnt = cmnt;
471 
472 	if (!smb_lgrp_enter())
473 		return (SMB_LGRP_OFFLINE);
474 
475 	db = smb_lgrp_db_open(SMB_LGRP_DB_ORW);
476 	rc = smb_lgrp_gtbl_update(db, gname, &grp, SMB_LGRP_GTBL_CMNT);
477 	smb_lgrp_db_close(db);
478 
479 	smb_lgrp_exit();
480 	return (rc);
481 }
482 
483 /*
484  * smb_lgrp_getcmnt
485  *
486  * Obtain the description of the specified group
487  */
488 int
489 smb_lgrp_getcmnt(char *gname, char **cmnt)
490 {
491 	smb_group_t grp;
492 	sqlite *db;
493 	int rc;
494 
495 	if (!smb_lgrp_normalize_name(gname))
496 		return (SMB_LGRP_INVALID_NAME);
497 
498 	if (cmnt == NULL)
499 		return (SMB_LGRP_INVALID_ARG);
500 
501 	if (!smb_lgrp_enter())
502 		return (SMB_LGRP_OFFLINE);
503 
504 	db = smb_lgrp_db_open(SMB_LGRP_DB_ORD);
505 	rc = smb_lgrp_gtbl_lookup(db, SMB_LGRP_GTBL_NAME, &grp,
506 	    SMB_LGRP_INFO_CMNT, gname);
507 	smb_lgrp_db_close(db);
508 	smb_lgrp_exit();
509 
510 	if (rc == SMB_LGRP_SUCCESS) {
511 		*cmnt = grp.sg_cmnt;
512 		grp.sg_cmnt = NULL;
513 		smb_lgrp_free(&grp);
514 	}
515 
516 	return (rc);
517 }
518 
519 
520 /*
521  * smb_lgrp_setpriv
522  *
523  * Enable/disable the specified privilge for the group
524  */
525 int
526 smb_lgrp_setpriv(char *gname, uint8_t priv_lid, boolean_t enable)
527 {
528 	sqlite *db;
529 	int rc;
530 
531 	if (!smb_lgrp_normalize_name(gname))
532 		return (SMB_LGRP_INVALID_NAME);
533 
534 	if ((priv_lid < SE_MIN_LUID) || (priv_lid > SE_MAX_LUID))
535 		return (SMB_LGRP_NO_SUCH_PRIV);
536 
537 	if (!smb_lgrp_enter())
538 		return (SMB_LGRP_OFFLINE);
539 
540 	db = smb_lgrp_db_open(SMB_LGRP_DB_ORW);
541 	rc = smb_lgrp_gtbl_update_plist(db, gname, priv_lid, enable);
542 	smb_lgrp_db_close(db);
543 	smb_lgrp_exit();
544 
545 	if (enable) {
546 		if (rc == SMB_LGRP_PRIV_HELD)
547 			rc = SMB_LGRP_SUCCESS;
548 	} else {
549 		if (rc == SMB_LGRP_PRIV_NOT_HELD)
550 			rc = SMB_LGRP_SUCCESS;
551 	}
552 
553 	return (rc);
554 }
555 
556 /*
557  * smb_lgrp_getpriv
558  *
559  * Obtain the status of the specified privilge for the group
560  */
561 int
562 smb_lgrp_getpriv(char *gname, uint8_t priv_lid, boolean_t *enable)
563 {
564 	sqlite *db;
565 	smb_group_t grp;
566 	int rc;
567 
568 	if (!smb_lgrp_normalize_name(gname))
569 		return (SMB_LGRP_INVALID_NAME);
570 
571 	if ((priv_lid < SE_MIN_LUID) || (priv_lid > SE_MAX_LUID))
572 		return (SMB_LGRP_NO_SUCH_PRIV);
573 
574 	if (!smb_lgrp_enter())
575 		return (SMB_LGRP_OFFLINE);
576 
577 	db = smb_lgrp_db_open(SMB_LGRP_DB_ORD);
578 	rc = smb_lgrp_gtbl_lookup(db, SMB_LGRP_GTBL_NAME, &grp,
579 	    SMB_LGRP_INFO_PRIV, gname);
580 	smb_lgrp_db_close(db);
581 	smb_lgrp_exit();
582 
583 	if (rc == SMB_LGRP_SUCCESS) {
584 		*enable = (smb_privset_query(grp.sg_privs, priv_lid) == 1);
585 		smb_lgrp_free(&grp);
586 	}
587 
588 	return (rc);
589 }
590 
591 /*
592  * smb_lgrp_add_member
593  *
594  * Add the given account to the specified group as its member.
595  */
596 int
597 smb_lgrp_add_member(char *gname, smb_sid_t *msid, uint16_t sid_type)
598 {
599 	sqlite *db;
600 	smb_gsid_t mid;
601 	int rc;
602 
603 	if (!smb_lgrp_normalize_name(gname))
604 		return (SMB_LGRP_INVALID_NAME);
605 
606 	if (!smb_sid_isvalid(msid))
607 		return (SMB_LGRP_INVALID_ARG);
608 
609 	if (!smb_lgrp_chkmember(sid_type))
610 		return (SMB_LGRP_INVALID_MEMBER);
611 
612 	mid.gs_sid = msid;
613 	mid.gs_type = sid_type;
614 
615 	if (!smb_lgrp_enter())
616 		return (SMB_LGRP_OFFLINE);
617 
618 	db = smb_lgrp_db_open(SMB_LGRP_DB_ORW);
619 	rc = smb_lgrp_gtbl_update_mlist(db, gname, &mid, SMB_LGRP_DB_ADDMEMBER);
620 	smb_lgrp_db_close(db);
621 
622 	smb_lgrp_exit();
623 	return (rc);
624 }
625 
626 /*
627  * smb_lgrp_del_member
628  *
629  * Delete the specified member from the given group.
630  */
631 int
632 smb_lgrp_del_member(char *gname, smb_sid_t *msid, uint16_t sid_type)
633 {
634 	sqlite *db;
635 	smb_gsid_t mid;
636 	int rc;
637 
638 	if (!smb_lgrp_normalize_name(gname))
639 		return (SMB_LGRP_INVALID_NAME);
640 
641 	if (!smb_sid_isvalid(msid))
642 		return (SMB_LGRP_INVALID_ARG);
643 
644 	mid.gs_sid = msid;
645 	mid.gs_type = sid_type;
646 
647 	if (!smb_lgrp_enter())
648 		return (SMB_LGRP_OFFLINE);
649 
650 	db = smb_lgrp_db_open(SMB_LGRP_DB_ORW);
651 	rc = smb_lgrp_gtbl_update_mlist(db, gname, &mid, SMB_LGRP_DB_DELMEMBER);
652 	smb_lgrp_db_close(db);
653 
654 	smb_lgrp_exit();
655 	return (rc);
656 }
657 
658 /*
659  * smb_lgrp_getbyname
660  *
661  * Retrieves the information of the group specified by
662  * the given name.
663  *
664  * Note that this function doesn't allocate the group
665  * structure itself only the fields, so the given grp
666  * pointer has to point to a group structure.
667  * Caller must free the allocated memories for the fields
668  * by calling smb_lgrp_free().
669  */
670 int
671 smb_lgrp_getbyname(char *gname, smb_group_t *grp)
672 {
673 	sqlite *db;
674 	int rc;
675 
676 	if (!smb_lgrp_normalize_name(gname))
677 		return (SMB_LGRP_INVALID_NAME);
678 
679 	if (!smb_lgrp_enter())
680 		return (SMB_LGRP_OFFLINE);
681 
682 	db = smb_lgrp_db_open(SMB_LGRP_DB_ORD);
683 	rc = smb_lgrp_gtbl_lookup(db, SMB_LGRP_GTBL_NAME, grp,
684 	    SMB_LGRP_INFO_ALL, gname);
685 	smb_lgrp_db_close(db);
686 
687 	smb_lgrp_exit();
688 	return (rc);
689 }
690 
691 /*
692  * smb_lgrp_getbyrid
693  *
694  * Retrieves the information of the group specified by
695  * the given RID and domain type.
696  *
697  * Note that this function doesn't allocate the group
698  * structure itself only the fields, so the given grp
699  * pointer has to point to a group structure.
700  * Caller must free the allocated memories for the fields
701  * by calling smb_lgrp_free().
702  *
703  * If grp is NULL no information would be returned. The
704  * return value of SMB_LGRP_SUCCESS will indicate that a
705  * group with the given information exists.
706  */
707 int
708 smb_lgrp_getbyrid(uint32_t rid, smb_domain_type_t domtype, smb_group_t *grp)
709 {
710 	smb_group_t tmpgrp;
711 	sqlite *db;
712 	int infolvl = SMB_LGRP_INFO_ALL;
713 	int rc;
714 
715 	if (!smb_lgrp_enter())
716 		return (SMB_LGRP_OFFLINE);
717 
718 	if (grp == NULL) {
719 		grp = &tmpgrp;
720 		infolvl = SMB_LGRP_INFO_NONE;
721 	}
722 
723 	db = smb_lgrp_db_open(SMB_LGRP_DB_ORD);
724 	rc = smb_lgrp_gtbl_lookup(db, SMB_LGRP_GTBL_SIDRID, grp, infolvl,
725 	    rid, domtype);
726 	smb_lgrp_db_close(db);
727 
728 	smb_lgrp_exit();
729 	return (rc);
730 }
731 
732 /*
733  * smb_lgrp_numbydomain
734  *
735  * Returns the number of groups in the given domain in the
736  * arg 'count'
737  */
738 int
739 smb_lgrp_numbydomain(smb_domain_type_t dom_type, int *count)
740 {
741 	sqlite *db;
742 	int dom_idx;
743 	int rc;
744 
745 	switch (dom_type) {
746 	case SMB_DOMAIN_LOCAL:
747 		dom_idx = SMB_LGRP_LOCAL_IDX;
748 		break;
749 	case SMB_DOMAIN_BUILTIN:
750 		dom_idx = SMB_LGRP_BUILTIN_IDX;
751 		break;
752 	default:
753 		*count = 0;
754 		return (SMB_LGRP_INVALID_ARG);
755 	}
756 
757 	if (!smb_lgrp_enter())
758 		return (SMB_LGRP_OFFLINE);
759 
760 	db = smb_lgrp_db_open(SMB_LGRP_DB_ORD);
761 	rc = smb_lgrp_gtbl_count(db, dom_idx, count);
762 	smb_lgrp_db_close(db);
763 
764 	smb_lgrp_exit();
765 	return (rc);
766 }
767 
768 /*
769  * smb_lgrp_free
770  *
771  * Frees the allocated memory for the fields of the given
772  * group structure. Note that this function doesn't free
773  * the group itself.
774  */
775 void
776 smb_lgrp_free(smb_group_t *grp)
777 {
778 	int i;
779 
780 	if (grp == NULL)
781 		return;
782 
783 	free(grp->sg_name);
784 	free(grp->sg_cmnt);
785 	smb_sid_free(grp->sg_id.gs_sid);
786 	smb_privset_free(grp->sg_privs);
787 
788 	for (i = 0; i < grp->sg_nmembers; i++)
789 		smb_sid_free(grp->sg_members[i].gs_sid);
790 	free(grp->sg_members);
791 }
792 
793 /*
794  * smb_lgrp_iteropen
795  *
796  * Initializes the given group iterator by opening
797  * the group database and creating a virtual machine
798  * for iteration.
799  */
800 int
801 smb_lgrp_iteropen(smb_giter_t *iter)
802 {
803 	char *sql;
804 	char *errmsg = NULL;
805 	int rc = SMB_LGRP_SUCCESS;
806 
807 	assert(iter);
808 
809 	if (!smb_lgrp_enter())
810 		return (SMB_LGRP_OFFLINE);
811 
812 	bzero(iter, sizeof (smb_giter_t));
813 
814 	sql = sqlite_mprintf("SELECT * FROM groups");
815 	if (sql == NULL) {
816 		smb_lgrp_exit();
817 		return (SMB_LGRP_NO_MEMORY);
818 	}
819 
820 	iter->sgi_db = smb_lgrp_db_open(SMB_LGRP_DB_ORD);
821 	if (iter->sgi_db == NULL) {
822 		sqlite_freemem(sql);
823 		smb_lgrp_exit();
824 		return (SMB_LGRP_DBOPEN_FAILED);
825 	}
826 
827 	rc = sqlite_compile(iter->sgi_db, sql, NULL, &iter->sgi_vm, &errmsg);
828 	sqlite_freemem(sql);
829 
830 	if (rc != SQLITE_OK) {
831 		syslog(LOG_DEBUG, "failed to create a VM (%s)",
832 		    NULL_MSGCHK(errmsg));
833 		rc = SMB_LGRP_DB_ERROR;
834 	}
835 
836 	smb_lgrp_exit();
837 	return (rc);
838 }
839 
840 /*
841  * smb_lgrp_iterclose
842  *
843  * Closes the given group iterator.
844  */
845 void
846 smb_lgrp_iterclose(smb_giter_t *iter)
847 {
848 	char *errmsg = NULL;
849 	int rc;
850 
851 	assert(iter);
852 
853 	if (!smb_lgrp_enter())
854 		return;
855 
856 	rc = sqlite_finalize(iter->sgi_vm, &errmsg);
857 	if (rc != SQLITE_OK) {
858 		syslog(LOG_DEBUG, "failed to destroy a VM (%s)",
859 		    NULL_MSGCHK(errmsg));
860 	}
861 
862 	smb_lgrp_db_close(iter->sgi_db);
863 	smb_lgrp_exit();
864 }
865 
866 /*
867  * Returns B_TRUE if there has been an error during
868  * iteration.
869  */
870 boolean_t
871 smb_lgrp_itererror(smb_giter_t *iter)
872 {
873 	return (iter->sgi_nerr != 0);
874 }
875 
876 /*
877  * smb_lgrp_iterate
878  *
879  * Iterate through group database
880  * Group information is returned in provided group structure.
881  *
882  * Note that this function doesn't allocate the group
883  * structure itself only the fields, so the given grp
884  * pointer has to point to a group structure.
885  * Caller must free the allocated memories for the fields
886  * by calling smb_lgrp_free().
887  */
888 int
889 smb_lgrp_iterate(smb_giter_t *iter, smb_group_t *grp)
890 {
891 	const char **values;
892 	int ncol;
893 	int rc;
894 	int i;
895 
896 	if (iter->sgi_vm == NULL || iter->sgi_db == NULL)
897 		return (SMB_LGRP_INVALID_ARG);
898 
899 	if (!smb_lgrp_enter())
900 		return (SMB_LGRP_OFFLINE);
901 
902 	for (;;) {
903 		bzero(grp, sizeof (smb_group_t));
904 		rc = sqlite_step(iter->sgi_vm, &ncol, &values, NULL);
905 		if (rc == SQLITE_DONE) {
906 			smb_lgrp_exit();
907 			return (SMB_LGRP_NO_MORE);
908 		}
909 
910 		if (rc != SQLITE_ROW) {
911 			smb_lgrp_exit();
912 			return (SMB_LGRP_DBEXEC_FAILED);
913 		}
914 
915 		if (ncol != SMB_LGRP_GTBL_NCOL) {
916 			smb_lgrp_exit();
917 			return (SMB_LGRP_DB_ERROR);
918 		}
919 
920 		for (i = 0; i < ncol; i++) {
921 			if (values[i] == NULL) {
922 				smb_lgrp_exit();
923 				return (SMB_LGRP_DB_ERROR);
924 			}
925 		}
926 
927 		rc = smb_lgrp_decode(grp, (char **)values, SMB_LGRP_INFO_ALL,
928 		    iter->sgi_db);
929 		if (rc == SMB_LGRP_SUCCESS)
930 			break;
931 
932 		iter->sgi_nerr++;
933 		syslog(LOG_ERR, "smb_lgrp_iterate: %s", smb_lgrp_strerror(rc));
934 	}
935 
936 	smb_lgrp_exit();
937 	return (rc);
938 
939 }
940 
941 /*
942  * smb_lgrp_is_member
943  *
944  * Check to see if the specified account is a member of
945  * the given group.
946  */
947 boolean_t
948 smb_lgrp_is_member(smb_group_t *grp, smb_sid_t *sid)
949 {
950 	int i;
951 
952 	if (grp == NULL || grp->sg_members == NULL || sid == NULL)
953 		return (B_FALSE);
954 
955 	for (i = 0; i < grp->sg_nmembers; i++) {
956 		if (smb_sid_cmp(grp->sg_members[i].gs_sid, sid))
957 			return (B_TRUE);
958 	}
959 
960 	return (B_FALSE);
961 }
962 
963 /*
964  * smb_lgrp_strerror
965  *
966  * Returns a text for the given group error code.
967  */
968 char *
969 smb_lgrp_strerror(int errnum)
970 {
971 	int	i;
972 	int	nerr = (sizeof (errtab) / sizeof (errtab[0]));
973 
974 	for (i = 0; i < nerr; ++i) {
975 		if (errnum == errtab[i].errnum)
976 			return (errtab[i].errmsg);
977 	}
978 
979 	return ("unknown local group error");
980 }
981 
982 /*
983  * smb_lgrp_err_to_ntstatus
984  *
985  * This routine maps Local group operation errors to NT Status error codes.
986  */
987 uint32_t
988 smb_lgrp_err_to_ntstatus(uint32_t lgrp_err)
989 {
990 	int i;
991 	static struct err_map {
992 		uint32_t lgrp_err;
993 		uint32_t nt_status;
994 	} err_map[] = {
995 		{ SMB_LGRP_SUCCESS,		NT_STATUS_SUCCESS },
996 		{ SMB_LGRP_INVALID_ARG,		NT_STATUS_INVALID_PARAMETER },
997 		{ SMB_LGRP_INVALID_MEMBER,	NT_STATUS_INVALID_MEMBER },
998 		{ SMB_LGRP_INVALID_NAME,	NT_STATUS_INVALID_PARAMETER },
999 		{ SMB_LGRP_NOT_FOUND,		NT_STATUS_NO_SUCH_ALIAS },
1000 		{ SMB_LGRP_EXISTS,		NT_STATUS_ALIAS_EXISTS },
1001 		{ SMB_LGRP_NO_SID,		NT_STATUS_INVALID_SID },
1002 		{ SMB_LGRP_NO_LOCAL_SID,	NT_STATUS_INVALID_SID },
1003 		{ SMB_LGRP_SID_NOTLOCAL,	NT_STATUS_INVALID_SID },
1004 		{ SMB_LGRP_WKSID,		NT_STATUS_INVALID_SID },
1005 		{ SMB_LGRP_NO_MEMORY,		NT_STATUS_NO_MEMORY },
1006 		{ SMB_LGRP_DB_ERROR,		NT_STATUS_INTERNAL_DB_ERROR },
1007 		{ SMB_LGRP_DBINIT_ERROR,	NT_STATUS_INTERNAL_DB_ERROR },
1008 		{ SMB_LGRP_INTERNAL_ERROR,	NT_STATUS_INTERNAL_ERROR },
1009 		{ SMB_LGRP_MEMBER_IN_GROUP,	NT_STATUS_MEMBER_IN_ALIAS },
1010 		{ SMB_LGRP_MEMBER_NOT_IN_GROUP,	NT_STATUS_MEMBER_NOT_IN_ALIAS },
1011 		{ SMB_LGRP_NO_SUCH_PRIV,	NT_STATUS_NO_SUCH_PRIVILEGE },
1012 		{ SMB_LGRP_NO_SUCH_DOMAIN,	NT_STATUS_NO_SUCH_DOMAIN },
1013 		{ SMB_LGRP_PRIV_HELD,		NT_STATUS_SUCCESS },
1014 		{ SMB_LGRP_PRIV_NOT_HELD,	NT_STATUS_PRIVILEGE_NOT_HELD },
1015 		{ SMB_LGRP_BAD_DATA,		NT_STATUS_DATA_ERROR },
1016 		{ SMB_LGRP_NO_MORE,		NT_STATUS_NO_MORE_ENTRIES },
1017 		{ SMB_LGRP_DBOPEN_FAILED,	NT_STATUS_INTERNAL_DB_ERROR },
1018 		{ SMB_LGRP_DBEXEC_FAILED,	NT_STATUS_INTERNAL_DB_ERROR },
1019 		{ SMB_LGRP_DBINIT_FAILED,	NT_STATUS_INTERNAL_DB_ERROR },
1020 		{ SMB_LGRP_DOMLKP_FAILED,	NT_STATUS_INTERNAL_DB_ERROR },
1021 		{ SMB_LGRP_DOMINS_FAILED,	NT_STATUS_INTERNAL_DB_ERROR },
1022 		{ SMB_LGRP_INSERT_FAILED,	NT_STATUS_INTERNAL_DB_ERROR },
1023 		{ SMB_LGRP_DELETE_FAILED,	NT_STATUS_INTERNAL_DB_ERROR },
1024 		{ SMB_LGRP_UPDATE_FAILED,	NT_STATUS_INTERNAL_DB_ERROR },
1025 		{ SMB_LGRP_LOOKUP_FAILED,	NT_STATUS_INTERNAL_DB_ERROR },
1026 		{ SMB_LGRP_NOT_SUPPORTED,	NT_STATUS_NOT_SUPPORTED },
1027 		{ SMB_LGRP_OFFLINE,		NT_STATUS_INTERNAL_ERROR },
1028 		{ SMB_LGRP_POSIXCREATE_FAILED,	NT_STATUS_UNSUCCESSFUL }
1029 	};
1030 
1031 	for (i = 0; i < sizeof (err_map)/sizeof (err_map[0]); ++i) {
1032 		if (err_map[i].lgrp_err == lgrp_err)
1033 			return (err_map[i].nt_status);
1034 	}
1035 
1036 	return (NT_STATUS_INTERNAL_ERROR);
1037 }
1038 
1039 /*
1040  * smb_lgrp_chkmember
1041  *
1042  * Determines valid account types for being member of
1043  * a local group.  We really have no business trying to
1044  * keep track of the "type" of SIDs in a group, so just
1045  * validate that the SID type is a known enum value.
1046  */
1047 static boolean_t
1048 smb_lgrp_chkmember(uint16_t sid_type)
1049 {
1050 	switch (sid_type) {
1051 	case SidTypeNull:
1052 	case SidTypeUser:
1053 	case SidTypeGroup:
1054 	case SidTypeAlias:
1055 	case SidTypeWellKnownGroup:
1056 	case SidTypeDeletedAccount:
1057 	case SidTypeInvalid:
1058 	case SidTypeUnknown:
1059 		return (B_TRUE);
1060 	}
1061 	return (B_FALSE);
1062 }
1063 
1064 /*
1065  * smb_lgrp_start
1066  *
1067  * Initializes the library private global variables.
1068  * Create the database, if it doesn't exist, and add
1069  * the predefined builtin groups.
1070  */
1071 int
1072 smb_lgrp_start(void)
1073 {
1074 	static char	*builtin[] = {
1075 		"Administrators",
1076 		"Backup Operators",
1077 		"Power Users"
1078 	};
1079 	smb_wka_t	*wka;
1080 	char		*localsid;
1081 	int		i, rc;
1082 	int		ngrp = sizeof (builtin) / sizeof (builtin[0]);
1083 
1084 	(void) mutex_lock(&smb_localgrp.lg_mutex);
1085 
1086 	if ((localsid = smb_config_get_localsid()) == NULL) {
1087 		(void) mutex_unlock(&smb_localgrp.lg_mutex);
1088 		return (SMB_LGRP_NO_LOCAL_SID);
1089 	}
1090 
1091 	smb_localgrp.lg_machine_sid = smb_sid_fromstr(localsid);
1092 	free(localsid);
1093 
1094 	if (!smb_sid_isvalid(smb_localgrp.lg_machine_sid)) {
1095 		free(smb_localgrp.lg_machine_sid);
1096 		smb_localgrp.lg_machine_sid = NULL;
1097 		(void) mutex_unlock(&smb_localgrp.lg_mutex);
1098 		return (SMB_LGRP_NO_LOCAL_SID);
1099 	}
1100 
1101 	rc = smb_lgrp_db_init();
1102 	if (rc != SMB_LGRP_SUCCESS) {
1103 		free(smb_localgrp.lg_machine_sid);
1104 		smb_localgrp.lg_machine_sid = NULL;
1105 		(void) mutex_unlock(&smb_localgrp.lg_mutex);
1106 		return (rc);
1107 	}
1108 
1109 	smb_localgrp.lg_online = B_TRUE;
1110 	(void) mutex_unlock(&smb_localgrp.lg_mutex);
1111 
1112 	for (i = 0; i < ngrp; i++) {
1113 		char	*tname;
1114 
1115 		if ((wka = smb_wka_lookup_name(builtin[i])) == NULL)
1116 			continue;
1117 
1118 		if ((tname = strdup(wka->wka_name)) == NULL)
1119 			return (SMB_LGRP_NO_MEMORY);
1120 		if (!smb_lgrp_exists(tname)) {
1121 			rc = smb_lgrp_add(tname, wka->wka_desc);
1122 			if (rc != SMB_LGRP_SUCCESS) {
1123 				syslog(LOG_DEBUG, "failed to add %s",
1124 				    tname);
1125 			}
1126 		}
1127 		free(tname);
1128 	}
1129 
1130 	return (SMB_LGRP_SUCCESS);
1131 }
1132 
1133 /*
1134  * smb_lgrp_stop
1135  *
1136  * Unintialize the library global private variables.
1137  */
1138 void
1139 smb_lgrp_stop(void)
1140 {
1141 	(void) mutex_lock(&smb_localgrp.lg_mutex);
1142 	if (!smb_localgrp.lg_online)
1143 		return;
1144 
1145 	smb_localgrp.lg_online = B_FALSE;
1146 
1147 	while (smb_localgrp.lg_refcnt > 0)
1148 		(void) cond_wait(&smb_localgrp.lg_cv, &smb_localgrp.lg_mutex);
1149 
1150 	free(smb_localgrp.lg_machine_sid);
1151 	smb_localgrp.lg_machine_sid = NULL;
1152 	(void) mutex_unlock(&smb_localgrp.lg_mutex);
1153 }
1154 
1155 static boolean_t
1156 smb_lgrp_enter(void)
1157 {
1158 	boolean_t	status;
1159 
1160 	(void) mutex_lock(&smb_localgrp.lg_mutex);
1161 
1162 	status = smb_localgrp.lg_online;
1163 
1164 	if (smb_localgrp.lg_online)
1165 		++smb_localgrp.lg_refcnt;
1166 
1167 	(void) mutex_unlock(&smb_localgrp.lg_mutex);
1168 	return (status);
1169 }
1170 
1171 static void
1172 smb_lgrp_exit(void)
1173 {
1174 	(void) mutex_lock(&smb_localgrp.lg_mutex);
1175 	assert(smb_localgrp.lg_refcnt > 0);
1176 
1177 	if ((--smb_localgrp.lg_refcnt) == 0)
1178 		(void) cond_signal(&smb_localgrp.lg_cv);
1179 
1180 	(void) mutex_unlock(&smb_localgrp.lg_mutex);
1181 }
1182 
1183 /*
1184  * smb_lgrp_db_open
1185  *
1186  * Opens group database with the given mode.
1187  */
1188 static sqlite *
1189 smb_lgrp_db_open(int mode)
1190 {
1191 	sqlite *db;
1192 	char *errmsg = NULL;
1193 
1194 	db = sqlite_open(SMB_LGRP_DB_NAME, mode, &errmsg);
1195 	if (db == NULL) {
1196 		syslog(LOG_ERR, "failed to open group database (%s)",
1197 		    NULL_MSGCHK(errmsg));
1198 		sqlite_freemem(errmsg);
1199 	}
1200 
1201 	return (db);
1202 }
1203 
1204 /*
1205  * smb_lgrp_db_close
1206  *
1207  * Closes the given database handle
1208  */
1209 static void
1210 smb_lgrp_db_close(sqlite *db)
1211 {
1212 	if (db) {
1213 		sqlite_close(db);
1214 	}
1215 }
1216 
1217 /*
1218  * smb_lgrp_db_init
1219  *
1220  * Creates the group database based on the defined SQL statement.
1221  * It also initializes db_info and domain tables.
1222  */
1223 static int
1224 smb_lgrp_db_init(void)
1225 {
1226 	int dbrc = SQLITE_OK;
1227 	int rc = SMB_LGRP_SUCCESS;
1228 	sqlite *db = NULL;
1229 	char *errmsg = NULL;
1230 
1231 	db = sqlite_open(SMB_LGRP_DB_NAME, 0600, &errmsg);
1232 	if (db == NULL) {
1233 		syslog(LOG_ERR, "failed to create group database (%s)",
1234 		    NULL_MSGCHK(errmsg));
1235 		sqlite_freemem(errmsg);
1236 		return (SMB_LGRP_DBOPEN_FAILED);
1237 	}
1238 
1239 	sqlite_busy_timeout(db, SMB_LGRP_DB_TIMEOUT);
1240 	dbrc = sqlite_exec(db, "BEGIN TRANSACTION;", NULL, NULL, &errmsg);
1241 	if (dbrc != SQLITE_OK) {
1242 		syslog(LOG_DEBUG, "failed to begin database transaction (%s)",
1243 		    NULL_MSGCHK(errmsg));
1244 		sqlite_freemem(errmsg);
1245 		sqlite_close(db);
1246 		return (SMB_LGRP_DBEXEC_FAILED);
1247 	}
1248 
1249 	switch (sqlite_exec(db, SMB_LGRP_DB_SQL, NULL, NULL, &errmsg)) {
1250 	case SQLITE_ERROR:
1251 		/*
1252 		 * This is the normal situation: CREATE probably failed because
1253 		 * tables already exist. It may indicate an error in SQL as well
1254 		 * but we cannot tell.
1255 		 */
1256 		sqlite_freemem(errmsg);
1257 		dbrc = sqlite_exec(db, "ROLLBACK TRANSACTION", NULL, NULL,
1258 		    &errmsg);
1259 		rc = SMB_LGRP_SUCCESS;
1260 		break;
1261 
1262 	case SQLITE_OK:
1263 		dbrc = sqlite_exec(db, "COMMIT TRANSACTION", NULL, NULL,
1264 		    &errmsg);
1265 		if (dbrc != SQLITE_OK)
1266 			break;
1267 		rc = smb_lgrp_dtbl_insert(db, NT_BUILTIN_DOMAIN_SIDSTR,
1268 		    NULL);
1269 		if (rc == SMB_LGRP_SUCCESS)
1270 			rc = smb_lgrp_db_setinfo(db);
1271 		if (rc != SMB_LGRP_SUCCESS) {
1272 			(void) sqlite_close(db);
1273 			(void) unlink(SMB_LGRP_DB_NAME);
1274 			return (rc);
1275 		}
1276 		break;
1277 
1278 	default:
1279 		syslog(LOG_ERR,
1280 		    "failed to initialize group database (%s)", errmsg);
1281 		sqlite_freemem(errmsg);
1282 		dbrc = sqlite_exec(db, "ROLLBACK TRANSACTION", NULL, NULL,
1283 		    &errmsg);
1284 		rc = SMB_LGRP_DBINIT_FAILED;
1285 		break;
1286 	}
1287 
1288 	if (dbrc != SQLITE_OK) {
1289 		/* this is bad - database may be left in a locked state */
1290 		syslog(LOG_DEBUG, "failed to close a transaction (%s)",
1291 		    NULL_MSGCHK(errmsg));
1292 		sqlite_freemem(errmsg);
1293 	}
1294 
1295 	(void) sqlite_close(db);
1296 	return (rc);
1297 }
1298 
1299 /*
1300  * smb_lgrp_gtbl_lookup
1301  *
1302  * This is a flexible lookup function for the group database.
1303  * The key type can be specified by the 'key' arg and the actual key
1304  * values can be passed after the 'infolvl' arg. 'infolvl' arg specifies
1305  * what information items for the specified group is needed.
1306  *
1307  * Note that the function assumes the given key is unique and only
1308  * specifies one or 0 group. The keys that are supported now are
1309  * the group name and the group SID
1310  *
1311  * Note that this function doesn't allocate the group
1312  * structure itself only the fields, so the given grp
1313  * pointer has to point to a group structure.
1314  * Caller must free the allocated memories for the fields
1315  * by calling smb_lgrp_free().
1316  */
1317 static int
1318 smb_lgrp_gtbl_lookup(sqlite *db, int key, smb_group_t *grp, int infolvl, ...)
1319 {
1320 	char *errmsg = NULL;
1321 	char *sql;
1322 	char **result;
1323 	int nrow, ncol;
1324 	int rc, dom_idx;
1325 	smb_group_t grpkey;
1326 	va_list ap;
1327 
1328 	if (db == NULL)
1329 		return (SMB_LGRP_DBOPEN_FAILED);
1330 
1331 	bzero(grp, sizeof (smb_group_t));
1332 	va_start(ap, infolvl);
1333 
1334 	switch (key) {
1335 	case SMB_LGRP_GTBL_NAME:
1336 		grpkey.sg_name = va_arg(ap, char *);
1337 		sql = sqlite_mprintf("SELECT * FROM groups WHERE name = '%s'",
1338 		    grpkey.sg_name);
1339 		break;
1340 
1341 	case SMB_LGRP_GTBL_SIDRID:
1342 		grpkey.sg_rid = va_arg(ap, uint32_t);
1343 		grpkey.sg_domain = va_arg(ap, smb_domain_type_t);
1344 		if (grpkey.sg_domain == SMB_DOMAIN_LOCAL) {
1345 			dom_idx = SMB_LGRP_LOCAL_IDX;
1346 			/* need to map the given rid to a gid */
1347 			rc = smb_lgrp_getgid(grpkey.sg_rid,
1348 			    (gid_t *)&grpkey.sg_rid);
1349 			if (rc != SMB_LGRP_SUCCESS) {
1350 				va_end(ap);
1351 				return (rc);
1352 			}
1353 		} else {
1354 			dom_idx = SMB_LGRP_BUILTIN_IDX;
1355 		}
1356 
1357 		sql = sqlite_mprintf("SELECT * FROM groups "
1358 		    "WHERE (sid_idx = %d) AND (sid_rid = %u)",
1359 		    dom_idx, grpkey.sg_rid);
1360 		break;
1361 
1362 	default:
1363 		va_end(ap);
1364 		return (SMB_LGRP_INVALID_ARG);
1365 	}
1366 
1367 	va_end(ap);
1368 	if (sql == NULL)
1369 		return (SMB_LGRP_NO_MEMORY);
1370 
1371 	rc = sqlite_get_table(db, sql, &result, &nrow, &ncol, &errmsg);
1372 	sqlite_freemem(sql);
1373 
1374 	if (rc != SQLITE_OK) {
1375 		syslog(LOG_DEBUG, "failed to lookup (%s)", NULL_MSGCHK(errmsg));
1376 		sqlite_freemem(errmsg);
1377 		return (SMB_LGRP_LOOKUP_FAILED);
1378 	}
1379 
1380 	if (nrow == 0)  {
1381 		/* group not found */
1382 		sqlite_free_table(result);
1383 		return (SMB_LGRP_NOT_FOUND);
1384 	}
1385 
1386 	if (nrow != 1 || ncol != SMB_LGRP_GTBL_NCOL) {
1387 		sqlite_free_table(result);
1388 		return (SMB_LGRP_DB_ERROR);
1389 	}
1390 
1391 	rc = smb_lgrp_decode(grp, &result[SMB_LGRP_GTBL_NCOL], infolvl, db);
1392 	sqlite_free_table(result);
1393 	return (rc);
1394 }
1395 
1396 /*
1397  * smb_lgrp_gtbl_exists
1398  *
1399  * Checks to see if the given group exists or not.
1400  */
1401 static boolean_t
1402 smb_lgrp_gtbl_exists(sqlite *db, char *gname)
1403 {
1404 	char *errmsg = NULL;
1405 	char *sql;
1406 	char **result;
1407 	int nrow, ncol;
1408 	int rc;
1409 
1410 	if (db == NULL)
1411 		return (B_FALSE);
1412 
1413 	sql = sqlite_mprintf("SELECT name FROM groups WHERE name = '%s'",
1414 	    gname);
1415 	rc = sqlite_get_table(db, sql, &result, &nrow, &ncol, &errmsg);
1416 	sqlite_freemem(sql);
1417 
1418 	if (rc != SQLITE_OK) {
1419 		syslog(LOG_DEBUG, "failed to lookup %s (%s)",
1420 		    gname, NULL_MSGCHK(errmsg));
1421 		sqlite_freemem(errmsg);
1422 		return (B_FALSE);
1423 	}
1424 
1425 	sqlite_free_table(result);
1426 	return (nrow != 0);
1427 }
1428 
1429 /*
1430  * smb_lgrp_gtbl_count
1431  *
1432  * Counts the number of groups in the domain specified by
1433  * 'dom_idx'
1434  */
1435 static int
1436 smb_lgrp_gtbl_count(sqlite *db, int dom_idx, int *count)
1437 {
1438 	char *errmsg = NULL;
1439 	char *sql;
1440 	char **result;
1441 	int nrow, ncol;
1442 	int rc;
1443 
1444 	*count = 0;
1445 	if (db == NULL)
1446 		return (SMB_LGRP_DBOPEN_FAILED);
1447 
1448 	sql = sqlite_mprintf("SELECT sid_idx FROM groups WHERE sid_idx = %d",
1449 	    dom_idx);
1450 	rc = sqlite_get_table(db, sql, &result, &nrow, &ncol, &errmsg);
1451 	sqlite_freemem(sql);
1452 
1453 	if (rc != SQLITE_OK) {
1454 		syslog(LOG_DEBUG, "failed to count (%s)", NULL_MSGCHK(errmsg));
1455 		sqlite_freemem(errmsg);
1456 		return (SMB_LGRP_LOOKUP_FAILED);
1457 	}
1458 
1459 	sqlite_free_table(result);
1460 	if (ncol > 1)
1461 		return (SMB_LGRP_DB_ERROR);
1462 
1463 	*count = nrow;
1464 	return (SMB_LGRP_SUCCESS);
1465 }
1466 
1467 /*
1468  * smb_lgrp_gtbl_insert
1469  *
1470  * Insert a record for the given group in the group database.
1471  *
1472  * NOTE: this function assumes that this group has no members
1473  * at this time.
1474  */
1475 static int
1476 smb_lgrp_gtbl_insert(sqlite *db, smb_group_t *grp)
1477 {
1478 	smb_lgpid_t privs[SE_MAX_LUID + 1];
1479 	smb_lgplist_t plist;
1480 	char *errmsg = NULL;
1481 	char *sql;
1482 	int dom_idx;
1483 	int rc;
1484 
1485 	if (db == NULL)
1486 		return (SMB_LGRP_DBOPEN_FAILED);
1487 
1488 	dom_idx = (grp->sg_domain == SMB_DOMAIN_LOCAL)
1489 	    ? SMB_LGRP_LOCAL_IDX : SMB_LGRP_BUILTIN_IDX;
1490 
1491 	plist.p_cnt = SE_MAX_LUID;
1492 	plist.p_ids = privs;
1493 	smb_lgrp_encode_privset(grp, &plist);
1494 
1495 	sql = sqlite_mprintf("INSERT INTO groups "
1496 	    "(name, sid_idx, sid_rid, sid_type, sid_attrs, comment, "
1497 	    "n_privs, privs, n_members, members) "
1498 	    "VALUES('%s', %u, %u, %u, %u, '%q', %u, '%q', %u, '%q')",
1499 	    grp->sg_name, dom_idx, grp->sg_rid, grp->sg_id.gs_type,
1500 	    grp->sg_attr, (grp->sg_cmnt) ? grp->sg_cmnt : "",
1501 	    plist.p_cnt, (char *)plist.p_ids, 0, "");
1502 
1503 	if (sql == NULL)
1504 		return (SMB_LGRP_NO_MEMORY);
1505 
1506 	rc = sqlite_exec(db, sql, NULL, NULL, &errmsg);
1507 	sqlite_freemem(sql);
1508 
1509 	if (rc != SQLITE_OK) {
1510 		syslog(LOG_DEBUG, "failed to insert %s (%s)",
1511 		    grp->sg_name, NULL_MSGCHK(errmsg));
1512 		sqlite_freemem(errmsg);
1513 		rc = SMB_LGRP_INSERT_FAILED;
1514 	} else {
1515 		rc = SMB_LGRP_SUCCESS;
1516 	}
1517 
1518 	return (rc);
1519 }
1520 
1521 /*
1522  * smb_lgrp_gtbl_delete
1523  *
1524  * Removes the specified group from the database
1525  */
1526 static int
1527 smb_lgrp_gtbl_delete(sqlite *db, char *gname)
1528 {
1529 	char *errmsg = NULL;
1530 	char *sql;
1531 	int rc;
1532 
1533 	if (db == NULL)
1534 		return (SMB_LGRP_DBOPEN_FAILED);
1535 
1536 	sql = sqlite_mprintf("DELETE FROM groups WHERE name = '%s'", gname);
1537 	if (sql == NULL)
1538 		return (SMB_LGRP_NO_MEMORY);
1539 
1540 	rc = sqlite_exec(db, sql, NULL, NULL, &errmsg);
1541 	sqlite_freemem(sql);
1542 
1543 	if (rc != SQLITE_OK) {
1544 		syslog(LOG_DEBUG, "failed to delete %s (%s)",
1545 		    gname, NULL_MSGCHK(errmsg));
1546 		sqlite_freemem(errmsg);
1547 		rc = SMB_LGRP_DELETE_FAILED;
1548 	} else {
1549 		rc = SMB_LGRP_SUCCESS;
1550 	}
1551 
1552 	return (rc);
1553 }
1554 
1555 /*
1556  * smb_lgrp_gtbl_update
1557  *
1558  * Updates the specified group information, the supported items
1559  * are group name and comment
1560  */
1561 static int
1562 smb_lgrp_gtbl_update(sqlite *db, char *gname, smb_group_t *grp, int col_id)
1563 {
1564 	char *errmsg = NULL;
1565 	char *sql;
1566 	int rc;
1567 
1568 	if (db == NULL)
1569 		return (SMB_LGRP_DBOPEN_FAILED);
1570 
1571 	/* UPDATE doesn't fail if gname doesn't exist */
1572 	if (!smb_lgrp_gtbl_exists(db, gname))
1573 		return (SMB_LGRP_NOT_FOUND);
1574 
1575 	switch (col_id) {
1576 	case SMB_LGRP_GTBL_NAME:
1577 		if (smb_lgrp_gtbl_exists(db, grp->sg_name))
1578 			return (SMB_LGRP_EXISTS);
1579 		sql = sqlite_mprintf("UPDATE groups SET name = '%s' "
1580 		    "WHERE name = '%s'", grp->sg_name, gname);
1581 		break;
1582 
1583 	case SMB_LGRP_GTBL_CMNT:
1584 		sql = sqlite_mprintf("UPDATE groups SET comment = '%q' "
1585 		"WHERE name = '%s'", grp->sg_cmnt, gname);
1586 		break;
1587 
1588 	default:
1589 		return (SMB_LGRP_INVALID_ARG);
1590 	}
1591 
1592 	if (sql == NULL)
1593 		return (SMB_LGRP_NO_MEMORY);
1594 
1595 	rc = sqlite_exec(db, sql, NULL, NULL, &errmsg);
1596 	sqlite_freemem(sql);
1597 
1598 	if (rc != SQLITE_OK) {
1599 		syslog(LOG_DEBUG, "failed to update %s (%s)",
1600 		    gname, NULL_MSGCHK(errmsg));
1601 		sqlite_freemem(errmsg);
1602 		rc = SMB_LGRP_UPDATE_FAILED;
1603 	} else {
1604 		rc = SMB_LGRP_SUCCESS;
1605 	}
1606 
1607 	return (rc);
1608 }
1609 
1610 /*
1611  * smb_lgrp_gtbl_update_mlist
1612  *
1613  * Adds/removes the specified member from the member list of the
1614  * given group
1615  */
1616 static int
1617 smb_lgrp_gtbl_update_mlist(sqlite *db, char *gname, smb_gsid_t *member,
1618     int flags)
1619 {
1620 	smb_lgmlist_t new_members;
1621 	smb_lgmlist_t members;
1622 	smb_lgmid_t mid;
1623 	char *errmsg = NULL;
1624 	char *sql;
1625 	char **result;
1626 	int nrow, ncol;
1627 	int rc;
1628 
1629 	if (db == NULL)
1630 		return (SMB_LGRP_DBOPEN_FAILED);
1631 
1632 	sql = sqlite_mprintf("SELECT n_members, members FROM groups "
1633 	    "WHERE name = '%s'", gname);
1634 
1635 	if (sql == NULL)
1636 		return (SMB_LGRP_NO_MEMORY);
1637 
1638 	rc = sqlite_get_table(db, sql, &result, &nrow, &ncol, &errmsg);
1639 	sqlite_freemem(sql);
1640 
1641 	if (rc != SQLITE_OK) {
1642 		syslog(LOG_DEBUG, "failed to lookup %s (%s)",
1643 		    gname, NULL_MSGCHK(errmsg));
1644 		sqlite_freemem(errmsg);
1645 		return (SMB_LGRP_LOOKUP_FAILED);
1646 	}
1647 
1648 	if (nrow == 0)  {
1649 		/* group not found */
1650 		sqlite_free_table(result);
1651 		return (SMB_LGRP_NOT_FOUND);
1652 	}
1653 
1654 	if (nrow != 1 || ncol != 2) {
1655 		sqlite_free_table(result);
1656 		return (SMB_LGRP_DB_ERROR);
1657 	}
1658 
1659 	bzero(&mid, sizeof (mid));
1660 	mid.m_type = member->gs_type;
1661 	rc = smb_lgrp_dtbl_getidx(db, member->gs_sid, mid.m_type,
1662 	    &mid.m_idx, &mid.m_rid);
1663 	if (rc != SMB_LGRP_SUCCESS) {
1664 		sqlite_free_table(result);
1665 		return (rc);
1666 	}
1667 
1668 	members.m_cnt = atoi(result[2]);
1669 	members.m_ids = result[3];
1670 
1671 	switch (flags) {
1672 	case SMB_LGRP_DB_ADDMEMBER:
1673 		rc = smb_lgrp_mlist_add(&members, &mid, &new_members);
1674 		break;
1675 	case SMB_LGRP_DB_DELMEMBER:
1676 		rc = smb_lgrp_mlist_del(&members, &mid, &new_members);
1677 		break;
1678 	default:
1679 		rc = SMB_LGRP_INVALID_ARG;
1680 	}
1681 
1682 	sqlite_free_table(result);
1683 	if (rc != SMB_LGRP_SUCCESS)
1684 		return (rc);
1685 
1686 	sql = sqlite_mprintf("UPDATE groups SET n_members = %u, members = '%s'"
1687 	    " WHERE name = '%s'", new_members.m_cnt, new_members.m_ids, gname);
1688 
1689 	free(new_members.m_ids);
1690 
1691 	if (sql == NULL)
1692 		return (SMB_LGRP_NO_MEMORY);
1693 
1694 	rc = sqlite_exec(db, sql, NULL, NULL, &errmsg);
1695 	sqlite_freemem(sql);
1696 
1697 	if (rc != SQLITE_OK) {
1698 		syslog(LOG_DEBUG, "failed to update %s (%s)", gname,
1699 		    NULL_MSGCHK(errmsg));
1700 		sqlite_freemem(errmsg);
1701 		rc = SMB_LGRP_UPDATE_FAILED;
1702 	} else {
1703 		rc = SMB_LGRP_SUCCESS;
1704 	}
1705 
1706 	return (rc);
1707 }
1708 
1709 /*
1710  * smb_lgrp_gtbl_update_plist
1711  *
1712  * Adds/removes the specified privilege from the privilege list of the
1713  * given group
1714  */
1715 static int
1716 smb_lgrp_gtbl_update_plist(sqlite *db, char *gname, uint8_t priv_id,
1717     boolean_t enable)
1718 {
1719 	char *sql;
1720 	char *errmsg = NULL;
1721 	char **result;
1722 	int nrow, ncol;
1723 	int rc;
1724 	smb_lgplist_t privs;
1725 	smb_lgplist_t new_privs;
1726 
1727 	if (db == NULL)
1728 		return (SMB_LGRP_DBOPEN_FAILED);
1729 
1730 	sql = sqlite_mprintf("SELECT n_privs, privs FROM groups "
1731 	    "WHERE name = '%s'", gname);
1732 
1733 	if (sql == NULL)
1734 		return (SMB_LGRP_NO_MEMORY);
1735 
1736 	rc = sqlite_get_table(db, sql, &result, &nrow, &ncol, &errmsg);
1737 	sqlite_freemem(sql);
1738 
1739 	if (rc != SQLITE_OK) {
1740 		syslog(LOG_DEBUG, "failed to lookup %s (%s)",
1741 		    gname, NULL_MSGCHK(errmsg));
1742 		sqlite_freemem(errmsg);
1743 		return (SMB_LGRP_LOOKUP_FAILED);
1744 	}
1745 
1746 	if (nrow == 0)  {
1747 		/* group not found */
1748 		sqlite_free_table(result);
1749 		return (SMB_LGRP_NOT_FOUND);
1750 	}
1751 
1752 	if (nrow != 1 || ncol != 2) {
1753 		sqlite_free_table(result);
1754 		return (SMB_LGRP_DB_ERROR);
1755 	}
1756 
1757 	privs.p_cnt = atoi(result[2]);
1758 	privs.p_ids = (smb_lgpid_t *)result[3];
1759 
1760 	if (enable)
1761 		rc = smb_lgrp_plist_add(&privs, priv_id, &new_privs);
1762 	else
1763 		rc = smb_lgrp_plist_del(&privs, priv_id, &new_privs);
1764 
1765 	sqlite_free_table(result);
1766 	if (rc != SMB_LGRP_SUCCESS)
1767 		return (rc);
1768 
1769 	sql = sqlite_mprintf("UPDATE groups SET n_privs = %u, privs = '%q'"
1770 	    " WHERE name = '%s'", new_privs.p_cnt, (char *)new_privs.p_ids,
1771 	    gname);
1772 
1773 	free(new_privs.p_ids);
1774 
1775 	if (sql == NULL)
1776 		return (SMB_LGRP_NO_MEMORY);
1777 
1778 	rc = sqlite_exec(db, sql, NULL, NULL, &errmsg);
1779 	sqlite_freemem(sql);
1780 
1781 	if (rc != SQLITE_OK) {
1782 		syslog(LOG_DEBUG, "failed to update %s (%s)",
1783 		    gname, NULL_MSGCHK(errmsg));
1784 		sqlite_freemem(errmsg);
1785 		rc = SMB_LGRP_UPDATE_FAILED;
1786 	} else {
1787 		rc = SMB_LGRP_SUCCESS;
1788 	}
1789 
1790 	return (rc);
1791 }
1792 
1793 /*
1794  * smb_lgrp_dtbl_insert
1795  *
1796  * Inserts the specified domain SID in the dmain table.
1797  * Upon successful insert the index will be returned in
1798  * 'dom_idx' arg.
1799  */
1800 static int
1801 smb_lgrp_dtbl_insert(sqlite *db, char *dom_sid, uint32_t *dom_idx)
1802 {
1803 	char *errmsg = NULL;
1804 	char *sql;
1805 	int rc;
1806 
1807 	sql = sqlite_mprintf("INSERT INTO domains (dom_sid, dom_cnt)"
1808 	    " VALUES('%s', 1);", dom_sid);
1809 	if (sql == NULL)
1810 		return (SMB_LGRP_NO_MEMORY);
1811 
1812 	rc = sqlite_exec(db, sql, NULL, NULL, &errmsg);
1813 	sqlite_freemem(sql);
1814 
1815 	if (rc != SQLITE_OK) {
1816 		syslog(LOG_DEBUG, "failed to insert domain SID (%s)",
1817 		    NULL_MSGCHK(errmsg));
1818 		sqlite_freemem(errmsg);
1819 		return (SMB_LGRP_DOMINS_FAILED);
1820 	}
1821 
1822 	if (dom_idx)
1823 		*dom_idx = sqlite_last_insert_rowid(db);
1824 	return (SMB_LGRP_SUCCESS);
1825 }
1826 
1827 /*
1828  * smb_lgrp_dtbl_getidx
1829  *
1830  * Searches the domain table for the domain SID of the
1831  * given member SID. If it finds the domain SID it'll
1832  * return the index and the RID, otherwise it'll insert
1833  * it in the domain table as a new SID.
1834  */
1835 static int
1836 smb_lgrp_dtbl_getidx(sqlite *db, smb_sid_t *sid, uint16_t sid_type,
1837     uint32_t *dom_idx, uint32_t *rid)
1838 {
1839 	char sidstr[SMB_SID_STRSZ];
1840 	smb_sid_t *dom_sid;
1841 	char **result;
1842 	int nrow, ncol;
1843 	char *errmsg = NULL;
1844 	char *sql;
1845 	int rc;
1846 
1847 	if (smb_sid_indomain(smb_localgrp.lg_machine_sid, sid)) {
1848 		/* This is a local SID */
1849 		int id_type = (sid_type == SidTypeUser)
1850 		    ? SMB_IDMAP_USER : SMB_IDMAP_GROUP;
1851 		*dom_idx = SMB_LGRP_LOCAL_IDX;
1852 		if (smb_idmap_getid(sid, rid, &id_type) != IDMAP_SUCCESS)
1853 			return (SMB_LGRP_INTERNAL_ERROR);
1854 
1855 		return (SMB_LGRP_SUCCESS);
1856 	}
1857 
1858 	if ((dom_sid = smb_sid_split(sid, rid)) == NULL)
1859 		return (SMB_LGRP_NO_MEMORY);
1860 
1861 	smb_sid_tostr(dom_sid, sidstr);
1862 	free(dom_sid);
1863 
1864 	sql = sqlite_mprintf("SELECT dom_idx FROM domains WHERE dom_sid = '%s'",
1865 	    sidstr);
1866 	if (sql == NULL)
1867 		return (SMB_LGRP_NO_MEMORY);
1868 
1869 	rc = sqlite_get_table(db, sql, &result, &nrow, &ncol, &errmsg);
1870 	sqlite_freemem(sql);
1871 
1872 	if (rc != SQLITE_OK) {
1873 		syslog(LOG_DEBUG, "failed to lookup domain SID (%s)",
1874 		    NULL_MSGCHK(errmsg));
1875 		sqlite_freemem(errmsg);
1876 		return (SMB_LGRP_DOMLKP_FAILED);
1877 	}
1878 
1879 	switch (nrow) {
1880 	case 0:
1881 		/* new domain SID; insert it into the domains table */
1882 		sqlite_free_table(result);
1883 		return (smb_lgrp_dtbl_insert(db, sidstr, dom_idx));
1884 
1885 	case 1:
1886 		*dom_idx = atoi(result[1]);
1887 		sqlite_free_table(result);
1888 		return (SMB_LGRP_SUCCESS);
1889 	}
1890 
1891 	sqlite_free_table(result);
1892 	return (SMB_LGRP_DB_ERROR);
1893 }
1894 
1895 /*
1896  * smb_lgrp_dtbl_getsid
1897  *
1898  * Searchs the domain table for the given domain index.
1899  * Converts the found domain SID to binary format and
1900  * returns it in the 'sid' arg.
1901  *
1902  * Caller must free the returned SID by calling free().
1903  */
1904 static int
1905 smb_lgrp_dtbl_getsid(sqlite *db, uint32_t dom_idx, smb_sid_t **sid)
1906 {
1907 	char **result;
1908 	int nrow, ncol;
1909 	char *errmsg = NULL;
1910 	char *sql;
1911 	int rc;
1912 
1913 	sql = sqlite_mprintf("SELECT dom_sid FROM domains WHERE dom_idx = %u",
1914 	    dom_idx);
1915 	if (sql == NULL)
1916 		return (SMB_LGRP_NO_MEMORY);
1917 
1918 	rc = sqlite_get_table(db, sql, &result, &nrow, &ncol, &errmsg);
1919 	sqlite_freemem(sql);
1920 
1921 	if (rc != SQLITE_OK) {
1922 		syslog(LOG_DEBUG, "failed to lookup domain index (%s)",
1923 		    NULL_MSGCHK(errmsg));
1924 		sqlite_freemem(errmsg);
1925 		return (SMB_LGRP_DOMLKP_FAILED);
1926 	}
1927 
1928 	switch (nrow) {
1929 	case 0:
1930 		rc = SMB_LGRP_NO_SUCH_DOMAIN;
1931 		break;
1932 
1933 	case 1:
1934 		*sid = smb_sid_fromstr(result[1]);
1935 		rc = (*sid == NULL)
1936 		    ? SMB_LGRP_INTERNAL_ERROR : SMB_LGRP_SUCCESS;
1937 		break;
1938 
1939 	default:
1940 		rc = SMB_LGRP_DB_ERROR;
1941 		break;
1942 	}
1943 
1944 	sqlite_free_table(result);
1945 	return (rc);
1946 }
1947 
1948 /*
1949  * smb_lgrp_db_setinfo
1950  *
1951  * Initializes the db_info table upon database creation.
1952  */
1953 static int
1954 smb_lgrp_db_setinfo(sqlite *db)
1955 {
1956 	char *errmsg = NULL;
1957 	char *sql;
1958 	int rc;
1959 
1960 	sql = sqlite_mprintf("INSERT INTO db_info (ver_major, ver_minor,"
1961 	    " magic) VALUES (%d, %d, %u)", SMB_LGRP_DB_VERMAJOR,
1962 	    SMB_LGRP_DB_VERMINOR, SMB_LGRP_DB_MAGIC);
1963 
1964 	if (sql == NULL)
1965 		return (SMB_LGRP_NO_MEMORY);
1966 
1967 	rc = sqlite_exec(db, sql, NULL, NULL, &errmsg);
1968 	sqlite_freemem(sql);
1969 	if (rc != SQLITE_OK) {
1970 		syslog(LOG_DEBUG, "failed to insert database information (%s)",
1971 		    NULL_MSGCHK(errmsg));
1972 		sqlite_freemem(errmsg);
1973 		rc = SMB_LGRP_DBINIT_ERROR;
1974 	} else {
1975 		rc = SMB_LGRP_SUCCESS;
1976 	}
1977 
1978 	return (rc);
1979 }
1980 
1981 /*
1982  * smb_lgrp_mlist_add
1983  *
1984  * Adds the given member (newm) to the input member list (in_members)
1985  * if it's not already there. The result list will be returned in
1986  * out_members. The caller must free the allocated memory for
1987  * out_members by calling free().
1988  *
1989  * in_members and out_members are hex strings.
1990  */
1991 static int
1992 smb_lgrp_mlist_add(smb_lgmlist_t *in_members, smb_lgmid_t *newm,
1993     smb_lgmlist_t *out_members)
1994 {
1995 	char mid_hex[SMB_LGRP_MID_HEXSZ];
1996 	char *in_list;
1997 	char *out_list;
1998 	int in_size;
1999 	int out_size;
2000 	int mid_hexsz;
2001 	int i;
2002 
2003 	out_members->m_cnt = 0;
2004 	out_members->m_ids = NULL;
2005 
2006 	bzero(mid_hex, sizeof (mid_hex));
2007 	mid_hexsz = bintohex((const char *)newm, sizeof (smb_lgmid_t),
2008 	    mid_hex, sizeof (mid_hex));
2009 
2010 	/*
2011 	 * Check to see if this is already a group member
2012 	 */
2013 	in_list = in_members->m_ids;
2014 	for (i = 0; i < in_members->m_cnt; i++) {
2015 		if (strncmp(in_list, mid_hex, mid_hexsz) == 0)
2016 			return (SMB_LGRP_MEMBER_IN_GROUP);
2017 		in_list += mid_hexsz;
2018 	}
2019 
2020 	in_size = (in_members->m_ids) ? strlen(in_members->m_ids) : 0;
2021 	out_size = in_size + sizeof (mid_hex) + 1;
2022 	out_list = malloc(out_size);
2023 	if (out_list == NULL)
2024 		return (SMB_LGRP_NO_MEMORY);
2025 
2026 	bzero(out_list, out_size);
2027 	if (in_members->m_ids)
2028 		(void) strlcpy(out_list, in_members->m_ids, out_size);
2029 	(void) strcat(out_list, mid_hex);
2030 
2031 	out_members->m_cnt = in_members->m_cnt + 1;
2032 	out_members->m_ids = out_list;
2033 
2034 	return (SMB_LGRP_SUCCESS);
2035 }
2036 
2037 /*
2038  * smb_lgrp_mlist_del
2039  *
2040  * Removes the given member (msid) from the input member list
2041  * (in_members) if it's already there. The result list will b
2042  * returned in out_members. The caller must free the allocated
2043  * memory for out_members by calling free().
2044  *
2045  * in_members and out_members are hex strings.
2046  *
2047  * Note that we ignore the SID "type" when matching because
2048  * we always want to delete when the SID part matches.
2049  * The "type" part can be fiction.
2050  */
2051 static int
2052 smb_lgrp_mlist_del(smb_lgmlist_t *in_members, smb_lgmid_t *mid,
2053     smb_lgmlist_t *out_members)
2054 {
2055 	char mid_hex[SMB_LGRP_MID_HEXSZ];
2056 	char *in_list;
2057 	char *out_list;
2058 	int in_size;
2059 	int out_size;
2060 	int mid_hexsz;
2061 	int out_cnt;
2062 	int i;
2063 
2064 	out_members->m_cnt = 0;
2065 	out_members->m_ids = NULL;
2066 
2067 	if ((in_members == NULL) || (in_members->m_cnt == 0))
2068 		return (SMB_LGRP_MEMBER_NOT_IN_GROUP);
2069 
2070 	in_size = strlen(in_members->m_ids);
2071 	out_size = in_size + sizeof (mid_hex) + 1;
2072 	out_list = malloc(out_size);
2073 	if (out_list == NULL)
2074 		return (SMB_LGRP_NO_MEMORY);
2075 
2076 	*out_list = '\0';
2077 
2078 	bzero(mid_hex, sizeof (mid_hex));
2079 	mid_hexsz = bintohex((const char *)mid, sizeof (smb_lgmid_t),
2080 	    mid_hex, sizeof (mid_hex));
2081 
2082 	in_list = in_members->m_ids;
2083 	for (i = 0, out_cnt = 0; i < in_members->m_cnt; i++) {
2084 		/* Keep only those NOT matching in IDX,RID */
2085 		if (strncmp(in_list, mid_hex, SMB_LGRP_IDXRID_LEN)) {
2086 			(void) strncat(out_list, in_list, mid_hexsz);
2087 			out_cnt++;
2088 		}
2089 		in_list += mid_hexsz;
2090 	}
2091 
2092 	if (out_cnt == in_members->m_cnt) {
2093 		free(out_list);
2094 		return (SMB_LGRP_MEMBER_NOT_IN_GROUP);
2095 	}
2096 
2097 	out_members->m_cnt = out_cnt;
2098 	out_members->m_ids = out_list;
2099 	return (SMB_LGRP_SUCCESS);
2100 }
2101 
2102 /*
2103  * smb_lgrp_plist_add
2104  *
2105  * Adds the given privilege to the input list (in_privs)
2106  * if it's not already there. The result list is returned
2107  * in out_privs. The caller must free the allocated memory
2108  * for out_privs by calling free().
2109  */
2110 static int
2111 smb_lgrp_plist_add(smb_lgplist_t *in_privs, smb_lgpid_t priv_id,
2112     smb_lgplist_t *out_privs)
2113 {
2114 	int i, size;
2115 	smb_lgpid_t *pbuf;
2116 
2117 	out_privs->p_cnt = 0;
2118 	out_privs->p_ids = NULL;
2119 
2120 	for (i = 0; i < in_privs->p_cnt; i++) {
2121 		if (in_privs->p_ids[i] == priv_id)
2122 			return (SMB_LGRP_PRIV_HELD);
2123 	}
2124 
2125 	size = (in_privs->p_cnt + 1) * sizeof (smb_lgpid_t) + 1;
2126 	pbuf = malloc(size);
2127 	if (pbuf == NULL)
2128 		return (SMB_LGRP_NO_MEMORY);
2129 
2130 	bzero(pbuf, size);
2131 	bcopy(in_privs->p_ids, pbuf, in_privs->p_cnt * sizeof (smb_lgpid_t));
2132 	pbuf[in_privs->p_cnt] = priv_id;
2133 
2134 	out_privs->p_cnt = in_privs->p_cnt + 1;
2135 	out_privs->p_ids = pbuf;
2136 
2137 	return (SMB_LGRP_SUCCESS);
2138 }
2139 
2140 /*
2141  * smb_lgrp_plist_del
2142  *
2143  * Removes the given privilege from the input list (in_privs)
2144  * if it's already there. The result list is returned
2145  * in out_privs. The caller must free the allocated memory
2146  * for out_privs by calling free().
2147  */
2148 static int
2149 smb_lgrp_plist_del(smb_lgplist_t *in_privs, smb_lgpid_t priv_id,
2150     smb_lgplist_t *out_privs)
2151 {
2152 	int i, size;
2153 
2154 	out_privs->p_cnt = 0;
2155 	out_privs->p_ids = NULL;
2156 
2157 	if ((in_privs == NULL) || (in_privs->p_cnt == 0))
2158 		return (SMB_LGRP_PRIV_NOT_HELD);
2159 
2160 	size = (in_privs->p_cnt - 1) * sizeof (smb_lgpid_t) + 1;
2161 	out_privs->p_ids = malloc(size);
2162 	if (out_privs->p_ids == NULL)
2163 		return (SMB_LGRP_NO_MEMORY);
2164 
2165 	bzero(out_privs->p_ids, size);
2166 
2167 	for (i = 0; i < in_privs->p_cnt; i++) {
2168 		if (in_privs->p_ids[i] != priv_id)
2169 			out_privs->p_ids[out_privs->p_cnt++] =
2170 			    in_privs->p_ids[i];
2171 	}
2172 
2173 	if (out_privs->p_cnt == in_privs->p_cnt) {
2174 		free(out_privs->p_ids);
2175 		out_privs->p_cnt = 0;
2176 		out_privs->p_ids = NULL;
2177 		return (SMB_LGRP_PRIV_NOT_HELD);
2178 	}
2179 
2180 	return (SMB_LGRP_SUCCESS);
2181 }
2182 
2183 /*
2184  * smb_lgrp_encode_privset
2185  *
2186  * Encodes given privilege set into a buffer to be stored in the group
2187  * database. Each entry of the encoded buffer contains the privilege ID
2188  * of an enable privilege. The returned buffer is null-terminated.
2189  */
2190 static void
2191 smb_lgrp_encode_privset(smb_group_t *grp, smb_lgplist_t *plist)
2192 {
2193 	smb_privset_t *privs;
2194 	uint32_t pcnt = plist->p_cnt;
2195 	int i;
2196 
2197 	bzero(plist->p_ids, sizeof (smb_lgpid_t) * plist->p_cnt);
2198 	plist->p_cnt = 0;
2199 
2200 	privs = grp->sg_privs;
2201 	if ((privs == NULL) || (privs->priv_cnt == 0))
2202 		return;
2203 
2204 	if (pcnt < privs->priv_cnt) {
2205 		assert(0);
2206 	}
2207 
2208 	for (i = 0; i < privs->priv_cnt; i++) {
2209 		if (privs->priv[i].attrs == SE_PRIVILEGE_ENABLED) {
2210 			plist->p_ids[plist->p_cnt++] =
2211 			    (uint8_t)privs->priv[i].luid.lo_part;
2212 		}
2213 	}
2214 }
2215 
2216 /*
2217  * smb_lgrp_decode_privset
2218  *
2219  * Decodes the privilege information read from group table
2220  * (nprivs, privs) into a binray format specified by the
2221  * privilege field of smb_group_t
2222  */
2223 static int
2224 smb_lgrp_decode_privset(smb_group_t *grp, char *nprivs, char *privs)
2225 {
2226 	smb_lgplist_t plist;
2227 	int i;
2228 
2229 	plist.p_cnt = atoi(nprivs);
2230 	if (strlen(privs) != plist.p_cnt)
2231 		return (SMB_LGRP_BAD_DATA);
2232 
2233 	plist.p_ids = (smb_lgpid_t *)privs;
2234 	grp->sg_privs = smb_privset_new();
2235 	if (grp->sg_privs == NULL)
2236 		return (SMB_LGRP_NO_MEMORY);
2237 
2238 	for (i = 0; i < plist.p_cnt; i++)
2239 		smb_privset_enable(grp->sg_privs, plist.p_ids[i]);
2240 
2241 	return (SMB_LGRP_SUCCESS);
2242 }
2243 
2244 /*
2245  * smb_lgrp_decode_members
2246  *
2247  * Decodes the members information read from group table
2248  * (nmembers, members) into a binary format specified by the
2249  * member fields of smb_group_t
2250  */
2251 static int
2252 smb_lgrp_decode_members(smb_group_t *grp, char *nmembers, char *members,
2253     sqlite *db)
2254 {
2255 	smb_lgmid_t *m_id;
2256 	smb_lgmid_t *m_ids;
2257 	smb_gsid_t *m_sid;
2258 	smb_gsid_t *m_sids;
2259 	int m_num;
2260 	int mids_size;
2261 	int i, rc;
2262 
2263 	grp->sg_nmembers = 0;
2264 	grp->sg_members = NULL;
2265 
2266 	m_num = atoi(nmembers);
2267 	mids_size = m_num * sizeof (smb_lgmid_t);
2268 	if ((m_ids = malloc(mids_size)) == NULL)
2269 		return (SMB_LGRP_NO_MEMORY);
2270 
2271 	m_sids = calloc(m_num, sizeof (smb_gsid_t));
2272 	if (m_sids == NULL) {
2273 		free(m_ids);
2274 		return (SMB_LGRP_NO_MEMORY);
2275 	}
2276 
2277 	(void) hextobin(members, strlen(members), (char *)m_ids, mids_size);
2278 
2279 	m_id = m_ids;
2280 	m_sid = m_sids;
2281 	for (i = 0; i < m_num; i++, m_id++, m_sid++) {
2282 		rc = smb_lgrp_getsid(m_id->m_idx, &m_id->m_rid, m_id->m_type,
2283 		    db, &m_sid->gs_sid);
2284 
2285 		if (rc != SMB_LGRP_SUCCESS) {
2286 			free(m_ids);
2287 			for (m_sid = m_sids; m_sid->gs_sid != NULL; m_sid++)
2288 				smb_sid_free(m_sid->gs_sid);
2289 			free(m_sids);
2290 			return (rc);
2291 		}
2292 
2293 		m_sid->gs_type = m_id->m_type;
2294 	}
2295 
2296 	free(m_ids);
2297 
2298 	grp->sg_nmembers = m_num;
2299 	grp->sg_members = m_sids;
2300 	return (SMB_LGRP_SUCCESS);
2301 }
2302 
2303 /*
2304  * smb_lgrp_decode
2305  *
2306  * Fills out the fields of the given group (grp) based in the
2307  * string information read from the group table. infolvl determines
2308  * which fields are requested and need to be decoded.
2309  *
2310  * Allocated memories must be freed by calling smb_lgrp_free()
2311  * upon successful return.
2312  */
2313 static int
2314 smb_lgrp_decode(smb_group_t *grp, char **values, int infolvl, sqlite *db)
2315 {
2316 	uint32_t sid_idx;
2317 	int rc;
2318 
2319 	if (infolvl == SMB_LGRP_INFO_NONE)
2320 		return (SMB_LGRP_SUCCESS);
2321 
2322 	if (infolvl & SMB_LGRP_INFO_NAME) {
2323 		grp->sg_name = strdup(values[SMB_LGRP_GTBL_NAME]);
2324 		if (grp->sg_name == NULL)
2325 			return (SMB_LGRP_NO_MEMORY);
2326 	}
2327 
2328 	if (infolvl & SMB_LGRP_INFO_CMNT) {
2329 		grp->sg_cmnt = strdup(values[SMB_LGRP_GTBL_CMNT]);
2330 		if (grp->sg_cmnt == NULL) {
2331 			smb_lgrp_free(grp);
2332 			return (SMB_LGRP_NO_MEMORY);
2333 		}
2334 	}
2335 
2336 
2337 	if (infolvl & SMB_LGRP_INFO_SID) {
2338 		sid_idx = atoi(values[SMB_LGRP_GTBL_SIDIDX]);
2339 		grp->sg_rid = atoi(values[SMB_LGRP_GTBL_SIDRID]);
2340 		grp->sg_attr = atoi(values[SMB_LGRP_GTBL_SIDATR]);
2341 		grp->sg_id.gs_type = atoi(values[SMB_LGRP_GTBL_SIDTYP]);
2342 		rc = smb_lgrp_getsid(sid_idx, &grp->sg_rid, grp->sg_id.gs_type,
2343 		    db, &grp->sg_id.gs_sid);
2344 		if (rc != SMB_LGRP_SUCCESS) {
2345 			smb_lgrp_free(grp);
2346 			return (rc);
2347 		}
2348 		grp->sg_domain = (sid_idx == SMB_LGRP_LOCAL_IDX)
2349 		    ? SMB_DOMAIN_LOCAL : SMB_DOMAIN_BUILTIN;
2350 	}
2351 
2352 	if (infolvl & SMB_LGRP_INFO_PRIV) {
2353 		rc = smb_lgrp_decode_privset(grp, values[SMB_LGRP_GTBL_NPRIVS],
2354 		    values[SMB_LGRP_GTBL_PRIVS]);
2355 
2356 		if (rc != SMB_LGRP_SUCCESS) {
2357 			smb_lgrp_free(grp);
2358 			return (rc);
2359 		}
2360 	}
2361 
2362 	if (infolvl & SMB_LGRP_INFO_MEMB) {
2363 		rc = smb_lgrp_decode_members(grp, values[SMB_LGRP_GTBL_NMEMBS],
2364 		    values[SMB_LGRP_GTBL_MEMBS], db);
2365 		if (rc != SMB_LGRP_SUCCESS) {
2366 			smb_lgrp_free(grp);
2367 			return (rc);
2368 		}
2369 	}
2370 
2371 	return (SMB_LGRP_SUCCESS);
2372 }
2373 
2374 /*
2375  * smb_lgrp_normalize_name
2376  *
2377  * Trim whitespace, validate the group name and convert it to lowercase.
2378  */
2379 static boolean_t
2380 smb_lgrp_normalize_name(char *name)
2381 {
2382 	(void) trim_whitespace(name);
2383 
2384 	if (smb_name_validate_account(name) != ERROR_SUCCESS)
2385 		return (B_FALSE);
2386 
2387 	(void) smb_strlwr(name);
2388 	return (B_TRUE);
2389 }
2390 
2391 /*
2392  * smb_lgrp_set_default_privs
2393  *
2394  * set default privileges for Administrators and Backup Operators
2395  */
2396 static void
2397 smb_lgrp_set_default_privs(smb_group_t *grp)
2398 {
2399 	if (smb_strcasecmp(grp->sg_name, "Administrators", 0) == 0) {
2400 		smb_privset_enable(grp->sg_privs, SE_TAKE_OWNERSHIP_LUID);
2401 		smb_privset_enable(grp->sg_privs, SE_BACKUP_LUID);
2402 		smb_privset_enable(grp->sg_privs, SE_RESTORE_LUID);
2403 		return;
2404 	}
2405 
2406 	if (smb_strcasecmp(grp->sg_name, "Backup Operators", 0) == 0) {
2407 		smb_privset_enable(grp->sg_privs, SE_BACKUP_LUID);
2408 		smb_privset_enable(grp->sg_privs, SE_RESTORE_LUID);
2409 		return;
2410 	}
2411 }
2412 
2413 /*
2414  * smb_lgrp_getsid
2415  *
2416  * Returns a SID based on the provided information
2417  * If dom_idx is 0, it means 'rid' contains a UID/GID and the
2418  * returned SID will be a local SID. If dom_idx is not 0 then
2419  * the domain SID will be fetched from the domain table.
2420  */
2421 static int
2422 smb_lgrp_getsid(int dom_idx, uint32_t *rid, uint16_t sid_type,
2423     sqlite *db, smb_sid_t **sid)
2424 {
2425 	smb_sid_t *dom_sid = NULL;
2426 	smb_sid_t *res_sid = NULL;
2427 	idmap_stat stat;
2428 	int id_type;
2429 	int rc;
2430 
2431 	*sid = NULL;
2432 	if (dom_idx == SMB_LGRP_LOCAL_IDX) {
2433 		id_type = (sid_type == SidTypeUser)
2434 		    ? SMB_IDMAP_USER : SMB_IDMAP_GROUP;
2435 		stat = smb_idmap_getsid(*rid, id_type, &res_sid);
2436 		if (stat != IDMAP_SUCCESS) {
2437 			syslog(LOG_ERR, "smb_lgrp_getsid: "
2438 			    "failed to get a SID for %s id=%u (%d)",
2439 			    (id_type == SMB_IDMAP_USER) ? "user" : "group",
2440 			    *rid, stat);
2441 			return (SMB_LGRP_NO_SID);
2442 		}
2443 
2444 		/*
2445 		 * Make sure the returned SID is local
2446 		 */
2447 		if (!smb_sid_indomain(smb_localgrp.lg_machine_sid, res_sid)) {
2448 			syslog(LOG_ERR, "smb_lgrp_getsid: "
2449 			    "local %s (%u) is mapped to a non-local SID",
2450 			    (id_type == SMB_IDMAP_USER) ? "user" : "group",
2451 			    *rid);
2452 			smb_sid_free(res_sid);
2453 			return (SMB_LGRP_SID_NOTLOCAL);
2454 		}
2455 
2456 		(void) smb_sid_getrid(res_sid, rid);
2457 		*sid = res_sid;
2458 		return (SMB_LGRP_SUCCESS);
2459 	}
2460 
2461 	rc = smb_lgrp_dtbl_getsid(db, dom_idx, &dom_sid);
2462 	if (rc != SMB_LGRP_SUCCESS) {
2463 		syslog(LOG_ERR, "smb_lgrp_getsid: %s", smb_lgrp_strerror(rc));
2464 		return (SMB_LGRP_DB_ERROR);
2465 	}
2466 
2467 	res_sid = smb_sid_splice(dom_sid, *rid);
2468 	smb_sid_free(dom_sid);
2469 	if (res_sid == NULL) {
2470 		syslog(LOG_ERR, "smb_lgrp_getsid: %s", smb_lgrp_strerror(rc));
2471 		return (SMB_LGRP_NO_MEMORY);
2472 	}
2473 
2474 	*sid = res_sid;
2475 	return (SMB_LGRP_SUCCESS);
2476 }
2477 
2478 /*
2479  * smb_lgrp_getgid
2480  *
2481  * Converts given local RID to a local gid since for user
2482  * defined local groups, gid is stored in the table.
2483  */
2484 static int
2485 smb_lgrp_getgid(uint32_t rid, gid_t *gid)
2486 {
2487 	smb_sid_t *sid;
2488 	int idtype;
2489 	int rc;
2490 
2491 	if ((sid = smb_sid_splice(smb_localgrp.lg_machine_sid, rid)) == NULL)
2492 		return (SMB_LGRP_NO_MEMORY);
2493 
2494 	idtype = SMB_IDMAP_GROUP;
2495 	rc = smb_idmap_getid(sid, gid, &idtype);
2496 	smb_sid_free(sid);
2497 
2498 	return ((rc == IDMAP_SUCCESS) ? SMB_LGRP_SUCCESS : SMB_LGRP_NOT_FOUND);
2499 }
2500 
2501 /*
2502  * smb_lgrp_exists
2503  *
2504  * Returns B_TRUE if the local group with the given name exists.
2505  * Otherwise, returns B_FALSE.
2506  */
2507 static boolean_t
2508 smb_lgrp_exists(char *gname)
2509 {
2510 	sqlite *db;
2511 	boolean_t rc;
2512 
2513 	if (!smb_lgrp_normalize_name(gname))
2514 		return (B_FALSE);
2515 
2516 	db = smb_lgrp_db_open(SMB_LGRP_DB_ORD);
2517 	if (db == NULL)
2518 		return (B_FALSE);
2519 
2520 	rc = smb_lgrp_gtbl_exists(db, gname);
2521 	smb_lgrp_db_close(db);
2522 
2523 	return (rc);
2524 }
2525 
2526 /*
2527  * smb_lgrp_pgrp_valid_gname
2528  *
2529  * Validate posix group name string.
2530  */
2531 static int
2532 smb_lgrp_pgrp_valid_gname(char *group)
2533 {
2534 	char *ptr = group;
2535 	char c;
2536 	int len = 0;
2537 	int badchar = 0;
2538 
2539 	if (!group || !*group)
2540 		return (SMB_LGRP_PGRP_INVALID);
2541 
2542 	for (c = *ptr; c != '\0'; ptr++, c = *ptr) {
2543 		len++;
2544 		if (!isprint(c) || (c == ':') || (c == '\n'))
2545 			return (SMB_LGRP_PGRP_INVALID);
2546 
2547 		if (!(islower(c) || isdigit(c)))
2548 			badchar++;
2549 	}
2550 
2551 	if ((len > SMB_LGRP_PGRP_MAXGLEN - 1) || (badchar != 0))
2552 		return (SMB_LGRP_PGRP_INVALID);
2553 
2554 	if (getgrnam(group) != NULL)
2555 		return (SMB_LGRP_PGRP_NOTUNIQUE);
2556 
2557 	return (SMB_LGRP_PGRP_UNIQUE);
2558 }
2559 
2560 /*
2561  * smb_lgrp_pgrp_add
2562  *
2563  * Create a posix group with the given name.
2564  * This group will be added to the /etc/group file.
2565  */
2566 static int
2567 smb_lgrp_pgrp_add(char *group)
2568 {
2569 	FILE *etcgrp;
2570 	FILE *etctmp;
2571 	int o_mask;
2572 	int newdone = 0;
2573 	struct stat sb;
2574 	char buf[SMB_LGRP_PGRP_GRPBUFSIZ];
2575 	gid_t gid;
2576 	int rc = 0;
2577 
2578 	rc = smb_lgrp_pgrp_valid_gname(group);
2579 	if ((rc == SMB_LGRP_PGRP_INVALID) || (rc == SMB_LGRP_PGRP_NOTUNIQUE))
2580 		return (-1);
2581 
2582 	if ((findnextgid(SMB_LGRP_PGRP_DEFRID, MAXUID, &gid)) != 0)
2583 		return (-1);
2584 
2585 	if ((etcgrp = fopen(SMB_LGRP_PGRP_GROUP, "r")) == NULL)
2586 		return (-1);
2587 
2588 	if (fstat(fileno(etcgrp), &sb) < 0)
2589 		sb.st_mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH;
2590 
2591 	o_mask = umask(077);
2592 	etctmp = fopen(SMB_LGRP_PGRP_GRPTMP, "w+");
2593 	(void) umask(o_mask);
2594 
2595 	if (etctmp == NULL) {
2596 		(void) fclose(etcgrp);
2597 		return (-1);
2598 	}
2599 
2600 	if (lockf(fileno(etctmp), F_LOCK, 0) != 0) {
2601 		(void) fclose(etcgrp);
2602 		(void) fclose(etctmp);
2603 		(void) unlink(SMB_LGRP_PGRP_GRPTMP);
2604 		return (-1);
2605 	}
2606 
2607 	if (fchmod(fileno(etctmp), sb.st_mode) != 0 ||
2608 	    fchown(fileno(etctmp), sb.st_uid, sb.st_gid) != 0) {
2609 		(void) lockf(fileno(etctmp), F_ULOCK, 0);
2610 		(void) fclose(etcgrp);
2611 		(void) fclose(etctmp);
2612 		(void) unlink(SMB_LGRP_PGRP_GRPTMP);
2613 		return (-1);
2614 	}
2615 
2616 	while (fgets(buf, SMB_LGRP_PGRP_GRPBUFSIZ, etcgrp) != NULL) {
2617 		/* Check for NameService reference */
2618 		if (!newdone && (buf[0] == '+' || buf[0] == '-')) {
2619 			(void) fprintf(etctmp, "%s::%u:\n", group, gid);
2620 			newdone = 1;
2621 		}
2622 
2623 		(void) fputs(buf, etctmp);
2624 	}
2625 	(void) fclose(etcgrp);
2626 
2627 	if (!newdone)
2628 		(void) fprintf(etctmp, "%s::%u:\n", group, gid);
2629 
2630 	if (rename(SMB_LGRP_PGRP_GRPTMP, SMB_LGRP_PGRP_GROUP) < 0) {
2631 		(void) lockf(fileno(etctmp), F_ULOCK, 0);
2632 		(void) fclose(etctmp);
2633 		(void) unlink(SMB_LGRP_PGRP_GRPTMP);
2634 		return (-1);
2635 	}
2636 
2637 	(void) lockf(fileno(etctmp), F_ULOCK, 0);
2638 	(void) fclose(etctmp);
2639 	return (0);
2640 }
2641