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