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