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