1#include <asl.h>
2#include <stdio.h>
3#include <unistd.h>
4#include <sys/stat.h>
5
6#include <security/pam_appl.h>
7#include <security/pam_modules.h>
8#include <security/openpam.h>
9
10
11#include <CoreFoundation/CoreFoundation.h>
12#include <DirectoryService/DirectoryService.h>
13#include <OpenDirectory/OpenDirectory.h>
14#include <OpenDirectory/OpenDirectoryPriv.h>
15#include <ServerInformation/ServerInformation.h>
16
17#include "Common.h"
18
19#if !defined(kDSValueAuthAuthorityDisabledUser)
20#define kDSValueAuthAuthorityDisabledUser ";DisabledUser;"
21#endif
22
23#define kOSInstall_mpkg "/System/Installation/Packages/OSInstall.mpkg"
24#define kOSInstall_collection "/System/Installation/Packages/OSInstall.collection"
25
26enum {
27	kWaitSeconds       =  1,
28	kMaxIterationCount = 30
29};
30
31int
32cfboolean_get_value(CFTypeRef p)
33{
34	int value = 0;
35	int retval = 0;
36
37	if (NULL == p) {
38		goto cleanup;
39	}
40
41	if (CFBooleanGetTypeID() == CFGetTypeID(p))
42		retval = CFBooleanGetValue(p);
43	else if (CFNumberGetTypeID() == CFGetTypeID(p) && CFNumberGetValue(p, kCFNumberIntType, &value))
44		retval = value;
45	else
46		retval = 0;
47
48cleanup:
49	if (PAM_SUCCESS != retval)
50		openpam_log(PAM_LOG_ERROR, "failed: %d", retval);
51
52	return retval;
53}
54
55int
56cstring_to_cfstring(const char *val, CFStringRef *buffer)
57{
58	int retval = PAM_BUF_ERR;
59
60	if (NULL == val || NULL == buffer) {
61		openpam_log(PAM_LOG_DEBUG, "NULL argument passed");
62		retval = PAM_SERVICE_ERR;
63		goto cleanup;
64	}
65
66	*buffer = CFStringCreateWithCString(kCFAllocatorDefault, val, kCFStringEncodingUTF8);
67	if (NULL == *buffer) {
68		openpam_log(PAM_LOG_DEBUG, "CFStringCreateWithCString() failed");
69		retval = PAM_BUF_ERR;
70		goto cleanup;
71	}
72
73	retval =  PAM_SUCCESS;
74
75cleanup:
76	if (PAM_SUCCESS != retval)
77		openpam_log(PAM_LOG_ERROR, "failed: %d", retval);
78
79	return retval;
80}
81
82int
83cfstring_to_cstring(const CFStringRef val, char **buffer)
84{
85	CFIndex maxlen = 0;
86	int retval = PAM_BUF_ERR;
87
88	if (NULL == val || NULL == buffer) {
89		openpam_log(PAM_LOG_DEBUG, "NULL argument passed");
90		retval = PAM_SERVICE_ERR;
91		goto cleanup;
92	}
93
94	maxlen = CFStringGetMaximumSizeForEncoding(CFStringGetLength(val), kCFStringEncodingUTF8);
95	*buffer = calloc(maxlen + 1, sizeof(char));
96	if (NULL == *buffer) {
97		openpam_log(PAM_LOG_DEBUG, "malloc() failed");
98		retval = PAM_BUF_ERR;
99		goto cleanup;
100	}
101
102	if (CFStringGetCString(val, *buffer, maxlen + 1, kCFStringEncodingUTF8)) {
103		retval =  PAM_SUCCESS;
104	} else {
105		openpam_log(PAM_LOG_DEBUG, "CFStringGetCString failed.");
106		free(*buffer);
107		*buffer = NULL;
108	}
109
110cleanup:
111	if (PAM_SUCCESS != retval)
112		openpam_log(PAM_LOG_ERROR, "failed: %d", retval);
113
114	return retval;
115}
116
117#ifdef OPENDIRECTORY_CACHE
118static void
119cleanup_cache(pam_handle_t *pamh, void *data, int pam_end_status)
120{
121    CFRelease((CFTypeRef)data);
122}
123#endif /* OPENDIRECTORY_CACHE */
124
125int
126od_record_create(pam_handle_t *pamh, ODRecordRef *record, CFStringRef cfUser)
127{
128	int retval = PAM_SERVICE_ERR;
129	const int attr_num = 5;
130
131	ODNodeRef cfNode = NULL;
132	CFErrorRef cferror = NULL;
133	CFArrayRef attrs = NULL;
134	CFTypeRef cfVals[attr_num];
135
136	if (NULL == record || NULL == cfUser) {
137		openpam_log(PAM_LOG_DEBUG, "NULL argument passed");
138		retval = PAM_SERVICE_ERR;
139		goto cleanup;
140	}
141
142#ifdef OPENDIRECTORY_CACHE
143#define CFRECORDNAME_CACHE "CFRecordName"
144#define CFRECORDNAME_NAME CFSTR("name")
145#define CFRECORDNAME_RECORD CFSTR("record")
146
147	CFDictionaryRef cfdict;
148	CFStringRef cachedUser;
149
150	if (pam_get_data(pamh, CFRECORDNAME_CACHE, (void *)&cfdict) == PAM_SUCCESS &&
151	    (CFGetTypeID(cfdict) == CFDictionaryGetTypeID()) &&
152	    (cachedUser = CFDictionaryGetValue(cfdict, CFRECORDNAME_NAME)) != NULL &&
153	    CFGetTypeID(cachedUser) == CFStringGetTypeID() &&
154	    CFStringCompare(cfUser, cachedUser, 0) == kCFCompareEqualTo &&
155	    (*record = (ODRecordRef)CFDictionaryGetValue(cfdict, CFRECORDNAME_RECORD)) != NULL)
156	{
157		CFRetain(*record);
158		return PAM_SUCCESS;
159	}
160#endif /* OPENDIRECTORY_CACHE */
161
162	int current_iterations = 0;
163
164	cfNode = ODNodeCreateWithNodeType(kCFAllocatorDefault,
165					  kODSessionDefault,
166					  eDSAuthenticationSearchNodeName,
167					  &cferror);
168	if (NULL == cfNode || NULL != cferror) {
169		openpam_log(PAM_LOG_ERROR, "ODNodeCreateWithNodeType failed.");
170		retval = PAM_SERVICE_ERR;
171		goto cleanup;
172	}
173
174	cfVals[0] = kODAttributeTypeAuthenticationAuthority;
175	cfVals[1] = kODAttributeTypeHomeDirectory;
176	cfVals[2] = kODAttributeTypeNFSHomeDirectory;
177	cfVals[3] = kODAttributeTypeUserShell;
178	cfVals[4] = kODAttributeTypeUniqueID;
179	attrs = CFArrayCreate(kCFAllocatorDefault, cfVals, (CFIndex)attr_num, &kCFTypeArrayCallBacks);
180	if (NULL == attrs) {
181		openpam_log(PAM_LOG_DEBUG, "CFArrayCreate() failed");
182		retval = PAM_BUF_ERR;
183		goto cleanup;
184	}
185
186	retval = PAM_SERVICE_ERR;
187	while (current_iterations <= kMaxIterationCount) {
188		CFIndex unreachable_count = 0;
189		CFArrayRef unreachable_nodes = ODNodeCopyUnreachableSubnodeNames(cfNode, NULL);
190		if (unreachable_nodes) {
191			unreachable_count = CFArrayGetCount(unreachable_nodes);
192			CFRelease(unreachable_nodes);
193			openpam_log(PAM_LOG_DEBUG, "%lu OD nodes unreachable.", unreachable_count);
194		}
195
196		*record = ODNodeCopyRecord(cfNode, kODRecordTypeUsers, cfUser, attrs, &cferror);
197		if (*record)
198			break;
199		if (0 == unreachable_count)
200			break;
201
202		openpam_log(PAM_LOG_DEBUG, "Waiting %d seconds for nodes to become reachable", kWaitSeconds);
203		sleep(kWaitSeconds);
204		++current_iterations;
205	}
206
207	if (*record) {
208#ifdef OPENDIRECTORY_CACHE
209		const void *keys[] = { CFRECORDNAME_NAME, CFRECORDNAME_RECORD };
210		const void *values[] = { cfUser, *record };
211		CFDictionaryRef dict;
212
213		dict = CFDictionaryCreate(NULL, keys, values, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
214		if (dict)
215			pam_set_data(pamh, CFRECORDNAME_CACHE, (void *)dict, cleanup_cache);
216#endif /* OPENDIRECTORY_CACHE */
217		retval = PAM_SUCCESS;
218	} else {
219		retval = PAM_USER_UNKNOWN;
220	}
221
222	if (current_iterations > 0) {
223		char *wt = NULL, *found = NULL;
224		int retval2;
225
226		if (*record)
227			found = "failure";
228		else
229			found = "success";
230
231		retval2 = asprintf(&wt, "%d", kWaitSeconds * current_iterations);
232		if (-1 == retval2) {
233			openpam_log(PAM_LOG_DEBUG, "Failed to convert current wait time to string.");
234			retval = PAM_BUF_ERR;
235			goto cleanup;
236		}
237
238
239		aslmsg m = asl_new(ASL_TYPE_MSG);
240		asl_set(m, "com.apple.message.domain", "com.apple.pam_modules.odAvailableWaitTime" );
241		asl_set(m, "com.apple.message.signature", "wait_time");
242		asl_set(m, "com.apple.message.value", wt);
243		asl_set(m, "com.apple.message.result", found);
244		asl_log(NULL, m, ASL_LEVEL_NOTICE, "OD nodes online delay: %ss. User record lookup: %s.", wt, found);
245		asl_free(m);
246		free(wt);
247	}
248
249cleanup:
250	if (NULL != attrs) {
251		CFRelease(attrs);
252	}
253
254	if (NULL != cferror) {
255		CFRelease(cferror);
256	}
257
258	if (NULL != cfNode) {
259		CFRelease(cfNode);
260	}
261
262	if (PAM_SUCCESS != retval) {
263		openpam_log(PAM_LOG_ERROR, "failed: %d", retval);
264		if (NULL != *record) {
265			CFRelease(*record);
266			*record = NULL;
267		}
268	}
269
270	return retval;
271}
272
273int
274od_record_create_cstring(pam_handle_t *pamh, ODRecordRef *record, const char *user)
275{
276	int retval = PAM_SUCCESS;
277	CFStringRef cfUser = NULL;
278
279	if (NULL == record || NULL == user) {
280		openpam_log(PAM_LOG_DEBUG, "NULL argument passed");
281		retval = PAM_SERVICE_ERR;
282		goto cleanup;
283	}
284
285	if (PAM_SUCCESS != (retval = cstring_to_cfstring(user, &cfUser)) ||
286	    PAM_SUCCESS != (retval = od_record_create(pamh, record, cfUser))) {
287		openpam_log(PAM_LOG_DEBUG, "od_record_create() failed");
288		goto cleanup;
289	}
290
291cleanup:
292	if (PAM_SUCCESS != retval) {
293		openpam_log(PAM_LOG_ERROR, "failed: %d", retval);
294		if (NULL != *record) {
295			CFRelease(*record);
296		}
297	}
298
299	if (NULL != cfUser) {
300		CFRelease(cfUser);
301	}
302
303	return retval;
304}
305
306/* Can return NULL */
307int
308od_record_attribute_create_cfarray(ODRecordRef record, CFStringRef attrib,  CFArrayRef *out)
309{
310	int retval = PAM_SUCCESS;
311
312	if (NULL == record || NULL == attrib || NULL == out) {
313		openpam_log(PAM_LOG_DEBUG, "NULL argument passed");
314		retval = PAM_SERVICE_ERR;
315		goto cleanup;
316	}
317
318	*out = ODRecordCopyValues(record, attrib, NULL);
319
320cleanup:
321	if (PAM_SUCCESS != retval) {
322		if (NULL != out) {
323			CFRelease(out);
324		}
325	}
326	return retval;
327}
328
329/* Can return NULL */
330int
331od_record_attribute_create_cfstring(ODRecordRef record, CFStringRef attrib,  CFStringRef *out)
332{
333	int retval = PAM_SERVICE_ERR;
334	CFTypeRef cval = NULL;
335	CFArrayRef vals = NULL;
336	CFIndex i = 0, count = 0;
337
338	if (NULL == record || NULL == attrib || NULL == out) {
339		openpam_log(PAM_LOG_DEBUG, "NULL argument passed");
340		retval = PAM_SERVICE_ERR;
341		goto cleanup;
342	}
343
344	*out = NULL;
345	retval = od_record_attribute_create_cfarray(record, attrib, &vals);
346	if (PAM_SUCCESS != retval) {
347		openpam_log(PAM_LOG_DEBUG, "od_record_attribute_create_cfarray() failed");
348		goto cleanup;
349	}
350	if (NULL == vals) {
351		retval = PAM_SUCCESS;
352		goto cleanup;
353	}
354
355	count = CFArrayGetCount(vals);
356	if (1 != count) {
357		char *attr_cstr = NULL;
358		cfstring_to_cstring(attrib, &attr_cstr);
359		openpam_log(PAM_LOG_DEBUG, "returned %lx attributes for %s", count, attr_cstr);
360		free(attr_cstr);
361	}
362
363	for (i = 0; i < count; ++i) {
364		cval = CFArrayGetValueAtIndex(vals, i);
365		if (NULL == cval) {
366			continue;
367		}
368		if (CFGetTypeID(cval) == CFStringGetTypeID()) {
369			*out = CFStringCreateCopy(kCFAllocatorDefault, cval);
370			if (NULL == *out) {
371				openpam_log(PAM_LOG_DEBUG, "CFStringCreateCopy() failed");
372				retval = PAM_BUF_ERR;
373				goto cleanup;
374			}
375			break;
376		} else {
377			openpam_log(PAM_LOG_DEBUG, "attribute is not a cfstring");
378			retval = PAM_PERM_DENIED;
379			goto cleanup;
380		}
381	}
382	retval = PAM_SUCCESS;
383
384cleanup:
385	if (PAM_SUCCESS != retval) {
386		if (NULL != out) {
387			CFRelease(out);
388		}
389	}
390	if (NULL != vals) {
391		CFRelease(vals);
392	}
393
394	return retval;
395}
396
397/* Can return NULL */
398int
399od_record_attribute_create_cstring(ODRecordRef record, CFStringRef attrib,  char **out)
400{
401	int retval = PAM_SERVICE_ERR;
402	CFStringRef val = NULL;
403
404	if (NULL == record || NULL == attrib || NULL == out) {
405		openpam_log(PAM_LOG_DEBUG, "NULL argument passed");
406		retval = PAM_SERVICE_ERR;
407		goto cleanup;
408	}
409
410	retval = od_record_attribute_create_cfstring(record, attrib, &val);
411	if (PAM_SUCCESS != retval) {
412		openpam_log(PAM_LOG_DEBUG, "od_record_attribute_create_cfstring() failed");
413		goto cleanup;
414	}
415
416	if (NULL != val) {
417		retval = cfstring_to_cstring(val, out);
418		if (PAM_SUCCESS != retval) {
419			openpam_log(PAM_LOG_DEBUG, "cfstring_to_cstring() failed");
420			goto cleanup;
421		}
422	}
423
424cleanup:
425	if (PAM_SUCCESS != retval) {
426		free(out);
427	}
428
429	if (NULL != val) {
430		CFRelease(val);
431	}
432
433	return retval;
434}
435
436int
437od_record_check_pwpolicy(ODRecordRef record)
438{
439    CFErrorRef oderror = NULL;
440	int retval = PAM_SERVICE_ERR;
441
442	if (NULL == record) {
443		openpam_log(PAM_LOG_DEBUG, "NULL argument passed");
444		retval = PAM_SERVICE_ERR;
445		goto cleanup;
446	}
447
448    if (!ODRecordAuthenticationAllowed(record, &oderror)) {
449        switch (CFErrorGetCode(oderror)) {
450			case kODErrorCredentialsAccountNotFound:
451				retval = PAM_USER_UNKNOWN;
452				break;
453			case kODErrorCredentialsAccountDisabled:
454			case kODErrorCredentialsAccountInactive:
455				retval = PAM_PERM_DENIED;
456				break;
457			case kODErrorCredentialsPasswordExpired:
458			case kODErrorCredentialsPasswordChangeRequired:
459				retval = PAM_NEW_AUTHTOK_REQD;
460				break;
461			case kODErrorCredentialsInvalid:
462				retval = PAM_AUTH_ERR;
463				break;
464			default:
465				retval = PAM_AUTH_ERR;
466				break;
467		}
468
469    } else {
470        retval = PAM_SUCCESS;
471    }
472
473cleanup:
474	openpam_log(PAM_LOG_DEBUG, "retval: %d", retval);
475	return retval;
476}
477
478int
479od_record_check_authauthority(ODRecordRef record)
480{
481	int retval = PAM_PERM_DENIED;
482	CFStringRef authauth = NULL;
483
484	if (NULL == record) {
485		openpam_log(PAM_LOG_DEBUG, "NULL argument passed");
486		retval = PAM_SERVICE_ERR;
487		goto cleanup;
488	}
489
490	retval = od_record_attribute_create_cfstring(record, kODAttributeTypeAuthenticationAuthority, &authauth);
491	if (PAM_SUCCESS != retval) {
492		openpam_log(PAM_LOG_DEBUG, "od_record_attribute_create_cfstring() failed");
493		goto cleanup;
494	}
495	if (NULL == authauth) {
496		retval = PAM_SUCCESS;
497		goto cleanup;
498	}
499	if (!CFStringHasPrefix(authauth, CFSTR(kDSValueAuthAuthorityDisabledUser))) {
500		retval = PAM_SUCCESS;
501	}
502
503cleanup:
504	if (PAM_SUCCESS != retval) {
505		openpam_log(PAM_LOG_ERROR, "failed: %d", retval);
506	}
507
508	if (authauth) {
509		CFRelease(authauth);
510	}
511
512	return retval;
513}
514
515int
516od_record_check_homedir(ODRecordRef record)
517{
518	int retval = PAM_SERVICE_ERR;
519	CFStringRef tmp = NULL;
520
521	if (NULL == record) {
522		openpam_log(PAM_LOG_DEBUG, "NULL argument passed");
523		retval = PAM_SERVICE_ERR;
524		goto cleanup;
525	}
526
527	retval = od_record_attribute_create_cfstring(record, kODAttributeTypeNFSHomeDirectory, &tmp);
528	if (PAM_SUCCESS != retval) {
529		openpam_log(PAM_LOG_DEBUG, "od_record_attribute_create_cfstring() failed");
530		goto cleanup;
531	}
532
533	/* Allow NULL home directories */
534	if (NULL == tmp) {
535		retval = PAM_SUCCESS;
536		goto cleanup;
537	}
538
539	/* Do not allow login with '/dev/null' home */
540	if (kCFCompareEqualTo == CFStringCompare(tmp, CFSTR("/dev/null"), 0)) {
541		openpam_log(PAM_LOG_DEBUG, "home directory is /dev/null");
542		retval = PAM_PERM_DENIED;
543		goto cleanup;
544	}
545
546	if (kCFCompareEqualTo == CFStringCompare(tmp, CFSTR("99"), 0)) {
547		openpam_log(PAM_LOG_DEBUG, "home directory is 99");
548		retval = PAM_PERM_DENIED;
549		goto cleanup;
550	}
551
552	retval = PAM_SUCCESS;
553
554cleanup:
555	if (PAM_SUCCESS != retval)
556		openpam_log(PAM_LOG_ERROR, "failed: %d", retval);
557
558	if (NULL != tmp) {
559		CFRelease(tmp);
560	}
561
562	return retval;
563}
564
565int
566od_record_check_shell(ODRecordRef record)
567{
568	int retval = PAM_PERM_DENIED;
569	CFStringRef cfstr = NULL;
570
571	if (NULL == record) {
572		openpam_log(PAM_LOG_DEBUG, "NULL argument passed");
573		retval = PAM_SERVICE_ERR;
574		goto cleanup;
575	}
576
577	retval = od_record_attribute_create_cfstring(record, kODAttributeTypeUserShell, &cfstr);
578	if (PAM_SUCCESS != retval) {
579		openpam_log(PAM_LOG_DEBUG, "od_record_attribute_create_cfstring() failed");
580		goto cleanup;
581	}
582
583	if (NULL == cfstr) {
584		retval = PAM_SUCCESS;
585		goto cleanup;
586	}
587
588	if (CFStringCompare(cfstr, CFSTR("/usr/bin/false"), 0) == kCFCompareEqualTo) {
589		openpam_log(PAM_LOG_DEBUG, "user shell is /bin/false");
590		retval = PAM_PERM_DENIED;
591	}
592
593cleanup:
594	if (PAM_SUCCESS != retval)
595		openpam_log(PAM_LOG_ERROR, "failed: %d", retval);
596
597	if (NULL != cfstr) {
598		CFRelease(cfstr);
599	}
600
601	return retval;
602}
603
604int
605od_string_from_record(ODRecordRef record, CFStringRef attrib,  char **out)
606{
607	int retval = PAM_SERVICE_ERR;
608	CFStringRef val = NULL;
609
610	if (NULL == record) {
611		openpam_log(PAM_LOG_DEBUG, "%s - NULL ODRecord passed.", __func__);
612		goto cleanup;
613	}
614
615	retval = od_record_attribute_create_cfstring(record, attrib, &val);
616	if (PAM_SUCCESS != retval) {
617		goto cleanup;
618	}
619
620	if (val)
621		retval = cfstring_to_cstring(val, out);
622
623cleanup:
624	if (val)
625		CFRelease(val);
626
627	return retval;
628}
629
630int
631extract_homemount(char *in, char **out_url, char **out_path)
632{
633	// Directory Services people have assured me that this won't change
634	static const char URL_OPEN[] = "<url>";
635	static const char URL_CLOSE[] = "</url>";
636	static const char PATH_OPEN[] = "<path>";
637	static const char PATH_CLOSE[] = "</path>";
638
639	char *server_URL = NULL;
640	char *path = NULL;
641	char *record_start = NULL;
642	char *record_end = NULL;
643
644	int retval = PAM_SERVICE_ERR;
645
646	if (NULL == in)
647		goto fin;
648
649	record_start = in;
650	server_URL = strstr(record_start, URL_OPEN);
651	if (NULL == server_URL)
652		goto fin;
653	server_URL += sizeof(URL_OPEN)-1;
654	while ('\0' != *server_URL && isspace(*server_URL))
655		server_URL++;
656	record_end = strstr(server_URL, URL_CLOSE);
657	if (NULL == record_end)
658		goto fin;
659	while (record_end >= server_URL && '\0' != *record_end && isspace(*(record_end-1)))
660		record_end--;
661	if (NULL == record_end)
662		goto fin;
663	*record_end = '\0';
664	if (NULL == (*out_url = strdup(server_URL)))
665		goto fin;
666
667	record_start = record_end+1;
668	path = strstr(record_start, PATH_OPEN);
669	if (NULL == path)
670		goto ok;
671	path += sizeof(PATH_OPEN)-1;
672	while ('\0' != *path && isspace(*path))
673		path++;
674	record_end = strstr(path, PATH_CLOSE);
675	if (NULL == record_end)
676		goto fin;
677	while (record_end >= path && '\0' != *record_end && isspace(*(record_end-1)))
678		record_end--;
679	if (NULL == record_end)
680		goto fin;
681	*record_end = '\0';
682	if (NULL == (*out_path = strdup(path)))
683		goto fin;
684
685ok:
686	retval = PAM_SUCCESS;
687fin:
688	return retval;
689}
690
691int
692od_extract_home(pam_handle_t *pamh, const char *username, char **server_URL, char **path, char **homedir)
693{
694	int retval = PAM_SERVICE_ERR;
695	char *tmp = NULL;
696	ODRecordRef record = NULL;
697
698	retval = od_record_create_cstring(pamh, &record, username);
699	if (PAM_SUCCESS != retval) {
700		goto cleanup;
701	}
702
703	retval = od_string_from_record(record, kODAttributeTypeHomeDirectory, &tmp);
704	if (retval) {
705		openpam_log(PAM_LOG_DEBUG, "%s - get kODAttributeTypeHomeDirectory  : %d",
706			    __func__, retval);
707		goto cleanup;
708	}
709	extract_homemount(tmp, server_URL, path);
710	openpam_log(PAM_LOG_DEBUG, "%s - Server URL   : %s", __func__, *server_URL);
711	openpam_log(PAM_LOG_DEBUG, "%s - Path to mount: %s", __func__, *path);
712
713	retval = od_string_from_record(record, kODAttributeTypeNFSHomeDirectory, homedir);
714	openpam_log(PAM_LOG_DEBUG, "%s - Home dir     : %s", __func__, *homedir);
715	if (retval)
716		goto cleanup;
717
718	retval = PAM_SUCCESS;
719
720cleanup:
721	if (tmp)
722		free(tmp);
723	if (record)
724		CFRelease(record);
725
726	return retval;
727}
728
729/* extract the principal from OpenDirectory */
730int
731od_principal_for_user(pam_handle_t *pamh, const char *user, char **od_principal)
732{
733	int retval = PAM_SERVICE_ERR;
734	ODRecordRef record = NULL;
735	CFStringRef principal = NULL;
736	CFArrayRef authparts = NULL, vals = NULL;
737	CFIndex i = 0, count = 0;
738
739	if (NULL == user || NULL == od_principal) {
740		openpam_log(PAM_LOG_DEBUG, "NULL argument passed");
741		retval = PAM_SERVICE_ERR;
742		goto cleanup;
743	}
744
745	retval = od_record_create_cstring(pamh, &record, user);
746	if (PAM_SUCCESS != retval) {
747		openpam_log(PAM_LOG_DEBUG, "od_record_attribute_create_cfstring() failed");
748		goto cleanup;
749	}
750
751	retval = od_record_attribute_create_cfarray(record, kODAttributeTypeAuthenticationAuthority, &vals);
752	if (PAM_SUCCESS != retval) {
753		openpam_log(PAM_LOG_DEBUG, "od_record_attribute_create_cfarray() failed");
754		goto cleanup;
755	}
756	if (NULL == vals) {
757		openpam_log(PAM_LOG_DEBUG, "no authauth availale for user.");
758		retval = PAM_PERM_DENIED;
759		goto cleanup;
760	}
761
762	count = CFArrayGetCount(vals);
763	for (i = 0; i < count; i++)
764	{
765		const void *val = CFArrayGetValueAtIndex(vals, i);
766		if (NULL == val || CFGetTypeID(val) != CFStringGetTypeID())
767			break;
768
769		authparts = CFStringCreateArrayBySeparatingStrings(kCFAllocatorDefault, val, CFSTR(";"));
770		if (NULL == authparts)
771			continue;
772
773		if ((CFArrayGetCount(authparts) < 5) ||
774		    (CFStringCompare(CFArrayGetValueAtIndex(authparts, 1), CFSTR("Kerberosv5"), kCFCompareEqualTo)) ||
775		    (CFStringHasPrefix(CFArrayGetValueAtIndex(authparts, 4), CFSTR("LKDC:")))) {
776			if (NULL != authparts) {
777				CFRelease(authparts);
778				authparts = NULL;
779			}
780			continue;
781		} else {
782			break;
783		}
784	}
785
786	if (NULL == authparts) {
787		openpam_log(PAM_LOG_DEBUG, "No authentication authority returned");
788		retval = PAM_PERM_DENIED;
789		goto cleanup;
790	}
791
792	principal = CFArrayGetValueAtIndex(authparts, 3);
793	if (NULL == principal) {
794		openpam_log(PAM_LOG_DEBUG, "no principal found in authentication authority");
795		retval = PAM_PERM_DENIED;
796		goto cleanup;
797	}
798
799	retval = cfstring_to_cstring(principal, od_principal);
800	if (PAM_SUCCESS != retval) {
801		openpam_log(PAM_LOG_DEBUG, "cfstring_to_cstring() failed");
802		goto cleanup;
803	}
804
805
806cleanup:
807	if (PAM_SUCCESS != retval) {
808		openpam_log(PAM_LOG_DEBUG, "failed: %d", retval);
809	}
810
811	if (NULL != record) {
812		CFRelease(record);
813	}
814
815	if (NULL != authparts) {
816		CFRelease(authparts);
817	}
818
819	if (NULL != vals) {
820		CFRelease(vals);
821	}
822
823	return retval;
824}
825
826void
827pam_cf_cleanup(__unused pam_handle_t *pamh, void *data, __unused int pam_end_status)
828{
829	if (data) {
830		CFStringRef *cfstring = data;
831		CFRelease(*cfstring);
832	}
833}
834