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