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