1/*
2 * Copyright (c) 2000-2004 Apple Computer, 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 "webdavd.h"
25
26#include <sys/types.h>
27#include <pthread.h>
28#include "webdav_authcache.h"
29#include "webdav_network.h"
30
31/*****************************************************************************/
32
33/* define authcache_head structure */
34LIST_HEAD(authcache_head, authcache_entry);
35
36struct authcache_entry
37{
38	LIST_ENTRY(authcache_entry) entries;
39	uid_t uid;
40	CFHTTPAuthenticationRef auth;
41	CFStringRef username;
42	CFStringRef password;
43	CFStringRef domain;			/* can be NULL if there is no account domain */
44	u_int32_t authflags;		/* The options for this authorization */
45};
46
47/* authFlags */
48enum
49{
50	/* No flags */
51	kAuthNone					= 0x00000000,
52
53	kCredentialsFromMount		= 0x00000001,	/* Credentials passed at mount time */
54
55	/* Set if mount credentials should not be used (they were tried and didn't work) */
56	kNoMountCredentials			= 0x00000002,
57
58	/* Set once the credentials are successfully used for a transaction */
59	kCredentialsValid			= 0x00000004
60};
61
62/*****************************************************************************/
63
64static pthread_mutex_t authcache_lock;					/* lock for authcache */
65static u_int32_t authcache_generation = 1;				/* generation count of authcache (never zero)*/
66static struct authcache_head authcache_list;			/* the list of authcache_entry structs for the server */
67static struct authcache_entry *authcache_proxy_entry = NULL;	/* the authcache_entry for the proxy server, or NULL */
68static CFStringRef mount_username = NULL;
69static CFStringRef mount_password = NULL;
70static CFStringRef mount_proxy_username = NULL;
71static CFStringRef mount_proxy_password = NULL;
72static CFStringRef mount_domain = NULL;
73
74/*****************************************************************************/
75
76// static
77char *CopyCFStringToCString(CFStringRef theString);
78
79static
80void ReleaseCredentials(struct authcache_entry *entry_ptr);
81
82/*****************************************************************************/
83
84static
85void RemoveAuthentication(struct authcache_entry *entry_ptr)
86{
87	/* authcache_proxy_entry is never in the authcache_list */
88	if ( entry_ptr != authcache_proxy_entry )
89	{
90		LIST_REMOVE(entry_ptr, entries);
91	}
92	else
93	{
94		authcache_proxy_entry = NULL;
95	}
96	++authcache_generation;
97	if ( authcache_generation == 0 )
98	{
99		++authcache_generation;
100	}
101
102	if ( entry_ptr->auth != NULL )
103	{
104		CFRelease(entry_ptr->auth);
105		entry_ptr->auth = NULL;
106	}
107
108	ReleaseCredentials(entry_ptr);
109
110	free(entry_ptr);
111}
112
113/*****************************************************************************/
114
115static
116void ReleaseCredentials(
117	struct authcache_entry *entry_ptr)
118{
119	if (entry_ptr->username != NULL)
120	{
121		CFRelease(entry_ptr->username);
122		entry_ptr->username = NULL;
123	}
124	if (entry_ptr->password != NULL)
125	{
126		CFRelease(entry_ptr->password);
127		entry_ptr->password = NULL;
128	}
129	if (entry_ptr->domain != NULL)
130	{
131		CFRelease(entry_ptr->domain);
132		entry_ptr->domain = NULL;
133	}
134}
135
136/*****************************************************************************/
137
138static
139void SetCredentials(
140	struct authcache_entry *entry_ptr,
141	CFStringRef new_username,
142	CFStringRef new_password,
143	CFStringRef new_domain)
144{
145	entry_ptr->username = new_username;
146	entry_ptr->password = new_password;
147	entry_ptr->domain = new_domain;
148}
149
150/*****************************************************************************/
151
152// static
153char *CopyCFStringToCString(CFStringRef theString)
154{
155	char *cstring;
156	CFIndex usedBufLen;
157	CFIndex converted;
158	CFRange range;
159
160	range = CFRangeMake(0, CFStringGetLength(theString));
161	converted = CFStringGetBytes(theString, range, kCFStringEncodingUTF8, 0, false, NULL, 0, &usedBufLen);
162	cstring = malloc(usedBufLen + 1);
163	if ( cstring != NULL )
164	{
165		converted = CFStringGetBytes(theString, range, kCFStringEncodingUTF8, 0, false, (UInt8 *)cstring, usedBufLen, &usedBufLen);
166		cstring[usedBufLen] = '\0';
167	}
168	return ( cstring );
169}
170
171/*****************************************************************************/
172
173static
174int CopyMountCredentials(
175	CFHTTPAuthenticationRef auth,
176	CFStringRef *username,
177	CFStringRef *password,
178	CFStringRef *domain,
179	int *secureAuth)				/* <- TRUE if auth is sent securely */
180{
181	int result;
182    CFStringRef method;
183
184	/* determine if this authentication is secure */
185	if ( gSecureConnection )
186	{
187		/* the connection is secure so the authentication is secure */
188		*secureAuth = TRUE;
189	}
190	else
191	{
192		/* the connection is not secure, so secure means "not Basic authentication" */
193		method = CFHTTPAuthenticationCopyMethod(auth);
194		if ( method != NULL )
195		{
196			*secureAuth = !CFEqual(method, CFSTR("Basic"));
197			CFRelease(method);
198		}
199		else
200		{
201			*secureAuth = FALSE;
202		}
203	}
204
205	/* make sure we aren't using Basic over an unsecure connnection if gSecureServerAuth is TRUE */
206	if ( (gSecureServerAuth == TRUE) && (*secureAuth == FALSE) ) {
207		syslog(LOG_ERR, "Mount failed, Authentication method (Basic) too weak");
208		result = EAUTH;
209		goto SecureServerAuthRequired;
210	}
211
212	if ( mount_username != NULL )
213	{
214		CFRetain(mount_username);
215		*username = mount_username;
216
217		if ( mount_password != NULL )
218		{
219			CFRetain(mount_password);
220		}
221		*password = mount_password;
222
223		if ( mount_domain != NULL )
224		{
225			CFRetain(mount_domain);
226		}
227		*domain = mount_domain;
228
229		result = 0;
230	}
231	else
232	{
233		result = 1;
234	}
235
236SecureServerAuthRequired:
237
238	return ( result );
239}
240
241/*****************************************************************************/
242
243static
244int CopyMountProxyCredentials(CFHTTPAuthenticationRef auth,
245							  CFStringRef *username,
246							  CFStringRef *password,
247							  CFStringRef *domain,
248							  int *secureAuth)		/* <- TRUE if auth is sent securely */
249{
250#pragma unused(domain)
251	int result = 1;
252    CFStringRef method;
253
254	if (auth == NULL) {
255		syslog(LOG_ERR, "auth object arg is NULL");
256		return result;
257	}
258	if (username == NULL) {
259		syslog(LOG_ERR, "user arg is NULL");
260		return result;
261	}
262	if (password == NULL) {
263		syslog(LOG_ERR, "credential arg is NULL");
264		return result;
265	}
266	if (domain == NULL) {
267		syslog(LOG_ERR, "domain arg is NULL");
268		return result;
269	}
270	if (secureAuth == NULL) {
271		syslog(LOG_ERR, "secureAuth object is NULL");
272		return result;
273	}
274
275	/* determine if this authentication is secure */
276	if ( gSecureConnection )
277	{
278		/* the connection is secure so the authentication is secure */
279		*secureAuth = TRUE;
280	}
281	else
282	{
283		/* the connection is not secure, so secure means "not Basic authentication" */
284		method = CFHTTPAuthenticationCopyMethod(auth);
285		if ( method != NULL )
286		{
287			*secureAuth = !CFEqual(method, CFSTR("Basic"));
288			CFRelease(method);
289		}
290		else
291		{
292			*secureAuth = FALSE;
293		}
294	}
295
296	if ( mount_proxy_username != NULL )
297	{
298		CFRetain(mount_proxy_username);
299		*username = mount_proxy_username;
300
301		if ( mount_proxy_password != NULL )
302		{
303			CFRetain(mount_proxy_password);
304		}
305		*password = mount_proxy_password;
306		result = 0;
307	}
308
309	return ( result );
310}
311
312/*****************************************************************************/
313
314static
315int AddServerCredentials(
316	struct authcache_entry *entry_ptr,
317	CFHTTPMessageRef request)
318{
319	int result;
320	/* locals for getting new values */
321	CFStringRef username;
322	CFStringRef password;
323	CFStringRef domain;
324	int secureAuth;
325
326	username = password = domain = NULL;
327	result = EACCES;
328
329	if ( CFHTTPAuthenticationRequiresUserNameAndPassword(entry_ptr->auth) )
330	{
331		/* invalidate credential sources already tried */
332		if (entry_ptr->authflags & kCredentialsFromMount)
333		{
334			entry_ptr->authflags |= kNoMountCredentials;
335		}
336
337		/* if we haven't tried the mount credentials, try them now */
338		if ( (entry_ptr->authflags & kNoMountCredentials) == 0 )
339		{
340			result = CopyMountCredentials(entry_ptr->auth, &username, &password, &domain, &secureAuth);
341			if ( result == 0 )
342			{
343				ReleaseCredentials(entry_ptr);
344				SetCredentials(entry_ptr, username, password, domain);
345				entry_ptr->authflags |= kCredentialsFromMount;
346			}
347			else
348			{
349				/* there are no mount credentials */
350				syslog(LOG_DEBUG, "AddServerCred: no mount creds, req %p", request);
351				entry_ptr->authflags |= kNoMountCredentials;
352			}
353		}
354
355		if ( (result == 0) && !(secureAuth) )
356		{
357			CFURLRef url;
358			CFStringRef urlString;
359			char *urlStr;
360
361			urlStr = NULL;
362
363			url = CFHTTPMessageCopyRequestURL(request);
364			if ( url != NULL )
365			{
366				urlString = CFURLGetString(url);
367				if ( urlString != NULL )
368				{
369					urlStr = CopyCFStringToCString(urlString);
370				}
371				else
372				{
373					urlStr = NULL;
374				}
375				CFRelease(url);
376			}
377
378			syslog(LOG_ERR | LOG_AUTHPRIV, "WebDAV FS authentication credentials are being sent insecurely to: %s", (urlStr ? urlStr : ""));
379
380			if ( urlStr != NULL )
381			{
382				free(urlStr);
383			}
384		}
385	}
386	else
387	{
388		result = 0;
389	}
390	return ( result );
391}
392
393/*****************************************************************************/
394
395static
396int AddProxyCredentials(struct authcache_entry *entry_ptr, CFHTTPMessageRef request)
397{
398	int result;
399	/* locals for getting new values */
400	CFStringRef username;
401	CFStringRef password;
402	CFStringRef domain;
403	int secureAuth;
404
405	if ( CFHTTPAuthenticationRequiresUserNameAndPassword(entry_ptr->auth) )
406	{
407		username = password = domain = NULL;
408		result = EACCES;
409
410		/* invalidate credential sources already tried */
411		if (entry_ptr->authflags & kCredentialsFromMount)
412		{
413			entry_ptr->authflags |= kNoMountCredentials;
414		}
415
416		/* if we haven't tried the mount credentials, try them now */
417		if ( (entry_ptr->authflags & kNoMountCredentials) == 0 )
418		{
419			if ( CopyMountProxyCredentials(entry_ptr->auth, &username, &password, &domain, &secureAuth) == 0 )
420			{
421				ReleaseCredentials(entry_ptr);
422				SetCredentials(entry_ptr, username, password, domain);
423				entry_ptr->authflags |= kCredentialsFromMount;
424				result = 0;
425			}
426			else
427			{
428				/* there are no mount credentials */
429				syslog(LOG_DEBUG, "AddProxyCredentials: no mount creds, req %p", request);
430				entry_ptr->authflags |= kNoMountCredentials;
431			}
432		}
433
434		if ( (result == 0) && !(secureAuth) )
435		{
436			CFURLRef url;
437			CFStringRef urlString;
438			char *urlStr;
439
440			urlStr = NULL;
441
442			url = CFHTTPMessageCopyRequestURL(request);
443			if ( url != NULL )
444			{
445				urlString = CFURLGetString(url);
446				if ( urlString != NULL )
447				{
448					urlStr = CopyCFStringToCString(urlString);
449				}
450				else
451				{
452					urlStr = NULL;
453				}
454				CFRelease(url);
455			}
456
457			syslog(LOG_ERR | LOG_AUTHPRIV, "WebDAV FS authentication proxy credentials are being sent insecurely to: %s", (urlStr ? urlStr : ""));
458
459			if ( urlStr != NULL )
460			{
461				free(urlStr);
462			}
463		}
464	}
465	else {
466		result = 0;
467	}
468
469	return ( result );
470}
471
472/*****************************************************************************/
473
474static
475struct authcache_entry *CreateProxyAuthenticationFromResponse(
476	uid_t uid,							/* -> uid of the user making the request */
477	CFHTTPMessageRef request,			/* -> the request message to apply authentication to */
478	CFHTTPMessageRef response)			/* -> the response message  */
479{
480	struct authcache_entry *entry_ptr;
481	int result;
482
483	entry_ptr = calloc(sizeof(struct authcache_entry), 1);
484	require(entry_ptr != NULL, calloc);
485
486	entry_ptr->uid = uid;
487	entry_ptr->auth = CFHTTPAuthenticationCreateFromResponse(kCFAllocatorDefault, response);
488	require(entry_ptr->auth != NULL, CFHTTPAuthenticationCreateFromResponse);
489
490	require(CFHTTPAuthenticationIsValid(entry_ptr->auth, NULL), CFHTTPAuthenticationIsValid);
491
492	result = AddProxyCredentials(entry_ptr, request);
493	require_noerr_quiet(result, AddProxyCredentials);
494
495	authcache_proxy_entry = entry_ptr;
496	++authcache_generation;
497	if ( authcache_generation == 0 )
498	{
499		++authcache_generation;
500	}
501
502	return ( entry_ptr );
503
504AddProxyCredentials:
505CFHTTPAuthenticationIsValid:
506
507	CFRelease(entry_ptr->auth);
508
509CFHTTPAuthenticationCreateFromResponse:
510
511	free(entry_ptr);
512
513calloc:
514
515	return ( NULL );
516}
517
518/*****************************************************************************/
519
520static
521struct authcache_entry *CreateAuthenticationFromResponse(
522	uid_t uid,							/* -> uid of the user making the request */
523	CFHTTPMessageRef request,			/* -> the request message to apply authentication to */
524	CFHTTPMessageRef response,			/* -> the response message  */
525	int *result,						/* -> result of this function (errno) */
526	int isProxy)						/* -> if TRUE, create authcache_proxy_entry */
527{
528	struct authcache_entry *entry_ptr;
529	*result = 0;
530
531	entry_ptr = calloc(1, sizeof(struct authcache_entry));
532	require_action(entry_ptr != NULL, calloc_err, *result = ENOMEM);
533
534	entry_ptr->uid = uid;
535	entry_ptr->auth = CFHTTPAuthenticationCreateFromResponse(kCFAllocatorDefault, response);
536	require_action(entry_ptr->auth != NULL, CFHTTPAuthenticationCreateFromResponse, *result = EIO);
537
538	require_action(CFHTTPAuthenticationIsValid(entry_ptr->auth, NULL), CFHTTPAuthenticationIsValid, *result = EIO);
539
540	if ( !isProxy )
541	{
542		*result = AddServerCredentials(entry_ptr, request);
543		require_noerr_quiet(*result, AddServerCredentials);
544
545		LIST_INSERT_HEAD(&authcache_list, entry_ptr, entries);
546	}
547	else
548	{
549		*result = AddProxyCredentials(entry_ptr, request);
550		require_noerr_quiet(*result, AddProxyCredentials);
551	}
552
553	++authcache_generation;
554	if ( authcache_generation == 0 )
555	{
556		++authcache_generation;
557	}
558
559	return ( entry_ptr );
560
561AddProxyCredentials:
562AddServerCredentials:
563CFHTTPAuthenticationIsValid:
564
565	CFRelease(entry_ptr->auth);
566
567CFHTTPAuthenticationCreateFromResponse:
568
569	free(entry_ptr);
570
571calloc_err:
572
573	return ( NULL );
574}
575
576/*****************************************************************************/
577
578static
579struct authcache_entry *FindAuthenticationForRequest(
580	uid_t uid,							/* -> uid of the user making the request */
581	CFHTTPMessageRef request)			/* -> the request message to apply authentication to */
582{
583	struct authcache_entry *entry_ptr;
584
585	/* see if we have an authentication we can use */
586	LIST_FOREACH(entry_ptr, &authcache_list, entries)
587	{
588		/*
589		 * If this authentication is for the current user or root user,
590		 * and it applies to this request, then break.
591		 */
592		if ( (entry_ptr->uid == uid) || (0 == uid) )
593		{
594			if ( CFHTTPAuthenticationAppliesToRequest(entry_ptr->auth, request) )
595			{
596				break;
597			}
598		}
599	}
600	return ( entry_ptr );
601}
602
603/*****************************************************************************/
604
605static int ApplyCredentialsToRequest(struct authcache_entry *entry_ptr, CFHTTPMessageRef request)
606{
607	int result;
608
609	if ( entry_ptr->domain != NULL ) {
610		CFMutableDictionaryRef dict;
611
612		dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 3, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
613		require_action(dict != NULL, CFDictionaryCreateMutable, result = FALSE);
614
615		if ( entry_ptr->username != NULL ) {
616			CFDictionaryAddValue(dict, kCFHTTPAuthenticationUsername, entry_ptr->username);
617		}
618
619		if ( entry_ptr->password != NULL ) {
620			CFDictionaryAddValue(dict, kCFHTTPAuthenticationPassword, entry_ptr->password);
621		}
622
623		if ( entry_ptr->domain != NULL ) {
624			CFDictionaryAddValue(dict, kCFHTTPAuthenticationAccountDomain, entry_ptr->domain);
625		}
626
627		result = CFHTTPMessageApplyCredentialDictionary(request, entry_ptr->auth, dict, NULL);
628
629		CFRelease(dict);
630
631	}
632	else {
633		result = CFHTTPMessageApplyCredentials(request, entry_ptr->auth, entry_ptr->username, entry_ptr->password,  NULL);
634	}
635
636CFDictionaryCreateMutable:
637
638	return ( result );
639}
640
641/*****************************************************************************/
642
643static
644int AddExistingAuthentications(
645		uid_t uid,				/* -> uid of the user making the request */
646	CFHTTPMessageRef request)	/* -> the request message to apply authentication to */
647{
648	struct authcache_entry *entry_ptr;
649
650	entry_ptr = FindAuthenticationForRequest(uid, request);
651	if ( entry_ptr != NULL )
652	{
653		/* try to apply valid entry to the request */
654		if ( CFHTTPAuthenticationIsValid(entry_ptr->auth, NULL) )
655		{
656			if ( !ApplyCredentialsToRequest(entry_ptr, request) )
657			{
658				/*
659				 * Remove the unusable entry and do nothing -- we'll get a 401 when this request goes to the server
660				 * which should allow us to create a new entry.
661				 */
662				RemoveAuthentication(entry_ptr);
663			}
664		}
665		else
666		{
667			/*
668			 * Remove the unusable entry and do nothing -- we'll get a 401 when this request goes to the server
669			 * which should allow us to create a new entry.
670			 */
671			RemoveAuthentication(entry_ptr);
672		}
673	}
674
675	if ( authcache_proxy_entry != NULL )
676	{
677		/* try to apply valid entry to the request */
678		if ( !CFHTTPAuthenticationIsValid(authcache_proxy_entry->auth, NULL) || !ApplyCredentialsToRequest(authcache_proxy_entry, request) )
679		{
680			/*
681			 * Remove the unusable entry and do nothing -- we'll get a 407 when this request goes to the server
682			 * which should allow us to create a new entry.
683			 */
684			RemoveAuthentication(authcache_proxy_entry);
685		}
686	}
687
688	return ( 0 );
689}
690
691/*****************************************************************************/
692
693/*
694 * DoServerAuthentication
695 *
696 * Handles authentication challenge (401) from the server
697 * Adds a new authcache_entry, or update an exiting authcache_entry for a server.
698 */
699 static
700int DoServerAuthentication(
701	uid_t uid,							/* -> uid of the user making the request */
702	CFHTTPMessageRef request,			/* -> the request message to apply authentication to */
703	CFHTTPMessageRef response)			/* -> the response containing the challenge, or NULL if no challenge */
704{
705	struct authcache_entry *entry_ptr;
706	int result = 0;
707
708	/* see if we already have an authcache_entry */
709	entry_ptr = FindAuthenticationForRequest(uid, request);
710
711	/* if we have one, we need to try to update it and use it */
712	if ( entry_ptr != NULL )
713	{
714		// Clear valid flag since we just got a 401 for this auth_entry
715		entry_ptr->authflags &= ~kCredentialsValid;
716
717		/* ensure the CFHTTPAuthenticationRef is valid */
718		if ( CFHTTPAuthenticationIsValid(entry_ptr->auth, NULL) )
719		{
720			result = 0;
721		}
722		else
723		{
724			/* It's invalid so release the old one and try to create a new one */
725			CFRelease(entry_ptr->auth);
726			entry_ptr->auth = CFHTTPAuthenticationCreateFromResponse(kCFAllocatorDefault, response);
727			if ( entry_ptr->auth != NULL )
728			{
729				if ( CFHTTPAuthenticationIsValid(entry_ptr->auth, NULL) )
730				{
731					result = AddServerCredentials(entry_ptr, request);
732				}
733				else
734				{
735					result = EACCES;
736				}
737			}
738			else
739			{
740				result = EACCES;
741			}
742		}
743		if ( result != 0 )
744		{
745			RemoveAuthentication(entry_ptr);
746		}
747	}
748	else
749	{
750		/* create a new authcache_entry */
751		entry_ptr = CreateAuthenticationFromResponse(uid, request, response, &result, FALSE);
752	}
753
754	return ( result );
755}
756
757/*****************************************************************************/
758
759/*
760 * AddProxyAuthentication
761 *
762 * Add a new authcache_entry, or update an exiting authcache_entry for a proxy.
763 */
764static
765int AddProxyAuthentication(
766	uid_t uid,							/* -> uid of the user making the request */
767	CFHTTPMessageRef request,			/* -> the request message to apply authentication to */
768	CFHTTPMessageRef response)			/* -> the response containing the challenge, or NULL if no challenge */
769{
770	int result;
771
772	/* if we have an entry for the proxy, we need to try to update it and use it */
773	if ( authcache_proxy_entry != NULL )
774	{
775		authcache_proxy_entry->authflags &= ~kCredentialsValid;
776
777		/* ensure the CFHTTPAuthenticationRef is valid */
778		if ( CFHTTPAuthenticationIsValid(authcache_proxy_entry->auth, NULL) )
779		{
780			result = 0;
781		}
782		else
783		{
784			/* It's invalid so release the old and try to create a new one */
785			CFRelease(authcache_proxy_entry->auth);
786			authcache_proxy_entry->auth = CFHTTPAuthenticationCreateFromResponse(kCFAllocatorDefault, response);
787			if ( authcache_proxy_entry->auth != NULL )
788			{
789				if ( CFHTTPAuthenticationIsValid(authcache_proxy_entry->auth, NULL) )
790				{
791					result = AddProxyCredentials(authcache_proxy_entry, request);
792				}
793				else
794				{
795					result = EACCES;
796				}
797			}
798			else
799			{
800				result = EACCES;
801			}
802		}
803		if ( result != 0 )
804		{
805			RemoveAuthentication(authcache_proxy_entry);
806		}
807	}
808	else
809	{
810		/* create a new authcache_entry for the proxy */
811		authcache_proxy_entry = CreateProxyAuthenticationFromResponse(uid, request, response);
812		if ( authcache_proxy_entry != NULL )
813		{
814			result = 0;
815		}
816		else
817		{
818			result = EACCES;
819		}
820	}
821
822	return ( result );
823}
824
825/*****************************************************************************/
826
827int authcache_apply(
828	uid_t uid,							/* -> uid of the user making the request */
829	CFHTTPMessageRef request,			/* -> the request message to apply authentication to */
830	UInt32 statusCode,					/* -> the status code (401, 407), or 0 if no challenge */
831	CFHTTPMessageRef response,			/* -> the response containing the challenge, or NULL if no challenge */
832	UInt32 *generation)					/* <- the generation count of the cache entry */
833{
834	int result, result2;
835
836	/* lock the Authcache */
837	result = pthread_mutex_lock(&authcache_lock);
838	require_noerr_action(result, pthread_mutex_lock, webdav_kill(-1));
839
840	switch (statusCode)
841	{
842	case 0:
843		/* no challenge */
844		result = 0;
845		break;
846
847	case 401:
848		/* server challenge -- add server authentication */
849
850		/* only add server authentication if the uid is the mount's user or root user */
851		if ( (gProcessUID == uid) || (0 == uid) )
852		{
853			result = DoServerAuthentication(uid, request, response);
854		}
855		else
856		{
857			result = EACCES;
858		}
859		break;
860
861	case 407:
862		/* proxy challenge -- add proxy authentication */
863
864		/* only add proxy authentication if the uid is the mount's user or root user */
865		if ( (gProcessUID == uid) || (0 == uid) )
866		{
867			result = AddProxyAuthentication(uid, request, response);
868		}
869		else
870		{
871			result = EACCES;
872		}
873		break;
874
875	default:
876		/* should never happen */
877		result = EACCES;
878		break;
879	}
880
881	/* only apply existing authentications if the uid is the mount's user or root user */
882	if ( (result == 0) && ((gProcessUID == uid) || (0 == uid)) )
883	{
884		result = AddExistingAuthentications(uid, request);
885	}
886
887	/* return the current authcache_generation */
888	*generation = authcache_generation;
889
890	/* unlock the Authcache */
891	result2 = pthread_mutex_unlock(&authcache_lock);
892	require_noerr_action(result2, pthread_mutex_unlock, result = result2; webdav_kill(-1));
893
894pthread_mutex_unlock:
895pthread_mutex_lock:
896
897	return ( result );
898}
899
900/*****************************************************************************/
901
902int authcache_valid(
903	uid_t uid,							/* -> uid of the user making the request */
904	CFHTTPMessageRef request,			/* -> the message of the successful request */
905	UInt32 generation)					/* -> the generation count of the cache entry */
906{
907	int result, result2;
908
909	/* only validate authentications if the uid is the mount's user or root user */
910	require_quiet(((gProcessUID == uid) || (0 == uid)), not_owner_uid);
911
912	/* lock the Authcache */
913	result = pthread_mutex_lock(&authcache_lock);
914	require_noerr_action(result, pthread_mutex_lock, webdav_kill(-1));
915
916	if ( generation == authcache_generation )
917	{
918		struct authcache_entry *entry_ptr;
919
920		/* see if we have an authcache_entry */
921		entry_ptr = FindAuthenticationForRequest(uid, request);
922		if ( entry_ptr != NULL )
923		{
924			/* mark this authentication valid */
925			entry_ptr->authflags |= kCredentialsValid;
926		}
927
928		if ( authcache_proxy_entry != NULL )
929		{
930			/* mark this authentication valid */
931			authcache_proxy_entry->authflags |= kCredentialsValid;
932		}
933	}
934
935	/* unlock the Authcache */
936	result2 = pthread_mutex_unlock(&authcache_lock);
937	require_noerr_action(result2, pthread_mutex_unlock, result = result2; webdav_kill(-1));
938
939pthread_mutex_unlock:
940pthread_mutex_lock:
941not_owner_uid:
942
943	return ( 0 );
944}
945
946/*****************************************************************************/
947
948int authcache_proxy_invalidate(void)
949{
950	int result, result2;
951
952	/* lock the Authcache */
953	result = pthread_mutex_lock(&authcache_lock);
954	require_noerr_action(result, pthread_mutex_lock, webdav_kill(-1));
955
956	/* called when proxy settings change -- remove any proxy authentications */
957	if ( authcache_proxy_entry != NULL )
958	{
959		RemoveAuthentication(authcache_proxy_entry);
960	}
961
962	/* unlock the Authcache */
963	result2 = pthread_mutex_unlock(&authcache_lock);
964	require_noerr_action(result2, pthread_mutex_unlock, result = result2; webdav_kill(-1));
965
966pthread_mutex_unlock:
967pthread_mutex_lock:
968
969	return ( result );
970}
971
972/*****************************************************************************/
973
974int authcache_init(
975	char *username,				/* -> username to attempt to use on first server challenge, or NULL */
976	char *password,				/* -> password to attempt to use on first server challenge, or NULL */
977	char *proxy_username,		/* -> username to attempt to use on first proxy server challenge, or NULL */
978	char *proxy_password,		/* -> password to attempt to use on first proxy server challenge, or NULL */
979	char *domain)				/* -> account domain to attempt to use on first server challenge, or NULL */
980{
981	int result;
982	pthread_mutexattr_t mutexattr;
983
984	/* set up the lock on the list */
985	result = pthread_mutexattr_init(&mutexattr);
986	require_noerr(result, pthread_mutexattr_init);
987
988	result = pthread_mutex_init(&authcache_lock, &mutexattr);
989	require_noerr(result, pthread_mutex_init);
990
991	LIST_INIT(&authcache_list);
992	authcache_generation = 1;
993
994	result = 0;
995
996	if ( username != NULL && password != NULL && username[0] != '\0')
997	{
998		mount_username = CFStringCreateWithCString(kCFAllocatorDefault, username, kCFStringEncodingUTF8);
999		require_action(mount_username != NULL, CFStringCreateWithCString, result = ENOMEM);
1000
1001		mount_password = CFStringCreateWithCString(kCFAllocatorDefault, password, kCFStringEncodingUTF8);
1002		require_action(mount_password != NULL, CFStringCreateWithCString, result = ENOMEM);
1003	}
1004
1005	if ( proxy_username != NULL && proxy_password != NULL && proxy_username[0] != '\0')
1006	{
1007		mount_proxy_username = CFStringCreateWithCString(kCFAllocatorDefault, proxy_username, kCFStringEncodingUTF8);
1008		require_action(mount_proxy_username != NULL, CFStringCreateWithCString, result = ENOMEM);
1009
1010		mount_proxy_password = CFStringCreateWithCString(kCFAllocatorDefault, proxy_password, kCFStringEncodingUTF8);
1011		require_action(mount_proxy_password != NULL, CFStringCreateWithCString, result = ENOMEM);
1012	}
1013
1014	if ( domain != NULL && domain[0] != '\0' )
1015	{
1016		mount_domain = CFStringCreateWithCString(kCFAllocatorDefault, domain, kCFStringEncodingUTF8);
1017		require_action(mount_domain != NULL, CFStringCreateWithCString, result = ENOMEM);
1018	}
1019
1020CFStringCreateWithCString:
1021pthread_mutex_init:
1022pthread_mutexattr_init:
1023
1024	return ( result );
1025}
1026
1027/*****************************************************************************/
1028