1/*-
2 * Copyright (c) 2003 Networks Associates Technology, Inc.
3 * All rights reserved.
4 * Copyright (c) 2008-2009 Apple Inc. All rights reserved.
5 *
6 * Portions of this software were developed for the FreeBSD Project by
7 * ThinkSec AS and NAI Labs, the Security Research Division of Network
8 * Associates, Inc.  under DARPA/SPAWAR contract N66001-01-C-8035
9 * ("CBOSS"), as part of the DARPA CHATS research program.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 *    notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 *    notice, this list of conditions and the following disclaimer in the
18 *    documentation and/or other materials provided with the distribution.
19 * 3. The name of the author may not be used to endorse or promote
20 *    products derived from this software without specific prior written
21 *    permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36#include <sys/cdefs.h>
37
38#include <sys/types.h>
39#include <sys/syslimits.h>
40
41#include <grp.h>
42#include <pwd.h>
43#include <stdarg.h>
44#include <stdio.h>
45#include <string.h>
46#include <unistd.h>
47#ifdef __APPLE__
48#include <membership.h>
49#include <stdlib.h>
50#endif /* __APPLE__ */
51
52#define PAM_SM_AUTH
53
54#include <security/pam_appl.h>
55#include <security/pam_modules.h>
56#include <security/openpam.h>
57
58PAM_EXTERN int
59pam_sm_acct_mgmt(pam_handle_t *pamh, int flags __unused,
60    int argc __unused, const char *argv[] __unused)
61{
62	const char *group, *user;
63	const void *ruser;
64#ifndef __APPLE__
65	char *const *list;
66#endif /* !__APPLE__ */
67	struct passwd *pwd;
68	struct passwd pwdbuf;
69	char pwbuffer[2 * PATH_MAX];
70	struct group *grp;
71#ifdef __APPLE__
72	char *str1, *str, *p;
73	int found_group = 0;
74	uuid_t u_uuid, g_uuid;
75	int ismember;
76#endif /* __APPLE__ */
77
78	/* get target account */
79	if (pam_get_user(pamh, &user, NULL) != PAM_SUCCESS ||
80	    user == NULL || getpwnam_r(user, &pwdbuf, pwbuffer, sizeof(pwbuffer), &pwd) != 0 || pwd == NULL) {
81		openpam_log(PAM_LOG_ERROR, "Unable to obtain the username.");
82		return (PAM_AUTH_ERR);
83	}
84	if (pwd->pw_uid != 0 && openpam_get_option(pamh, "root_only")) {
85		openpam_log(PAM_LOG_DEBUG, "The root_only option means root only.");
86		return (PAM_IGNORE);
87	}
88
89	/* get applicant */
90	if (openpam_get_option(pamh, "ruser") &&
91		(pam_get_item(pamh, PAM_RUSER, &ruser) != PAM_SUCCESS || ruser == NULL ||
92		 getpwnam_r(ruser, &pwdbuf, pwbuffer, sizeof(pwbuffer), &pwd) != 0 || pwd == NULL)) {
93		openpam_log(PAM_LOG_ERROR, "Unable to obtain the remote username.");
94		return (PAM_AUTH_ERR);
95	}
96
97	/* get regulating group */
98	if ((group = openpam_get_option(pamh, "group")) == NULL) {
99		group = "wheel";
100		openpam_log(PAM_LOG_DEBUG, "With no group specfified, I am defaulting to wheel.");
101	}
102#ifdef __APPLE__
103	str1 = str = strdup(group);
104	while ((p = strsep(&str, ",")) != NULL) {
105		if ((grp = getgrnam(p)) == NULL || grp->gr_mem == NULL)
106			continue;
107
108		/* check if the group is empty */
109		if (*grp->gr_mem == NULL)
110			continue;
111
112		found_group = 1;
113
114		/* check membership */
115		if (mbr_uid_to_uuid(pwd->pw_uid, u_uuid) != 0)
116			continue;
117		if (mbr_gid_to_uuid(grp->gr_gid, g_uuid) != 0)
118			continue;
119		if (mbr_check_membership(u_uuid, g_uuid, &ismember) != 0)
120			continue;
121		if (ismember)
122			goto found;
123	}
124	if (!found_group) {
125		openpam_log(PAM_LOG_DEBUG, "The specified group (%s) could not be found.", group);
126		goto failed;
127	}
128#else /* !__APPLE__ */
129	if ((grp = getgrnam(group)) == NULL || grp->gr_mem == NULL) {
130		openpam_log(PAM_LOG_DEBUG, "The specified group (%s) is NULL.", group);
131		goto failed;
132	}
133
134	/* check if the group is empty */
135	if (*grp->gr_mem == NULL) {
136		openpam_log(PAM_LOG_DEBUG, "The specified group (%s) is empty.", group);
137		goto failed;
138	}
139
140	/* check membership */
141	if (pwd->pw_gid == grp->gr_gid)
142		goto found;
143	for (list = grp->gr_mem; *list != NULL; ++list)
144		if (strcmp(*list, pwd->pw_name) == 0)
145			goto found;
146#endif /* __APPLE__ */
147
148 not_found:
149	openpam_log(PAM_LOG_DEBUG, "The group check failed.");
150#ifdef __APPLE__
151	free(str1);
152#endif /* __APPLE__ */
153	if (openpam_get_option(pamh, "deny"))
154		return (PAM_SUCCESS);
155	return (PAM_AUTH_ERR);
156 found:
157	openpam_log(PAM_LOG_DEBUG, "The group check succeeded.");
158#ifdef __APPLE__
159	free(str1);
160#endif /* __APPLE__ */
161	if (openpam_get_option(pamh, "deny"))
162		return (PAM_AUTH_ERR);
163	return (PAM_SUCCESS);
164 failed:
165	if (openpam_get_option(pamh, "fail_safe"))
166		goto found;
167	else
168		goto not_found;
169}
170
171
172PAM_MODULE_ENTRY("pam_group");
173