1/*
2 * Copyright (c) 2007-2009 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24#include <stdio.h>
25#include <stdlib.h>
26#include <stdbool.h>
27#include <errno.h>
28#include <string.h>
29#include <grp.h>
30#include <pwd.h>
31#include <membership.h>
32#include <membershipPriv.h>
33#include <sys/syslimits.h>
34
35#define _PAM_EXTERN_FUNCTIONS
36#include <security/pam_modules.h>
37#include <security/pam_appl.h>
38#include <security/openpam.h>
39
40#define MODULE_NAME "pam_sacl"
41
42/* Note to self: To enable debug logging, we also have to make the syslog
43 * *.debug level go somewhere.
44 */
45#define DEBUG_MESSAGE(format, ...) \
46    if (NULL != debug) { \
47	openpam_log(PAM_LOG_DEBUG, format, __VA_ARGS__); \
48    }
49
50
51PAM_EXTERN int pam_sm_acct_mgmt(pam_handle_t * pamh, int flags,
52			int argc, const char ** argv)
53{
54	const char *	service = NULL;
55	const char *	username = NULL;
56	const char *	debug = NULL;
57	bool		allow_trustacct = false;
58
59	struct passwd *pwd = NULL;
60	struct passwd pwdbuf;
61	char pwbuffer[2 * PATH_MAX];
62
63	uuid_t	user_uuid;
64	int	err;
65	int	ismember;
66
67	service = openpam_get_option(pamh, "sacl_service");
68	allow_trustacct = openpam_get_option(pamh, "allow_trustacct");
69	debug = openpam_get_option(pamh, "debug");
70
71	if (!service) {
72		DEBUG_MESSAGE("%s: missing service option", MODULE_NAME);
73		return PAM_IGNORE;
74	}
75
76	if (pam_get_user(pamh, &username, NULL) != PAM_SUCCESS ||
77	    username == NULL || *username == '\0') {
78		DEBUG_MESSAGE("%s: missing username", MODULE_NAME);
79		return PAM_SYSTEM_ERR;
80	}
81
82	DEBUG_MESSAGE("%s: checking if account '%s' can access service '%s'",
83		    MODULE_NAME, username, service);
84
85	/* Since computer trust accounts in OD are not user accounts, you can't
86	 * add them to a SACL, so we always let them through (if the option is
87	 * set). A computer trust account has a username ending in '$' and no
88	 * corresponding user account (ie. no passwd entry).
89	 */
90	if (allow_trustacct) {
91		const char * c;
92
93		c = strrchr(username, '$');
94		if (c && *(c + 1) == '\0' && getpwnam_r(username, &pwdbuf, pwbuffer, sizeof(pwbuffer), &pwd) == 0) {
95			DEBUG_MESSAGE("%s: allowing '%s' because it is a "
96				"computer trust account",
97				MODULE_NAME, username);
98			return PAM_SUCCESS;
99		}
100	}
101
102	/* Get the UUID. This will fail if the user is is logging in over
103	 * SMB, is specifed as DOMAIN\user or user@REALM and the directory
104	 * does not have the aliases we need.
105	 */
106	if (mbr_user_name_to_uuid(username, user_uuid)) {
107		char * sacl_group;
108
109		/* We couldn't map the user to a UID, but we only care about
110		 * this if the relevant SACL groups exist.
111		 */
112
113		if (asprintf(&sacl_group, "com.apple.access_%s\n",
114							service) == -1) {
115			return PAM_SYSTEM_ERR;
116		}
117
118		if (getgrnam(sacl_group) == NULL &&
119		    getgrnam("com.apple.access_all_services") == NULL) {
120
121			DEBUG_MESSAGE("%s: allowing '%s' "
122				    "due to absence of service ACL",
123				    MODULE_NAME, username);
124
125			free(sacl_group);
126			return PAM_SUCCESS;
127		}
128
129		DEBUG_MESSAGE("%s: denying '%s' due to missing UUID",
130			MODULE_NAME, username);
131
132		free(sacl_group);
133		return PAM_PERM_DENIED;
134	}
135
136	err = mbr_check_service_membership(user_uuid, service, &ismember);
137	if (err) {
138	        if (err == ENOENT) {
139	                /* Service ACLs not configured. */
140			DEBUG_MESSAGE("%s: allowing '%s' "
141				"due to unconfigured service ACLs",
142				MODULE_NAME, username);
143	                return PAM_SUCCESS;
144	        }
145
146		DEBUG_MESSAGE("%s: denying '%s' "
147			"due to failed service ACL check (errno=%d)",
148			MODULE_NAME, username, err);
149
150	        return PAM_PERM_DENIED;
151	}
152
153        if (ismember) {
154		DEBUG_MESSAGE("%s: allowing '%s'", MODULE_NAME, username);
155		return PAM_SUCCESS;
156	} else {
157		DEBUG_MESSAGE("%s: denying '%s' "
158			"due to failed service ACL check",
159			MODULE_NAME, username);
160		return PAM_PERM_DENIED;
161	}
162}
163
164#ifdef PAM_STATIC
165PAM_MODULE_ENTRY(MODULE_NAME);
166#endif
167
168