1/*-
2 * Copyright 2010 Apple Inc
3 */
4
5#include <sys/cdefs.h>
6
7#include <sys/types.h>
8#include <sys/stat.h>
9#include <sys/param.h>
10#include <stdio.h>
11#include <stdlib.h>
12#include <string.h>
13#include <unistd.h>
14#include <pwd.h>
15
16#include <OpenDirectory/OpenDirectory.h>
17
18#include <GSS/gssapi_ntlm.h>
19#include <GSS/gssapi_spi.h>
20
21#define	PAM_SM_AUTH
22#define	PAM_SM_ACCOUNT
23#define	PAM_SM_PASSWORD
24
25#include <security/pam_appl.h>
26#include <security/pam_modules.h>
27#include <security/openpam.h>
28
29#include "Common.h"
30
31#define PAM_OPT_DEBUG		"debug"
32
33#define	PAM_LOG(...) \
34	openpam_log(PAM_LOG_DEBUG, __VA_ARGS__)
35
36#define	PAM_VERBOSE_ERROR(...) \
37openpam_log(PAM_LOG_ERROR, __VA_ARGS__)
38
39static const char *password_key = "NTLMPWD";
40
41
42/*
43 * authentication management
44 */
45
46PAM_EXTERN int
47pam_sm_authenticate(pam_handle_t *pamh, int flags __unused,
48    int argc __unused, const char *argv[] __unused)
49{
50	int retval;
51	const char *password;
52
53	PAM_LOG("pam_sm_authenticate: ntlm");
54
55	/* get password */
56	retval = pam_get_authtok(pamh, PAM_AUTHTOK, &password, NULL);
57	if (retval != PAM_SUCCESS)
58		return retval;
59
60	retval = pam_setenv(pamh, password_key, password, 1);
61	if (retval != PAM_SUCCESS)
62		return retval;
63
64	return PAM_IGNORE;
65}
66
67static void
68ac_complete(void *ctx, OM_uint32 major, gss_status_id_t status,
69			gss_cred_id_t cred, gss_OID_set oids, OM_uint32 time_rec)
70{
71    OM_uint32 junk;
72    gss_release_cred(&junk, &cred);
73    gss_release_oid_set(&junk, &oids);
74    PAM_LOG("ac_complete returned: %d for %d", major, geteuid());
75}
76
77
78
79PAM_EXTERN int
80pam_sm_setcred(pam_handle_t *pamh, int flags,
81    int argc __unused, const char *argv[] __unused)
82{
83	gss_auth_identity_desc identity;
84	const char *user, *password;
85	struct passwd *pwd;
86	struct passwd pwdbuf;
87	char pwbuffer[2 * PATH_MAX];
88	int retval;
89	uid_t euid = geteuid();
90	gid_t egid = getegid();
91	ODRecordRef record = NULL;
92	CFArrayRef array = NULL;
93	CFIndex i, count;
94
95	PAM_LOG("pam_sm_setcred: ntlm");
96
97	memset(&identity, 0, sizeof(identity));
98
99	/* Get username */
100	retval = pam_get_item(pamh, PAM_USER, (const void **)&user);
101	if (retval != PAM_SUCCESS) {
102		PAM_LOG("pam_sm_setcred: ntlm user can't be found");
103		goto cleanup;
104	}
105
106	if (getpwnam_r(user, &pwdbuf, pwbuffer, sizeof(pwbuffer), &pwd) != 0 || pwd == NULL) {
107		PAM_LOG("pam_sm_setcred: ntlm user %s doesn't exists", user);
108		retval = PAM_USER_UNKNOWN;
109		goto cleanup;
110	}
111
112	password = pam_getenv(pamh, password_key);
113	if (password == NULL) {
114		PAM_LOG("pam_sm_setcred: ntlm user %s doesn't have a password", user);
115		retval = PAM_IGNORE;
116		goto cleanup;
117	}
118
119	retval = od_record_create_cstring(pamh, &record, user);
120	if (retval || record == NULL) {
121		retval = PAM_IGNORE;
122		goto cleanup;
123	}
124
125	array = ODRecordCopyValues(record, kODAttributeTypeAuthenticationAuthority, NULL);
126	if (array == NULL) {
127		PAM_LOG("pam_sm_setcred: ntlm user %s doesn't have auth authority", user);
128		retval = PAM_IGNORE;
129		goto cleanup;
130	}
131
132	identity.username = (char *)user;
133	identity.password = (char *)password;
134
135	count = CFArrayGetCount(array);
136	for (i = 0; i < count && identity.realm == NULL; i++) {
137		CFStringRef val = CFArrayGetValueAtIndex(array, i);
138		if (NULL == val || CFGetTypeID(val) != CFStringGetTypeID())
139			break;
140
141		if (!CFStringHasPrefix(val, CFSTR(";NetLogon;")))
142			continue;
143
144		CFArrayRef parts = CFStringCreateArrayBySeparatingStrings(kCFAllocatorDefault, val, CFSTR(";"));
145		if (parts == NULL)
146			continue;
147
148		if (CFArrayGetCount(parts) < 4) {
149			CFRelease(parts);
150			continue;
151		}
152
153		CFStringRef domain = CFArrayGetValueAtIndex(parts, 3);
154
155		retval = cfstring_to_cstring(domain, &identity.realm);
156		CFRelease(parts);
157		if (retval)
158			goto cleanup;
159	}
160
161	if (identity.realm == NULL) {
162		PAM_LOG("pam_sm_setcred: no domain found skipping");
163		retval = PAM_IGNORE;
164		goto cleanup;
165	}
166
167	if (euid == 0) {
168		if (setegid(pwd->pw_gid) != 0) {
169			retval = PAM_SERVICE_ERR;
170			goto cleanup;
171		}
172		if (seteuid(pwd->pw_uid) != 0) {
173			retval = PAM_SERVICE_ERR;
174			goto cleanup;
175		}
176	}
177
178	(void)gss_acquire_cred_ex_f(NULL,
179				    GSS_C_NO_NAME,
180				    0,
181				    GSS_C_INDEFINITE,
182				    GSS_NTLM_MECHANISM,
183				    GSS_C_INITIATE,
184				    &identity,
185				    NULL,
186				    ac_complete);
187
188	if (euid == 0) {
189		seteuid(euid);
190		setegid(egid);
191	}
192
193	PAM_LOG("pam_sm_setcred: ntlm done, used domain: %s", identity.realm);
194
195cleanup:
196	if (record)
197		CFRelease(record);
198	if (array)
199		CFRelease(array);
200	if (identity.realm)
201		free(identity.realm);
202	pam_unsetenv(pamh, password_key);
203	return retval;
204}
205
206/*
207 * account management
208 */
209PAM_EXTERN int
210pam_sm_acct_mgmt(pam_handle_t *pamh, int flags __unused,
211    int argc __unused, const char *argv[] __unused)
212{
213	return PAM_SUCCESS;
214}
215
216/*
217 * password management
218 */
219PAM_EXTERN int
220pam_sm_chauthtok(pam_handle_t *pamh, int flags,
221    int argc __unused, const char *argv[] __unused)
222{
223	return PAM_AUTHTOK_ERR;
224}
225
226PAM_MODULE_ENTRY("pam_ntlm");
227