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