xref: /illumos-gate/usr/src/lib/smbsrv/libsmb/common/smb_lgrp.c (revision c28afb19581b550bf02e148f953e3b239421e1ee)
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 		char	*tname;
1108 
1109 		if ((wka = smb_wka_lookup_name(builtin[i])) == NULL)
1110 			continue;
1111 
1112 		if ((tname = strdup(wka->wka_name)) == NULL)
1113 			return (SMB_LGRP_NO_MEMORY);
1114 		if (!smb_lgrp_exists(tname)) {
1115 			rc = smb_lgrp_add(tname, wka->wka_desc);
1116 			if (rc != SMB_LGRP_SUCCESS) {
1117 				syslog(LOG_DEBUG, "failed to add %s",
1118 				    tname);
1119 			}
1120 		}
1121 		free(tname);
1122 	}
1123 
1124 	return (SMB_LGRP_SUCCESS);
1125 }
1126 
1127 /*
1128  * smb_lgrp_stop
1129  *
1130  * Unintialize the library global private variables.
1131  */
1132 void
1133 smb_lgrp_stop(void)
1134 {
1135 	(void) mutex_lock(&smb_localgrp.lg_mutex);
1136 	if (!smb_localgrp.lg_online)
1137 		return;
1138 
1139 	smb_localgrp.lg_online = B_FALSE;
1140 
1141 	while (smb_localgrp.lg_refcnt > 0)
1142 		(void) cond_wait(&smb_localgrp.lg_cv, &smb_localgrp.lg_mutex);
1143 
1144 	free(smb_localgrp.lg_machine_sid);
1145 	smb_localgrp.lg_machine_sid = NULL;
1146 	(void) mutex_unlock(&smb_localgrp.lg_mutex);
1147 }
1148 
1149 static boolean_t
1150 smb_lgrp_enter(void)
1151 {
1152 	boolean_t	status;
1153 
1154 	(void) mutex_lock(&smb_localgrp.lg_mutex);
1155 
1156 	status = smb_localgrp.lg_online;
1157 
1158 	if (smb_localgrp.lg_online)
1159 		++smb_localgrp.lg_refcnt;
1160 
1161 	(void) mutex_unlock(&smb_localgrp.lg_mutex);
1162 	return (status);
1163 }
1164 
1165 static void
1166 smb_lgrp_exit(void)
1167 {
1168 	(void) mutex_lock(&smb_localgrp.lg_mutex);
1169 	assert(smb_localgrp.lg_refcnt > 0);
1170 
1171 	if ((--smb_localgrp.lg_refcnt) == 0)
1172 		(void) cond_signal(&smb_localgrp.lg_cv);
1173 
1174 	(void) mutex_unlock(&smb_localgrp.lg_mutex);
1175 }
1176 
1177 /*
1178  * smb_lgrp_db_open
1179  *
1180  * Opens group database with the given mode.
1181  */
1182 static sqlite *
1183 smb_lgrp_db_open(int mode)
1184 {
1185 	sqlite *db;
1186 	char *errmsg = NULL;
1187 
1188 	db = sqlite_open(SMB_LGRP_DB_NAME, mode, &errmsg);
1189 	if (db == NULL) {
1190 		syslog(LOG_ERR, "failed to open group database (%s)",
1191 		    NULL_MSGCHK(errmsg));
1192 		sqlite_freemem(errmsg);
1193 	}
1194 
1195 	return (db);
1196 }
1197 
1198 /*
1199  * smb_lgrp_db_close
1200  *
1201  * Closes the given database handle
1202  */
1203 static void
1204 smb_lgrp_db_close(sqlite *db)
1205 {
1206 	if (db) {
1207 		sqlite_close(db);
1208 	}
1209 }
1210 
1211 /*
1212  * smb_lgrp_db_init
1213  *
1214  * Creates the group database based on the defined SQL statement.
1215  * It also initializes db_info and domain tables.
1216  */
1217 static int
1218 smb_lgrp_db_init(void)
1219 {
1220 	int dbrc = SQLITE_OK;
1221 	int rc = SMB_LGRP_SUCCESS;
1222 	sqlite *db = NULL;
1223 	char *errmsg = NULL;
1224 
1225 	db = sqlite_open(SMB_LGRP_DB_NAME, 0600, &errmsg);
1226 	if (db == NULL) {
1227 		syslog(LOG_ERR, "failed to create group database (%s)",
1228 		    NULL_MSGCHK(errmsg));
1229 		sqlite_freemem(errmsg);
1230 		return (SMB_LGRP_DBOPEN_FAILED);
1231 	}
1232 
1233 	sqlite_busy_timeout(db, SMB_LGRP_DB_TIMEOUT);
1234 	dbrc = sqlite_exec(db, "BEGIN TRANSACTION;", NULL, NULL, &errmsg);
1235 	if (dbrc != SQLITE_OK) {
1236 		syslog(LOG_DEBUG, "failed to begin database transaction (%s)",
1237 		    NULL_MSGCHK(errmsg));
1238 		sqlite_freemem(errmsg);
1239 		sqlite_close(db);
1240 		return (SMB_LGRP_DBEXEC_FAILED);
1241 	}
1242 
1243 	switch (sqlite_exec(db, SMB_LGRP_DB_SQL, NULL, NULL, &errmsg)) {
1244 	case SQLITE_ERROR:
1245 		/*
1246 		 * This is the normal situation: CREATE probably failed because
1247 		 * tables already exist. It may indicate an error in SQL as well
1248 		 * but we cannot tell.
1249 		 */
1250 		sqlite_freemem(errmsg);
1251 		dbrc = sqlite_exec(db, "ROLLBACK TRANSACTION", NULL, NULL,
1252 		    &errmsg);
1253 		rc = SMB_LGRP_SUCCESS;
1254 		break;
1255 
1256 	case SQLITE_OK:
1257 		dbrc = sqlite_exec(db, "COMMIT TRANSACTION", NULL, NULL,
1258 		    &errmsg);
1259 		if (dbrc != SQLITE_OK)
1260 			break;
1261 		rc = smb_lgrp_dtbl_insert(db, NT_BUILTIN_DOMAIN_SIDSTR,
1262 		    NULL);
1263 		if (rc == SMB_LGRP_SUCCESS)
1264 			rc = smb_lgrp_db_setinfo(db);
1265 		if (rc != SMB_LGRP_SUCCESS) {
1266 			(void) sqlite_close(db);
1267 			(void) unlink(SMB_LGRP_DB_NAME);
1268 			return (rc);
1269 		}
1270 		break;
1271 
1272 	default:
1273 		syslog(LOG_ERR,
1274 		    "failed to initialize group database (%s)", errmsg);
1275 		sqlite_freemem(errmsg);
1276 		dbrc = sqlite_exec(db, "ROLLBACK TRANSACTION", NULL, NULL,
1277 		    &errmsg);
1278 		rc = SMB_LGRP_DBINIT_FAILED;
1279 		break;
1280 	}
1281 
1282 	if (dbrc != SQLITE_OK) {
1283 		/* this is bad - database may be left in a locked state */
1284 		syslog(LOG_DEBUG, "failed to close a transaction (%s)",
1285 		    NULL_MSGCHK(errmsg));
1286 		sqlite_freemem(errmsg);
1287 	}
1288 
1289 	(void) sqlite_close(db);
1290 	return (rc);
1291 }
1292 
1293 /*
1294  * smb_lgrp_gtbl_lookup
1295  *
1296  * This is a flexible lookup function for the group database.
1297  * The key type can be specified by the 'key' arg and the actual key
1298  * values can be passed after the 'infolvl' arg. 'infolvl' arg specifies
1299  * what information items for the specified group is needed.
1300  *
1301  * Note that the function assumes the given key is unique and only
1302  * specifies one or 0 group. The keys that are supported now are
1303  * the group name and the group SID
1304  *
1305  * Note that this function doesn't allocate the group
1306  * structure itself only the fields, so the given grp
1307  * pointer has to point to a group structure.
1308  * Caller must free the allocated memories for the fields
1309  * by calling smb_lgrp_free().
1310  */
1311 static int
1312 smb_lgrp_gtbl_lookup(sqlite *db, int key, smb_group_t *grp, int infolvl, ...)
1313 {
1314 	char *errmsg = NULL;
1315 	char *sql;
1316 	char **result;
1317 	int nrow, ncol;
1318 	int rc, dom_idx;
1319 	smb_group_t grpkey;
1320 	va_list ap;
1321 
1322 	if (db == NULL)
1323 		return (SMB_LGRP_DBOPEN_FAILED);
1324 
1325 	bzero(grp, sizeof (smb_group_t));
1326 	va_start(ap, infolvl);
1327 
1328 	switch (key) {
1329 	case SMB_LGRP_GTBL_NAME:
1330 		grpkey.sg_name = va_arg(ap, char *);
1331 		sql = sqlite_mprintf("SELECT * FROM groups WHERE name = '%s'",
1332 		    grpkey.sg_name);
1333 		break;
1334 
1335 	case SMB_LGRP_GTBL_SIDRID:
1336 		grpkey.sg_rid = va_arg(ap, uint32_t);
1337 		grpkey.sg_domain = va_arg(ap, smb_domain_type_t);
1338 		if (grpkey.sg_domain == SMB_DOMAIN_LOCAL) {
1339 			dom_idx = SMB_LGRP_LOCAL_IDX;
1340 			/* need to map the given rid to a gid */
1341 			rc = smb_lgrp_getgid(grpkey.sg_rid,
1342 			    (gid_t *)&grpkey.sg_rid);
1343 			if (rc != SMB_LGRP_SUCCESS) {
1344 				va_end(ap);
1345 				return (rc);
1346 			}
1347 		} else {
1348 			dom_idx = SMB_LGRP_BUILTIN_IDX;
1349 		}
1350 
1351 		sql = sqlite_mprintf("SELECT * FROM groups "
1352 		    "WHERE (sid_idx = %d) AND (sid_rid = %u)",
1353 		    dom_idx, grpkey.sg_rid);
1354 		break;
1355 
1356 	default:
1357 		va_end(ap);
1358 		return (SMB_LGRP_INVALID_ARG);
1359 	}
1360 
1361 	va_end(ap);
1362 	if (sql == NULL)
1363 		return (SMB_LGRP_NO_MEMORY);
1364 
1365 	rc = sqlite_get_table(db, sql, &result, &nrow, &ncol, &errmsg);
1366 	sqlite_freemem(sql);
1367 
1368 	if (rc != SQLITE_OK) {
1369 		syslog(LOG_DEBUG, "failed to lookup (%s)", NULL_MSGCHK(errmsg));
1370 		sqlite_freemem(errmsg);
1371 		return (SMB_LGRP_LOOKUP_FAILED);
1372 	}
1373 
1374 	if (nrow == 0)  {
1375 		/* group not found */
1376 		sqlite_free_table(result);
1377 		return (SMB_LGRP_NOT_FOUND);
1378 	}
1379 
1380 	if (nrow != 1 || ncol != SMB_LGRP_GTBL_NCOL) {
1381 		sqlite_free_table(result);
1382 		return (SMB_LGRP_DB_ERROR);
1383 	}
1384 
1385 	rc = smb_lgrp_decode(grp, &result[SMB_LGRP_GTBL_NCOL], infolvl, db);
1386 	sqlite_free_table(result);
1387 	return (rc);
1388 }
1389 
1390 /*
1391  * smb_lgrp_gtbl_exists
1392  *
1393  * Checks to see if the given group exists or not.
1394  */
1395 static boolean_t
1396 smb_lgrp_gtbl_exists(sqlite *db, char *gname)
1397 {
1398 	char *errmsg = NULL;
1399 	char *sql;
1400 	char **result;
1401 	int nrow, ncol;
1402 	int rc;
1403 
1404 	if (db == NULL)
1405 		return (NULL);
1406 
1407 	sql = sqlite_mprintf("SELECT name FROM groups WHERE name = '%s'",
1408 	    gname);
1409 	rc = sqlite_get_table(db, sql, &result, &nrow, &ncol, &errmsg);
1410 	sqlite_freemem(sql);
1411 
1412 	if (rc != SQLITE_OK) {
1413 		syslog(LOG_DEBUG, "failed to lookup %s (%s)",
1414 		    gname, NULL_MSGCHK(errmsg));
1415 		sqlite_freemem(errmsg);
1416 		return (B_FALSE);
1417 	}
1418 
1419 	sqlite_free_table(result);
1420 	return (nrow != 0);
1421 }
1422 
1423 /*
1424  * smb_lgrp_gtbl_count
1425  *
1426  * Counts the number of groups in the domain specified by
1427  * 'dom_idx'
1428  */
1429 static int
1430 smb_lgrp_gtbl_count(sqlite *db, int dom_idx, int *count)
1431 {
1432 	char *errmsg = NULL;
1433 	char *sql;
1434 	char **result;
1435 	int nrow, ncol;
1436 	int rc;
1437 
1438 	*count = 0;
1439 	if (db == NULL)
1440 		return (SMB_LGRP_DBOPEN_FAILED);
1441 
1442 	sql = sqlite_mprintf("SELECT sid_idx FROM groups WHERE sid_idx = %d",
1443 	    dom_idx);
1444 	rc = sqlite_get_table(db, sql, &result, &nrow, &ncol, &errmsg);
1445 	sqlite_freemem(sql);
1446 
1447 	if (rc != SQLITE_OK) {
1448 		syslog(LOG_DEBUG, "failed to count (%s)", NULL_MSGCHK(errmsg));
1449 		sqlite_freemem(errmsg);
1450 		return (SMB_LGRP_LOOKUP_FAILED);
1451 	}
1452 
1453 	sqlite_free_table(result);
1454 	if (ncol > 1)
1455 		return (SMB_LGRP_DB_ERROR);
1456 
1457 	*count = nrow;
1458 	return (SMB_LGRP_SUCCESS);
1459 }
1460 
1461 /*
1462  * smb_lgrp_gtbl_insert
1463  *
1464  * Insert a record for the given group in the group database.
1465  *
1466  * NOTE: this function assumes that this group has no members
1467  * at this time.
1468  */
1469 static int
1470 smb_lgrp_gtbl_insert(sqlite *db, smb_group_t *grp)
1471 {
1472 	smb_lgpid_t privs[SE_MAX_LUID + 1];
1473 	smb_lgplist_t plist;
1474 	char *errmsg = NULL;
1475 	char *sql;
1476 	int dom_idx;
1477 	int rc;
1478 
1479 	if (db == NULL)
1480 		return (SMB_LGRP_DBOPEN_FAILED);
1481 
1482 	dom_idx = (grp->sg_domain == SMB_DOMAIN_LOCAL)
1483 	    ? SMB_LGRP_LOCAL_IDX : SMB_LGRP_BUILTIN_IDX;
1484 
1485 	plist.p_cnt = SE_MAX_LUID;
1486 	plist.p_ids = privs;
1487 	smb_lgrp_encode_privset(grp, &plist);
1488 
1489 	sql = sqlite_mprintf("INSERT INTO groups "
1490 	    "(name, sid_idx, sid_rid, sid_type, sid_attrs, comment, "
1491 	    "n_privs, privs, n_members, members) "
1492 	    "VALUES('%s', %u, %u, %u, %u, '%q', %u, '%q', %u, '%q')",
1493 	    grp->sg_name, dom_idx, grp->sg_rid, grp->sg_id.gs_type,
1494 	    grp->sg_attr, (grp->sg_cmnt) ? grp->sg_cmnt : "",
1495 	    plist.p_cnt, (char *)plist.p_ids, 0, "");
1496 
1497 	if (sql == NULL)
1498 		return (SMB_LGRP_NO_MEMORY);
1499 
1500 	rc = sqlite_exec(db, sql, NULL, NULL, &errmsg);
1501 	sqlite_freemem(sql);
1502 
1503 	if (rc != SQLITE_OK) {
1504 		syslog(LOG_DEBUG, "failed to insert %s (%s)",
1505 		    grp->sg_name, NULL_MSGCHK(errmsg));
1506 		sqlite_freemem(errmsg);
1507 		rc = SMB_LGRP_INSERT_FAILED;
1508 	} else {
1509 		rc = SMB_LGRP_SUCCESS;
1510 	}
1511 
1512 	return (rc);
1513 }
1514 
1515 /*
1516  * smb_lgrp_gtbl_delete
1517  *
1518  * Removes the specified group from the database
1519  */
1520 static int
1521 smb_lgrp_gtbl_delete(sqlite *db, char *gname)
1522 {
1523 	char *errmsg = NULL;
1524 	char *sql;
1525 	int rc;
1526 
1527 	if (db == NULL)
1528 		return (SMB_LGRP_DBOPEN_FAILED);
1529 
1530 	sql = sqlite_mprintf("DELETE FROM groups WHERE name = '%s'", gname);
1531 	if (sql == NULL)
1532 		return (SMB_LGRP_NO_MEMORY);
1533 
1534 	rc = sqlite_exec(db, sql, NULL, NULL, &errmsg);
1535 	sqlite_freemem(sql);
1536 
1537 	if (rc != SQLITE_OK) {
1538 		syslog(LOG_DEBUG, "failed to delete %s (%s)",
1539 		    gname, NULL_MSGCHK(errmsg));
1540 		sqlite_freemem(errmsg);
1541 		rc = SMB_LGRP_DELETE_FAILED;
1542 	} else {
1543 		rc = SMB_LGRP_SUCCESS;
1544 	}
1545 
1546 	return (rc);
1547 }
1548 
1549 /*
1550  * smb_lgrp_gtbl_update
1551  *
1552  * Updates the specified group information, the supported items
1553  * are group name and comment
1554  */
1555 static int
1556 smb_lgrp_gtbl_update(sqlite *db, char *gname, smb_group_t *grp, int col_id)
1557 {
1558 	char *errmsg = NULL;
1559 	char *sql;
1560 	int rc;
1561 
1562 	if (db == NULL)
1563 		return (SMB_LGRP_DBOPEN_FAILED);
1564 
1565 	/* UPDATE doesn't fail if gname doesn't exist */
1566 	if (!smb_lgrp_gtbl_exists(db, gname))
1567 		return (SMB_LGRP_NOT_FOUND);
1568 
1569 	switch (col_id) {
1570 	case SMB_LGRP_GTBL_NAME:
1571 		if (smb_lgrp_gtbl_exists(db, grp->sg_name))
1572 			return (SMB_LGRP_EXISTS);
1573 		sql = sqlite_mprintf("UPDATE groups SET name = '%s' "
1574 		    "WHERE name = '%s'", grp->sg_name, gname);
1575 		break;
1576 
1577 	case SMB_LGRP_GTBL_CMNT:
1578 		sql = sqlite_mprintf("UPDATE groups SET comment = '%q' "
1579 		"WHERE name = '%s'", grp->sg_cmnt, gname);
1580 		break;
1581 
1582 	default:
1583 		return (SMB_LGRP_INVALID_ARG);
1584 	}
1585 
1586 	if (sql == NULL)
1587 		return (SMB_LGRP_NO_MEMORY);
1588 
1589 	rc = sqlite_exec(db, sql, NULL, NULL, &errmsg);
1590 	sqlite_freemem(sql);
1591 
1592 	if (rc != SQLITE_OK) {
1593 		syslog(LOG_DEBUG, "failed to update %s (%s)",
1594 		    gname, NULL_MSGCHK(errmsg));
1595 		sqlite_freemem(errmsg);
1596 		rc = SMB_LGRP_UPDATE_FAILED;
1597 	} else {
1598 		rc = SMB_LGRP_SUCCESS;
1599 	}
1600 
1601 	return (rc);
1602 }
1603 
1604 /*
1605  * smb_lgrp_gtbl_update_mlist
1606  *
1607  * Adds/removes the specified member from the member list of the
1608  * given group
1609  */
1610 static int
1611 smb_lgrp_gtbl_update_mlist(sqlite *db, char *gname, smb_gsid_t *member,
1612     int flags)
1613 {
1614 	smb_lgmlist_t new_members;
1615 	smb_lgmlist_t members;
1616 	smb_lgmid_t mid;
1617 	char *errmsg = NULL;
1618 	char *sql;
1619 	char **result;
1620 	int nrow, ncol;
1621 	int rc;
1622 
1623 	if (db == NULL)
1624 		return (SMB_LGRP_DBOPEN_FAILED);
1625 
1626 	sql = sqlite_mprintf("SELECT n_members, members FROM groups "
1627 	    "WHERE name = '%s'", gname);
1628 
1629 	if (sql == NULL)
1630 		return (SMB_LGRP_NO_MEMORY);
1631 
1632 	rc = sqlite_get_table(db, sql, &result, &nrow, &ncol, &errmsg);
1633 	sqlite_freemem(sql);
1634 
1635 	if (rc != SQLITE_OK) {
1636 		syslog(LOG_DEBUG, "failed to lookup %s (%s)",
1637 		    gname, NULL_MSGCHK(errmsg));
1638 		sqlite_freemem(errmsg);
1639 		return (SMB_LGRP_LOOKUP_FAILED);
1640 	}
1641 
1642 	if (nrow == 0)  {
1643 		/* group not found */
1644 		sqlite_free_table(result);
1645 		return (SMB_LGRP_NOT_FOUND);
1646 	}
1647 
1648 	if (nrow != 1 || ncol != 2) {
1649 		sqlite_free_table(result);
1650 		return (SMB_LGRP_DB_ERROR);
1651 	}
1652 
1653 	bzero(&mid, sizeof (mid));
1654 	mid.m_type = member->gs_type;
1655 	rc = smb_lgrp_dtbl_getidx(db, member->gs_sid, mid.m_type,
1656 	    &mid.m_idx, &mid.m_rid);
1657 	if (rc != SMB_LGRP_SUCCESS) {
1658 		sqlite_free_table(result);
1659 		return (rc);
1660 	}
1661 
1662 	members.m_cnt = atoi(result[2]);
1663 	members.m_ids = result[3];
1664 
1665 	switch (flags) {
1666 	case SMB_LGRP_DB_ADDMEMBER:
1667 		rc = smb_lgrp_mlist_add(&members, &mid, &new_members);
1668 		break;
1669 	case SMB_LGRP_DB_DELMEMBER:
1670 		rc = smb_lgrp_mlist_del(&members, &mid, &new_members);
1671 		break;
1672 	default:
1673 		rc = SMB_LGRP_INVALID_ARG;
1674 	}
1675 
1676 	sqlite_free_table(result);
1677 	if (rc != SMB_LGRP_SUCCESS)
1678 		return (rc);
1679 
1680 	sql = sqlite_mprintf("UPDATE groups SET n_members = %u, members = '%s'"
1681 	    " WHERE name = '%s'", new_members.m_cnt, new_members.m_ids, gname);
1682 
1683 	free(new_members.m_ids);
1684 
1685 	if (sql == NULL)
1686 		return (SMB_LGRP_NO_MEMORY);
1687 
1688 	rc = sqlite_exec(db, sql, NULL, NULL, &errmsg);
1689 	sqlite_freemem(sql);
1690 
1691 	if (rc != SQLITE_OK) {
1692 		syslog(LOG_DEBUG, "failed to update %s (%s)", gname,
1693 		    NULL_MSGCHK(errmsg));
1694 		sqlite_freemem(errmsg);
1695 		rc = SMB_LGRP_UPDATE_FAILED;
1696 	} else {
1697 		rc = SMB_LGRP_SUCCESS;
1698 	}
1699 
1700 	return (rc);
1701 }
1702 
1703 /*
1704  * smb_lgrp_gtbl_update_plist
1705  *
1706  * Adds/removes the specified privilege from the privilege list of the
1707  * given group
1708  */
1709 static int
1710 smb_lgrp_gtbl_update_plist(sqlite *db, char *gname, uint8_t priv_id,
1711     boolean_t enable)
1712 {
1713 	char *sql;
1714 	char *errmsg = NULL;
1715 	char **result;
1716 	int nrow, ncol;
1717 	int rc;
1718 	smb_lgplist_t privs;
1719 	smb_lgplist_t new_privs;
1720 
1721 	if (db == NULL)
1722 		return (SMB_LGRP_DBOPEN_FAILED);
1723 
1724 	sql = sqlite_mprintf("SELECT n_privs, privs FROM groups "
1725 	    "WHERE name = '%s'", gname);
1726 
1727 	if (sql == NULL)
1728 		return (SMB_LGRP_NO_MEMORY);
1729 
1730 	rc = sqlite_get_table(db, sql, &result, &nrow, &ncol, &errmsg);
1731 	sqlite_freemem(sql);
1732 
1733 	if (rc != SQLITE_OK) {
1734 		syslog(LOG_DEBUG, "failed to lookup %s (%s)",
1735 		    gname, NULL_MSGCHK(errmsg));
1736 		sqlite_freemem(errmsg);
1737 		return (SMB_LGRP_LOOKUP_FAILED);
1738 	}
1739 
1740 	if (nrow == 0)  {
1741 		/* group not found */
1742 		sqlite_free_table(result);
1743 		return (SMB_LGRP_NOT_FOUND);
1744 	}
1745 
1746 	if (nrow != 1 || ncol != 2) {
1747 		sqlite_free_table(result);
1748 		return (SMB_LGRP_DB_ERROR);
1749 	}
1750 
1751 	privs.p_cnt = atoi(result[2]);
1752 	privs.p_ids = (smb_lgpid_t *)result[3];
1753 
1754 	if (enable)
1755 		rc = smb_lgrp_plist_add(&privs, priv_id, &new_privs);
1756 	else
1757 		rc = smb_lgrp_plist_del(&privs, priv_id, &new_privs);
1758 
1759 	sqlite_free_table(result);
1760 	if (rc != SMB_LGRP_SUCCESS)
1761 		return (rc);
1762 
1763 	sql = sqlite_mprintf("UPDATE groups SET n_privs = %u, privs = '%q'"
1764 	    " WHERE name = '%s'", new_privs.p_cnt, (char *)new_privs.p_ids,
1765 	    gname);
1766 
1767 	free(new_privs.p_ids);
1768 
1769 	if (sql == NULL)
1770 		return (SMB_LGRP_NO_MEMORY);
1771 
1772 	rc = sqlite_exec(db, sql, NULL, NULL, &errmsg);
1773 	sqlite_freemem(sql);
1774 
1775 	if (rc != SQLITE_OK) {
1776 		syslog(LOG_DEBUG, "failed to update %s (%s)",
1777 		    gname, NULL_MSGCHK(errmsg));
1778 		sqlite_freemem(errmsg);
1779 		rc = SMB_LGRP_UPDATE_FAILED;
1780 	} else {
1781 		rc = SMB_LGRP_SUCCESS;
1782 	}
1783 
1784 	return (rc);
1785 }
1786 
1787 /*
1788  * smb_lgrp_dtbl_insert
1789  *
1790  * Inserts the specified domain SID in the dmain table.
1791  * Upon successful insert the index will be returned in
1792  * 'dom_idx' arg.
1793  */
1794 static int
1795 smb_lgrp_dtbl_insert(sqlite *db, char *dom_sid, uint32_t *dom_idx)
1796 {
1797 	char *errmsg = NULL;
1798 	char *sql;
1799 	int rc;
1800 
1801 	sql = sqlite_mprintf("INSERT INTO domains (dom_sid, dom_cnt)"
1802 	    " VALUES('%s', 1);", dom_sid);
1803 	if (sql == NULL)
1804 		return (SMB_LGRP_NO_MEMORY);
1805 
1806 	rc = sqlite_exec(db, sql, NULL, NULL, &errmsg);
1807 	sqlite_freemem(sql);
1808 
1809 	if (rc != SQLITE_OK) {
1810 		syslog(LOG_DEBUG, "failed to insert domain SID (%s)",
1811 		    NULL_MSGCHK(errmsg));
1812 		sqlite_freemem(errmsg);
1813 		return (SMB_LGRP_DOMINS_FAILED);
1814 	}
1815 
1816 	if (dom_idx)
1817 		*dom_idx = sqlite_last_insert_rowid(db);
1818 	return (SMB_LGRP_SUCCESS);
1819 }
1820 
1821 /*
1822  * smb_lgrp_dtbl_getidx
1823  *
1824  * Searches the domain table for the domain SID of the
1825  * given member SID. If it finds the domain SID it'll
1826  * return the index and the RID, otherwise it'll insert
1827  * it in the domain table as a new SID.
1828  */
1829 static int
1830 smb_lgrp_dtbl_getidx(sqlite *db, smb_sid_t *sid, uint16_t sid_type,
1831     uint32_t *dom_idx, uint32_t *rid)
1832 {
1833 	char sidstr[SMB_SID_STRSZ];
1834 	smb_sid_t *dom_sid;
1835 	char **result;
1836 	int nrow, ncol;
1837 	char *errmsg = NULL;
1838 	char *sql;
1839 	int rc;
1840 
1841 	if (smb_sid_indomain(smb_localgrp.lg_machine_sid, sid)) {
1842 		/* This is a local SID */
1843 		int id_type = (sid_type == SidTypeUser)
1844 		    ? SMB_IDMAP_USER : SMB_IDMAP_GROUP;
1845 		*dom_idx = SMB_LGRP_LOCAL_IDX;
1846 		if (smb_idmap_getid(sid, rid, &id_type) != IDMAP_SUCCESS)
1847 			return (SMB_LGRP_INTERNAL_ERROR);
1848 
1849 		return (SMB_LGRP_SUCCESS);
1850 	}
1851 
1852 	if ((dom_sid = smb_sid_split(sid, rid)) == NULL)
1853 		return (SMB_LGRP_NO_MEMORY);
1854 
1855 	smb_sid_tostr(dom_sid, sidstr);
1856 	free(dom_sid);
1857 
1858 	sql = sqlite_mprintf("SELECT dom_idx FROM domains WHERE dom_sid = '%s'",
1859 	    sidstr);
1860 	if (sql == NULL)
1861 		return (SMB_LGRP_NO_MEMORY);
1862 
1863 	rc = sqlite_get_table(db, sql, &result, &nrow, &ncol, &errmsg);
1864 	sqlite_freemem(sql);
1865 
1866 	if (rc != SQLITE_OK) {
1867 		syslog(LOG_DEBUG, "failed to lookup domain SID (%s)",
1868 		    NULL_MSGCHK(errmsg));
1869 		sqlite_freemem(errmsg);
1870 		return (SMB_LGRP_DOMLKP_FAILED);
1871 	}
1872 
1873 	switch (nrow) {
1874 	case 0:
1875 		/* new domain SID; insert it into the domains table */
1876 		sqlite_free_table(result);
1877 		return (smb_lgrp_dtbl_insert(db, sidstr, dom_idx));
1878 
1879 	case 1:
1880 		*dom_idx = atoi(result[1]);
1881 		sqlite_free_table(result);
1882 		return (SMB_LGRP_SUCCESS);
1883 	}
1884 
1885 	sqlite_free_table(result);
1886 	return (SMB_LGRP_DB_ERROR);
1887 }
1888 
1889 /*
1890  * smb_lgrp_dtbl_getsid
1891  *
1892  * Searchs the domain table for the given domain index.
1893  * Converts the found domain SID to binary format and
1894  * returns it in the 'sid' arg.
1895  *
1896  * Caller must free the returned SID by calling free().
1897  */
1898 static int
1899 smb_lgrp_dtbl_getsid(sqlite *db, uint32_t dom_idx, smb_sid_t **sid)
1900 {
1901 	char **result;
1902 	int nrow, ncol;
1903 	char *errmsg = NULL;
1904 	char *sql;
1905 	int rc;
1906 
1907 	sql = sqlite_mprintf("SELECT dom_sid FROM domains WHERE dom_idx = %u",
1908 	    dom_idx);
1909 	if (sql == NULL)
1910 		return (SMB_LGRP_NO_MEMORY);
1911 
1912 	rc = sqlite_get_table(db, sql, &result, &nrow, &ncol, &errmsg);
1913 	sqlite_freemem(sql);
1914 
1915 	if (rc != SQLITE_OK) {
1916 		syslog(LOG_DEBUG, "failed to lookup domain index (%s)",
1917 		    NULL_MSGCHK(errmsg));
1918 		sqlite_freemem(errmsg);
1919 		return (SMB_LGRP_DOMLKP_FAILED);
1920 	}
1921 
1922 	switch (nrow) {
1923 	case 0:
1924 		rc = SMB_LGRP_NO_SUCH_DOMAIN;
1925 		break;
1926 
1927 	case 1:
1928 		*sid = smb_sid_fromstr(result[1]);
1929 		rc = (*sid == NULL)
1930 		    ? SMB_LGRP_INTERNAL_ERROR : SMB_LGRP_SUCCESS;
1931 		break;
1932 
1933 	default:
1934 		rc = SMB_LGRP_DB_ERROR;
1935 		break;
1936 	}
1937 
1938 	sqlite_free_table(result);
1939 	return (rc);
1940 }
1941 
1942 /*
1943  * smb_lgrp_db_setinfo
1944  *
1945  * Initializes the db_info table upon database creation.
1946  */
1947 static int
1948 smb_lgrp_db_setinfo(sqlite *db)
1949 {
1950 	char *errmsg = NULL;
1951 	char *sql;
1952 	int rc;
1953 
1954 	sql = sqlite_mprintf("INSERT INTO db_info (ver_major, ver_minor,"
1955 	    " magic) VALUES (%d, %d, %u)", SMB_LGRP_DB_VERMAJOR,
1956 	    SMB_LGRP_DB_VERMINOR, SMB_LGRP_DB_MAGIC);
1957 
1958 	if (sql == NULL)
1959 		return (SMB_LGRP_NO_MEMORY);
1960 
1961 	rc = sqlite_exec(db, sql, NULL, NULL, &errmsg);
1962 	sqlite_freemem(sql);
1963 	if (rc != SQLITE_OK) {
1964 		syslog(LOG_DEBUG, "failed to insert database information (%s)",
1965 		    NULL_MSGCHK(errmsg));
1966 		sqlite_freemem(errmsg);
1967 		rc = SMB_LGRP_DBINIT_ERROR;
1968 	} else {
1969 		rc = SMB_LGRP_SUCCESS;
1970 	}
1971 
1972 	return (rc);
1973 }
1974 
1975 /*
1976  * smb_lgrp_mlist_add
1977  *
1978  * Adds the given member (newm) to the input member list (in_members)
1979  * if it's not already there. The result list will be returned in
1980  * out_members. The caller must free the allocated memory for
1981  * out_members by calling free().
1982  *
1983  * in_members and out_members are hex strings.
1984  */
1985 static int
1986 smb_lgrp_mlist_add(smb_lgmlist_t *in_members, smb_lgmid_t *newm,
1987     smb_lgmlist_t *out_members)
1988 {
1989 	char mid_hex[SMB_LGRP_MID_HEXSZ];
1990 	char *in_list;
1991 	char *out_list;
1992 	int in_size;
1993 	int out_size;
1994 	int mid_hexsz;
1995 	int i;
1996 
1997 	out_members->m_cnt = 0;
1998 	out_members->m_ids = NULL;
1999 
2000 	bzero(mid_hex, sizeof (mid_hex));
2001 	mid_hexsz = bintohex((const char *)newm, sizeof (smb_lgmid_t),
2002 	    mid_hex, sizeof (mid_hex));
2003 
2004 	/*
2005 	 * Check to see if this is already a group member
2006 	 */
2007 	in_list = in_members->m_ids;
2008 	for (i = 0; i < in_members->m_cnt; i++) {
2009 		if (strncmp(in_list, mid_hex, mid_hexsz) == 0)
2010 			return (SMB_LGRP_MEMBER_IN_GROUP);
2011 		in_list += mid_hexsz;
2012 	}
2013 
2014 	in_size = (in_members->m_ids) ? strlen(in_members->m_ids) : 0;
2015 	out_size = in_size + sizeof (mid_hex) + 1;
2016 	out_list = malloc(out_size);
2017 	if (out_list == NULL)
2018 		return (SMB_LGRP_NO_MEMORY);
2019 
2020 	bzero(out_list, out_size);
2021 	if (in_members->m_ids)
2022 		(void) strlcpy(out_list, in_members->m_ids, out_size);
2023 	(void) strcat(out_list, mid_hex);
2024 
2025 	out_members->m_cnt = in_members->m_cnt + 1;
2026 	out_members->m_ids = out_list;
2027 
2028 	return (SMB_LGRP_SUCCESS);
2029 }
2030 
2031 /*
2032  * smb_lgrp_mlist_del
2033  *
2034  * Removes the given member (msid) from the input member list
2035  * (in_members) if it's already there. The result list will b
2036  * returned in out_members. The caller must free the allocated
2037  * memory for out_members by calling free().
2038  *
2039  * in_members and out_members are hex strings.
2040  */
2041 static int
2042 smb_lgrp_mlist_del(smb_lgmlist_t *in_members, smb_lgmid_t *mid,
2043     smb_lgmlist_t *out_members)
2044 {
2045 	char mid_hex[SMB_LGRP_MID_HEXSZ];
2046 	char *in_list;
2047 	char *out_list;
2048 	int in_size;
2049 	int out_size;
2050 	int mid_hexsz;
2051 	int out_cnt;
2052 	int i;
2053 
2054 	out_members->m_cnt = 0;
2055 	out_members->m_ids = NULL;
2056 
2057 	if ((in_members == NULL) || (in_members->m_cnt == 0))
2058 		return (SMB_LGRP_MEMBER_NOT_IN_GROUP);
2059 
2060 	in_size = strlen(in_members->m_ids);
2061 	out_size = in_size + sizeof (mid_hex) + 1;
2062 	out_list = malloc(out_size);
2063 	if (out_list == NULL)
2064 		return (SMB_LGRP_NO_MEMORY);
2065 
2066 	*out_list = '\0';
2067 
2068 	bzero(mid_hex, sizeof (mid_hex));
2069 	mid_hexsz = bintohex((const char *)mid, sizeof (smb_lgmid_t),
2070 	    mid_hex, sizeof (mid_hex));
2071 
2072 	in_list = in_members->m_ids;
2073 	for (i = 0, out_cnt = 0; i < in_members->m_cnt; i++) {
2074 		if (strncmp(in_list, mid_hex, mid_hexsz)) {
2075 			(void) strncat(out_list, in_list, mid_hexsz);
2076 			out_cnt++;
2077 		}
2078 		in_list += mid_hexsz;
2079 	}
2080 
2081 	if (out_cnt == in_members->m_cnt) {
2082 		free(out_list);
2083 		return (SMB_LGRP_MEMBER_NOT_IN_GROUP);
2084 	}
2085 
2086 	out_members->m_cnt = out_cnt;
2087 	out_members->m_ids = out_list;
2088 	return (SMB_LGRP_SUCCESS);
2089 }
2090 
2091 /*
2092  * smb_lgrp_plist_add
2093  *
2094  * Adds the given privilege to the input list (in_privs)
2095  * if it's not already there. The result list is returned
2096  * in out_privs. The caller must free the allocated memory
2097  * for out_privs by calling free().
2098  */
2099 static int
2100 smb_lgrp_plist_add(smb_lgplist_t *in_privs, smb_lgpid_t priv_id,
2101     smb_lgplist_t *out_privs)
2102 {
2103 	int i, size;
2104 	smb_lgpid_t *pbuf;
2105 
2106 	out_privs->p_cnt = 0;
2107 	out_privs->p_ids = NULL;
2108 
2109 	for (i = 0; i < in_privs->p_cnt; i++) {
2110 		if (in_privs->p_ids[i] == priv_id)
2111 			return (SMB_LGRP_PRIV_HELD);
2112 	}
2113 
2114 	size = (in_privs->p_cnt + 1) * sizeof (smb_lgpid_t) + 1;
2115 	pbuf = malloc(size);
2116 	if (pbuf == NULL)
2117 		return (SMB_LGRP_NO_MEMORY);
2118 
2119 	bzero(pbuf, size);
2120 	bcopy(in_privs->p_ids, pbuf, in_privs->p_cnt * sizeof (smb_lgpid_t));
2121 	pbuf[in_privs->p_cnt] = priv_id;
2122 
2123 	out_privs->p_cnt = in_privs->p_cnt + 1;
2124 	out_privs->p_ids = pbuf;
2125 
2126 	return (SMB_LGRP_SUCCESS);
2127 }
2128 
2129 /*
2130  * smb_lgrp_plist_del
2131  *
2132  * Removes the given privilege from the input list (in_privs)
2133  * if it's already there. The result list is returned
2134  * in out_privs. The caller must free the allocated memory
2135  * for out_privs by calling free().
2136  */
2137 static int
2138 smb_lgrp_plist_del(smb_lgplist_t *in_privs, smb_lgpid_t priv_id,
2139     smb_lgplist_t *out_privs)
2140 {
2141 	int i, size;
2142 
2143 	out_privs->p_cnt = 0;
2144 	out_privs->p_ids = NULL;
2145 
2146 	if ((in_privs == NULL) || (in_privs->p_cnt == 0))
2147 		return (SMB_LGRP_PRIV_NOT_HELD);
2148 
2149 	size = (in_privs->p_cnt - 1) * sizeof (smb_lgpid_t) + 1;
2150 	out_privs->p_ids = malloc(size);
2151 	if (out_privs->p_ids == NULL)
2152 		return (SMB_LGRP_NO_MEMORY);
2153 
2154 	bzero(out_privs->p_ids, size);
2155 
2156 	for (i = 0; i < in_privs->p_cnt; i++) {
2157 		if (in_privs->p_ids[i] != priv_id)
2158 			out_privs->p_ids[out_privs->p_cnt++] =
2159 			    in_privs->p_ids[i];
2160 	}
2161 
2162 	if (out_privs->p_cnt == in_privs->p_cnt) {
2163 		free(out_privs->p_ids);
2164 		out_privs->p_cnt = 0;
2165 		out_privs->p_ids = NULL;
2166 		return (SMB_LGRP_PRIV_NOT_HELD);
2167 	}
2168 
2169 	return (SMB_LGRP_SUCCESS);
2170 }
2171 
2172 /*
2173  * smb_lgrp_encode_privset
2174  *
2175  * Encodes given privilege set into a buffer to be stored in the group
2176  * database. Each entry of the encoded buffer contains the privilege ID
2177  * of an enable privilege. The returned buffer is null-terminated.
2178  */
2179 static void
2180 smb_lgrp_encode_privset(smb_group_t *grp, smb_lgplist_t *plist)
2181 {
2182 	smb_privset_t *privs;
2183 	uint32_t pcnt = plist->p_cnt;
2184 	int i;
2185 
2186 	bzero(plist->p_ids, sizeof (smb_lgpid_t) * plist->p_cnt);
2187 	plist->p_cnt = 0;
2188 
2189 	privs = grp->sg_privs;
2190 	if ((privs == NULL) || (privs->priv_cnt == 0))
2191 		return;
2192 
2193 	if (pcnt < privs->priv_cnt) {
2194 		assert(0);
2195 	}
2196 
2197 	for (i = 0; i < privs->priv_cnt; i++) {
2198 		if (privs->priv[i].attrs == SE_PRIVILEGE_ENABLED) {
2199 			plist->p_ids[plist->p_cnt++] =
2200 			    (uint8_t)privs->priv[i].luid.lo_part;
2201 		}
2202 	}
2203 }
2204 
2205 /*
2206  * smb_lgrp_decode_privset
2207  *
2208  * Decodes the privilege information read from group table
2209  * (nprivs, privs) into a binray format specified by the
2210  * privilege field of smb_group_t
2211  */
2212 static int
2213 smb_lgrp_decode_privset(smb_group_t *grp, char *nprivs, char *privs)
2214 {
2215 	smb_lgplist_t plist;
2216 	int i;
2217 
2218 	plist.p_cnt = atoi(nprivs);
2219 	if (strlen(privs) != plist.p_cnt)
2220 		return (SMB_LGRP_BAD_DATA);
2221 
2222 	plist.p_ids = (smb_lgpid_t *)privs;
2223 	grp->sg_privs = smb_privset_new();
2224 	if (grp->sg_privs == NULL)
2225 		return (SMB_LGRP_NO_MEMORY);
2226 
2227 	for (i = 0; i < plist.p_cnt; i++)
2228 		smb_privset_enable(grp->sg_privs, plist.p_ids[i]);
2229 
2230 	return (SMB_LGRP_SUCCESS);
2231 }
2232 
2233 /*
2234  * smb_lgrp_decode_members
2235  *
2236  * Decodes the members information read from group table
2237  * (nmembers, members) into a binary format specified by the
2238  * member fields of smb_group_t
2239  */
2240 static int
2241 smb_lgrp_decode_members(smb_group_t *grp, char *nmembers, char *members,
2242     sqlite *db)
2243 {
2244 	smb_lgmid_t *m_id;
2245 	smb_lgmid_t *m_ids;
2246 	smb_gsid_t *m_sid;
2247 	smb_gsid_t *m_sids;
2248 	int m_num;
2249 	int mids_size;
2250 	int i, rc;
2251 
2252 	grp->sg_nmembers = 0;
2253 	grp->sg_members = NULL;
2254 
2255 	m_num = atoi(nmembers);
2256 	mids_size = m_num * sizeof (smb_lgmid_t);
2257 	if ((m_ids = malloc(mids_size)) == NULL)
2258 		return (SMB_LGRP_NO_MEMORY);
2259 
2260 	m_sids = calloc(m_num, sizeof (smb_gsid_t));
2261 	if (m_sids == NULL) {
2262 		free(m_ids);
2263 		return (SMB_LGRP_NO_MEMORY);
2264 	}
2265 
2266 	(void) hextobin(members, strlen(members), (char *)m_ids, mids_size);
2267 
2268 	m_id = m_ids;
2269 	m_sid = m_sids;
2270 	for (i = 0; i < m_num; i++, m_id++, m_sid++) {
2271 		rc = smb_lgrp_getsid(m_id->m_idx, &m_id->m_rid, m_id->m_type,
2272 		    db, &m_sid->gs_sid);
2273 
2274 		if (rc != SMB_LGRP_SUCCESS) {
2275 			free(m_ids);
2276 			for (m_sid = m_sids; m_sid->gs_sid != NULL; m_sid++)
2277 				smb_sid_free(m_sid->gs_sid);
2278 			free(m_sids);
2279 			return (rc);
2280 		}
2281 
2282 		m_sid->gs_type = m_id->m_type;
2283 	}
2284 
2285 	free(m_ids);
2286 
2287 	grp->sg_nmembers = m_num;
2288 	grp->sg_members = m_sids;
2289 	return (SMB_LGRP_SUCCESS);
2290 }
2291 
2292 /*
2293  * smb_lgrp_decode
2294  *
2295  * Fills out the fields of the given group (grp) based in the
2296  * string information read from the group table. infolvl determines
2297  * which fields are requested and need to be decoded.
2298  *
2299  * Allocated memories must be freed by calling smb_lgrp_free()
2300  * upon successful return.
2301  */
2302 static int
2303 smb_lgrp_decode(smb_group_t *grp, char **values, int infolvl, sqlite *db)
2304 {
2305 	uint32_t sid_idx;
2306 	int rc;
2307 
2308 	if (infolvl == SMB_LGRP_INFO_NONE)
2309 		return (SMB_LGRP_SUCCESS);
2310 
2311 	if (infolvl & SMB_LGRP_INFO_NAME) {
2312 		grp->sg_name = strdup(values[SMB_LGRP_GTBL_NAME]);
2313 		if (grp->sg_name == NULL)
2314 			return (SMB_LGRP_NO_MEMORY);
2315 	}
2316 
2317 	if (infolvl & SMB_LGRP_INFO_CMNT) {
2318 		grp->sg_cmnt = strdup(values[SMB_LGRP_GTBL_CMNT]);
2319 		if (grp->sg_cmnt == NULL) {
2320 			smb_lgrp_free(grp);
2321 			return (SMB_LGRP_NO_MEMORY);
2322 		}
2323 	}
2324 
2325 
2326 	if (infolvl & SMB_LGRP_INFO_SID) {
2327 		sid_idx = atoi(values[SMB_LGRP_GTBL_SIDIDX]);
2328 		grp->sg_rid = atoi(values[SMB_LGRP_GTBL_SIDRID]);
2329 		grp->sg_attr = atoi(values[SMB_LGRP_GTBL_SIDATR]);
2330 		grp->sg_id.gs_type = atoi(values[SMB_LGRP_GTBL_SIDTYP]);
2331 		rc = smb_lgrp_getsid(sid_idx, &grp->sg_rid, grp->sg_id.gs_type,
2332 		    db, &grp->sg_id.gs_sid);
2333 		if (rc != SMB_LGRP_SUCCESS) {
2334 			smb_lgrp_free(grp);
2335 			return (rc);
2336 		}
2337 		grp->sg_domain = (sid_idx == SMB_LGRP_LOCAL_IDX)
2338 		    ? SMB_DOMAIN_LOCAL : SMB_DOMAIN_BUILTIN;
2339 	}
2340 
2341 	if (infolvl & SMB_LGRP_INFO_PRIV) {
2342 		rc = smb_lgrp_decode_privset(grp, values[SMB_LGRP_GTBL_NPRIVS],
2343 		    values[SMB_LGRP_GTBL_PRIVS]);
2344 
2345 		if (rc != SMB_LGRP_SUCCESS) {
2346 			smb_lgrp_free(grp);
2347 			return (rc);
2348 		}
2349 	}
2350 
2351 	if (infolvl & SMB_LGRP_INFO_MEMB) {
2352 		rc = smb_lgrp_decode_members(grp, values[SMB_LGRP_GTBL_NMEMBS],
2353 		    values[SMB_LGRP_GTBL_MEMBS], db);
2354 		if (rc != SMB_LGRP_SUCCESS) {
2355 			smb_lgrp_free(grp);
2356 			return (rc);
2357 		}
2358 	}
2359 
2360 	return (SMB_LGRP_SUCCESS);
2361 }
2362 
2363 /*
2364  * smb_lgrp_normalize_name
2365  *
2366  * Trim whitespace, validate the group name and convert it to lowercase.
2367  */
2368 static boolean_t
2369 smb_lgrp_normalize_name(char *name)
2370 {
2371 	(void) trim_whitespace(name);
2372 
2373 	if (smb_name_validate_account(name) != ERROR_SUCCESS)
2374 		return (B_FALSE);
2375 
2376 	(void) smb_strlwr(name);
2377 	return (B_TRUE);
2378 }
2379 
2380 /*
2381  * smb_lgrp_set_default_privs
2382  *
2383  * set default privileges for Administrators and Backup Operators
2384  */
2385 static void
2386 smb_lgrp_set_default_privs(smb_group_t *grp)
2387 {
2388 	if (smb_strcasecmp(grp->sg_name, "Administrators", 0) == 0) {
2389 		smb_privset_enable(grp->sg_privs, SE_TAKE_OWNERSHIP_LUID);
2390 		return;
2391 	}
2392 
2393 	if (smb_strcasecmp(grp->sg_name, "Backup Operators", 0) == 0) {
2394 		smb_privset_enable(grp->sg_privs, SE_BACKUP_LUID);
2395 		smb_privset_enable(grp->sg_privs, SE_RESTORE_LUID);
2396 		return;
2397 	}
2398 }
2399 
2400 /*
2401  * smb_lgrp_getsid
2402  *
2403  * Returns a SID based on the provided information
2404  * If dom_idx is 0, it means 'rid' contains a UID/GID and the
2405  * returned SID will be a local SID. If dom_idx is not 0 then
2406  * the domain SID will be fetched from the domain table.
2407  */
2408 static int
2409 smb_lgrp_getsid(int dom_idx, uint32_t *rid, uint16_t sid_type,
2410     sqlite *db, smb_sid_t **sid)
2411 {
2412 	smb_sid_t *dom_sid = NULL;
2413 	smb_sid_t *res_sid = NULL;
2414 	idmap_stat stat;
2415 	int id_type;
2416 	int rc;
2417 
2418 	*sid = NULL;
2419 	if (dom_idx == SMB_LGRP_LOCAL_IDX) {
2420 		id_type = (sid_type == SidTypeUser)
2421 		    ? SMB_IDMAP_USER : SMB_IDMAP_GROUP;
2422 		stat = smb_idmap_getsid(*rid, id_type, &res_sid);
2423 		if (stat != IDMAP_SUCCESS) {
2424 			syslog(LOG_ERR, "smb_lgrp_getsid: "
2425 			    "failed to get a SID for %s id=%u (%d)",
2426 			    (id_type == SMB_IDMAP_USER) ? "user" : "group",
2427 			    *rid, stat);
2428 			return (SMB_LGRP_NO_SID);
2429 		}
2430 
2431 		/*
2432 		 * Make sure the returned SID is local
2433 		 */
2434 		if (!smb_sid_indomain(smb_localgrp.lg_machine_sid, res_sid)) {
2435 			syslog(LOG_ERR, "smb_lgrp_getsid: "
2436 			    "local %s (%u) is mapped to a non-local SID",
2437 			    (id_type == SMB_IDMAP_USER) ? "user" : "group",
2438 			    *rid);
2439 			smb_sid_free(res_sid);
2440 			return (SMB_LGRP_SID_NOTLOCAL);
2441 		}
2442 
2443 		(void) smb_sid_getrid(res_sid, rid);
2444 		*sid = res_sid;
2445 		return (SMB_LGRP_SUCCESS);
2446 	}
2447 
2448 	rc = smb_lgrp_dtbl_getsid(db, dom_idx, &dom_sid);
2449 	if (rc != SMB_LGRP_SUCCESS) {
2450 		syslog(LOG_ERR, "smb_lgrp_getsid: %s", smb_lgrp_strerror(rc));
2451 		return (SMB_LGRP_DB_ERROR);
2452 	}
2453 
2454 	res_sid = smb_sid_splice(dom_sid, *rid);
2455 	smb_sid_free(dom_sid);
2456 	if (res_sid == NULL) {
2457 		syslog(LOG_ERR, "smb_lgrp_getsid: %s", smb_lgrp_strerror(rc));
2458 		return (SMB_LGRP_NO_MEMORY);
2459 	}
2460 
2461 	*sid = res_sid;
2462 	return (SMB_LGRP_SUCCESS);
2463 }
2464 
2465 /*
2466  * smb_lgrp_getgid
2467  *
2468  * Converts given local RID to a local gid since for user
2469  * defined local groups, gid is stored in the table.
2470  */
2471 static int
2472 smb_lgrp_getgid(uint32_t rid, gid_t *gid)
2473 {
2474 	smb_sid_t *sid;
2475 	int idtype;
2476 	int rc;
2477 
2478 	if ((sid = smb_sid_splice(smb_localgrp.lg_machine_sid, rid)) == NULL)
2479 		return (SMB_LGRP_NO_MEMORY);
2480 
2481 	idtype = SMB_IDMAP_GROUP;
2482 	rc = smb_idmap_getid(sid, gid, &idtype);
2483 	smb_sid_free(sid);
2484 
2485 	return ((rc == IDMAP_SUCCESS) ? SMB_LGRP_SUCCESS : SMB_LGRP_NOT_FOUND);
2486 }
2487 
2488 /*
2489  * smb_lgrp_exists
2490  *
2491  * Returns B_TRUE if the local group with the given name exists.
2492  * Otherwise, returns B_FALSE.
2493  */
2494 static boolean_t
2495 smb_lgrp_exists(char *gname)
2496 {
2497 	sqlite *db;
2498 	boolean_t rc;
2499 
2500 	if (!smb_lgrp_normalize_name(gname))
2501 		return (B_FALSE);
2502 
2503 	db = smb_lgrp_db_open(SMB_LGRP_DB_ORD);
2504 	if (db == NULL)
2505 		return (B_FALSE);
2506 
2507 	rc = smb_lgrp_gtbl_exists(db, gname);
2508 	smb_lgrp_db_close(db);
2509 
2510 	return (rc);
2511 }
2512 
2513 /*
2514  * smb_lgrp_pgrp_valid_gname
2515  *
2516  * Validate posix group name string.
2517  */
2518 static int
2519 smb_lgrp_pgrp_valid_gname(char *group)
2520 {
2521 	char *ptr = group;
2522 	char c;
2523 	int len = 0;
2524 	int badchar = 0;
2525 
2526 	if (!group || !*group)
2527 		return (SMB_LGRP_PGRP_INVALID);
2528 
2529 	for (c = *ptr; c != NULL; ptr++, c = *ptr) {
2530 		len++;
2531 		if (!isprint(c) || (c == ':') || (c == '\n'))
2532 			return (SMB_LGRP_PGRP_INVALID);
2533 
2534 		if (!(islower(c) || isdigit(c)))
2535 			badchar++;
2536 	}
2537 
2538 	if ((len > SMB_LGRP_PGRP_MAXGLEN - 1) || (badchar != 0))
2539 		return (SMB_LGRP_PGRP_INVALID);
2540 
2541 	if (getgrnam(group) != NULL)
2542 		return (SMB_LGRP_PGRP_NOTUNIQUE);
2543 
2544 	return (SMB_LGRP_PGRP_UNIQUE);
2545 }
2546 
2547 /*
2548  * smb_lgrp_pgrp_valid_gid
2549  *
2550  * Check to see that the gid is not a reserved gid
2551  * -- nobody (60001), noaccess (60002) or nogroup (65534)
2552  */
2553 static int
2554 smb_lgrp_pgrp_valid_gid(gid_t gid)
2555 {
2556 	return (gid != 60001 && gid != 60002 && gid != 65534);
2557 }
2558 
2559 /*
2560  * smb_lgrp_pgrp_findnextgid(void)
2561  *
2562  * This method finds the next valid GID.
2563  * It sorts the used GIDs in decreasing order to return MAXUSED + 1.
2564  * It then adds one to obtain the next valid GID.
2565  * On failure, -1 is returned. On success, a valid GID is returned.
2566  */
2567 static int
2568 smb_lgrp_pgrp_findnextgid(void)
2569 {
2570 	FILE *fptr;
2571 	gid_t last, next;
2572 	int gid;
2573 
2574 	if ((fptr = popen("exec sh -c "
2575 	    "\"getent group|cut -f3 -d:|sort -nr|uniq \" 2>/dev/null",
2576 	    "r")) == NULL)
2577 		return (-1);
2578 
2579 	if (fscanf(fptr, "%u\n", &next) == EOF) {
2580 		(void) pclose(fptr);
2581 		return (SMB_LGRP_PGRP_DEFRID + 1);
2582 	}
2583 
2584 	last = MAXUID;
2585 	gid = -1;
2586 	do {
2587 		if (!smb_lgrp_pgrp_valid_gid(next))
2588 			continue;
2589 
2590 		if (next <= SMB_LGRP_PGRP_DEFRID) {
2591 			if (last != SMB_LGRP_PGRP_DEFRID + 1)
2592 				gid = SMB_LGRP_PGRP_DEFRID + 1;
2593 			break;
2594 		}
2595 
2596 		if ((gid = next + 1) != last) {
2597 			while (!smb_lgrp_pgrp_valid_gid((gid_t)gid))
2598 				gid++;
2599 			if (gid > 0 && gid < last)
2600 				break;
2601 		}
2602 
2603 		gid = -1;
2604 		last = next;
2605 	} while (fscanf(fptr, "%u\n", &next) != EOF);
2606 
2607 	(void) pclose(fptr);
2608 	return (gid);
2609 }
2610 
2611 /*
2612  * smb_lgrp_pgrp_add
2613  *
2614  * Create a posix group with the given name.
2615  * This group will be added to the /etc/group file.
2616  */
2617 static int
2618 smb_lgrp_pgrp_add(char *group)
2619 {
2620 	FILE *etcgrp;
2621 	FILE *etctmp;
2622 	int o_mask, gret;
2623 	int newdone = 0;
2624 	struct stat sb;
2625 	char buf[SMB_LGRP_PGRP_GRPBUFSIZ];
2626 	gid_t gid;
2627 	int rc = 0;
2628 
2629 	rc = smb_lgrp_pgrp_valid_gname(group);
2630 	if ((rc == SMB_LGRP_PGRP_INVALID) || (rc == SMB_LGRP_PGRP_NOTUNIQUE))
2631 		return (-1);
2632 
2633 	if ((gret = smb_lgrp_pgrp_findnextgid()) < 0)
2634 		return (-1);
2635 	gid = gret;
2636 
2637 	if ((etcgrp = fopen(SMB_LGRP_PGRP_GROUP, "r")) == NULL)
2638 		return (-1);
2639 
2640 	if (fstat(fileno(etcgrp), &sb) < 0)
2641 		sb.st_mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH;
2642 
2643 	o_mask = umask(077);
2644 	etctmp = fopen(SMB_LGRP_PGRP_GRPTMP, "w+");
2645 	(void) umask(o_mask);
2646 
2647 	if (etctmp == NULL) {
2648 		(void) fclose(etcgrp);
2649 		return (-1);
2650 	}
2651 
2652 	if (lockf(fileno(etctmp), F_LOCK, 0) != 0) {
2653 		(void) fclose(etcgrp);
2654 		(void) fclose(etctmp);
2655 		(void) unlink(SMB_LGRP_PGRP_GRPTMP);
2656 		return (-1);
2657 	}
2658 
2659 	if (fchmod(fileno(etctmp), sb.st_mode) != 0 ||
2660 	    fchown(fileno(etctmp), sb.st_uid, sb.st_gid) != 0) {
2661 		(void) lockf(fileno(etctmp), F_ULOCK, 0);
2662 		(void) fclose(etcgrp);
2663 		(void) fclose(etctmp);
2664 		(void) unlink(SMB_LGRP_PGRP_GRPTMP);
2665 		return (-1);
2666 	}
2667 
2668 	while (fgets(buf, SMB_LGRP_PGRP_GRPBUFSIZ, etcgrp) != NULL) {
2669 		/* Check for NameService reference */
2670 		if (!newdone && (buf[0] == '+' || buf[0] == '-')) {
2671 			(void) fprintf(etctmp, "%s::%u:\n", group, gid);
2672 			newdone = 1;
2673 		}
2674 
2675 		(void) fputs(buf, etctmp);
2676 	}
2677 	(void) fclose(etcgrp);
2678 
2679 	if (!newdone)
2680 		(void) fprintf(etctmp, "%s::%u:\n", group, gid);
2681 
2682 	if (rename(SMB_LGRP_PGRP_GRPTMP, SMB_LGRP_PGRP_GROUP) < 0) {
2683 		(void) lockf(fileno(etctmp), F_ULOCK, 0);
2684 		(void) fclose(etctmp);
2685 		(void) unlink(SMB_LGRP_PGRP_GRPTMP);
2686 		return (-1);
2687 	}
2688 
2689 	(void) lockf(fileno(etctmp), F_ULOCK, 0);
2690 	(void) fclose(etctmp);
2691 	return (0);
2692 }
2693