1 /*
2  * $LynxId: HTAABrow.c,v 1.39 2013/07/21 00:14:39 tom Exp $
3  *
4  * MODULE							HTAABrow.c
5  *		BROWSER SIDE ACCESS AUTHORIZATION MODULE
6  *
7  *	Contains the code for keeping track on server hostnames,
8  *	port numbers, scheme names, usernames, passwords
9  *	(and servers' public keys).
10  *
11  * IMPORTANT:
12  *	Routines in this module use dynamic allocation, but free
13  *	automatically all the memory reserved by them.
14  *
15  *	Therefore the caller never has to (and never should)
16  *	free() any object returned by these functions.
17  *
18  *	Therefore also all the strings returned by this package
19  *	are only valid until the next call to the same function
20  *	is made.  This approach is selected, because of the nature
21  *	of access authorization: no string returned by the package
22  *	needs to be valid longer than until the next call.
23  *
24  *	This also makes it easy to plug the AA package in:
25  *	you don't have to ponder whether to free() something
26  *	here or is it done somewhere else (because it is always
27  *	done somewhere else).
28  *
29  *	The strings that the package needs to store are copied
30  *	so the original strings given as parameters to AA
31  *	functions may be freed or modified with no side effects.
32  *
33  *	The AA package does not free() anything else than what
34  *	it has itself allocated.
35  *
36  * AUTHORS:
37  *	AL	Ari Luotonen	luotonen@dxcern.cern.ch
38  *
39  * HISTORY:
40  *	Oct 17	AL	Made corrections suggested by marca:
41  *			Added  if (!realm->username) return NULL;
42  *			Changed some ""s to NULLs.
43  *			Now doing calloc() to init uuencode source;
44  *			otherwise HTUU_encode() reads uninitialized memory
45  *			every now and then (not a real bug but not pretty).
46  *			Corrected the formula for uuencode destination size.
47  *
48  * 28 Apr 1997	AJL	Do Proxy Authorisation.
49  *
50  * BUGS:
51  *
52  *
53  */
54 
55 #include <HTUtils.h>
56 #include <HTString.h>
57 #include <HTParse.h>		/* URL parsing function         */
58 #include <HTList.h>		/* HTList object                */
59 #include <HTAlert.h>		/* HTConfirm(), HTPrompt()      */
60 #include <HTAAUtil.h>		/* AA common to both sides      */
61 #include <HTAssoc.h>		/* Assoc list                   */
62 #include <HTAccess.h>		/* Are we using an HTTP gateway? */
63 #include <HTAABrow.h>		/* Implemented here             */
64 #include <HTUU.h>		/* Uuencoding and uudecoding    */
65 
66 #include <LYLeaks.h>
67 
68 /*
69  *  Local datatype definitions
70  *
71  *  HTAAServer contains all the information about one server.
72  */
73 typedef struct {
74 
75     char *hostname;		/* Host's name                  */
76     int portnumber;		/* Port number                  */
77     BOOL IsProxy;		/* Is it a proxy?               */
78     HTList *setups;		/* List of protection setups
79 				   on this server; i.e., valid
80 				   authentication schemes and
81 				   templates when to use them.
82 				   This is actually a list of
83 				   HTAASetup objects.           */
84     HTList *realms;		/* Information about passwords  */
85 } HTAAServer;
86 
87 /*
88  *  HTAASetup contains information about one server's one
89  *  protected tree of documents.
90  */
91 typedef struct {
92     HTAAServer *server;		/* Which server serves this tree             */
93     char *ctemplate;		/* Template for this tree                    */
94     HTList *valid_schemes;	/* Valid authentic.schemes                   */
95     HTAssocList **scheme_specifics;	/* Scheme specific params                  */
96     BOOL retry;			/* Failed last time -- reprompt (or whatever) */
97 } HTAASetup;
98 
99 /*
100  *  Information about usernames and passwords in
101  *  Basic and Pubkey authentication schemes;
102  */
103 typedef struct {
104     char *realmname;		/* Password domain name         */
105     char *username;		/* Username in that domain      */
106     char *password;		/* Corresponding password       */
107 } HTAARealm;
108 
109 /*
110  *  To free off all globals. - FM
111  */
112 static void free_HTAAGlobals(void);
113 static BOOL free_HTAAGlobalsSet = FALSE;
114 static char *HTAA_composeAuthResult = NULL;
115 static char *compose_auth_stringResult = NULL;	/* Uuencoded presentation */
116 
117 /*
118  *  Module-wide global variables
119  */
120 static HTList *server_table = NULL;	/* Browser's info about servers      */
121 static char *secret_key = NULL;	/* Browser's latest secret key       */
122 static HTAASetup *current_setup = NULL;		/* The server setup we are currently */
123 
124 					/* talking to                        */
125 static char *current_hostname = NULL;	/* The server's name and portnumber  */
126 static int current_portnumber = 80;	/* where we are currently trying to  */
127 
128 					/* connect.                          */
129 static char *current_docname = NULL;	/* The document's name we are        */
130 
131 					/* trying to access.                 */
132 static char *HTAAForwardAuth = NULL;	/* Authorization: line to forward    */
133 
134 					/* (used by gateway httpds)          */
135 static HTAASetup *proxy_setup = NULL;	/* Same as above, but for Proxy -AJL */
136 static char *proxy_hostname = NULL;
137 static char *proxy_docname = NULL;
138 static int proxy_portnumber = 80;
139 
140 /*** HTAAForwardAuth for enabling gateway-httpds to forward Authorization ***/
141 
HTAAForwardAuth_set(const char * scheme_name,const char * scheme_specifics)142 void HTAAForwardAuth_set(const char *scheme_name,
143 			 const char *scheme_specifics)
144 {
145     size_t len = (20
146 		  + (scheme_name ? strlen(scheme_name) : 0)
147 		  + (scheme_specifics ? strlen(scheme_specifics) : 0));
148 
149     FREE(HTAAForwardAuth);
150     if ((HTAAForwardAuth = typecallocn(char, len)) == 0)
151 	  outofmem(__FILE__, "HTAAForwardAuth_set");
152 
153     assert(HTAAForwardAuth != 0);
154 
155     strcpy(HTAAForwardAuth, "Authorization: ");
156     if (scheme_name) {
157 	strcat(HTAAForwardAuth, scheme_name);
158 	strcat(HTAAForwardAuth, " ");
159 	if (scheme_specifics) {
160 	    strcat(HTAAForwardAuth, scheme_specifics);
161 	}
162     }
163 }
164 
HTAAForwardAuth_reset(void)165 void HTAAForwardAuth_reset(void)
166 {
167     FREE(HTAAForwardAuth);
168 }
169 
170 /**************************** HTAAServer ***********************************/
171 
172 static void HTAASetup_delete(HTAASetup * killme);	/* Forward */
173 
174 /* static						HTAAServer_new()
175  *		ALLOCATE A NEW NODE TO HOLD SERVER INFO
176  *		AND ADD IT TO THE LIST OF SERVERS
177  * ON ENTRY:
178  *	hostname	is the name of the host that the server
179  *			is running in.
180  *	portnumber	is the portnumber which the server listens.
181  *	IsProxy		should be TRUE if this is a proxy.
182  *
183  * ON EXIT:
184  *	returns		the newly-allocated node with all the strings
185  *			duplicated.
186  *			Strings will be automatically freed by
187  *			the function HTAAServer_delete(), which also
188  *			frees the node itself.
189  */
HTAAServer_new(const char * hostname,int portnumber,int IsProxy)190 static HTAAServer *HTAAServer_new(const char *hostname,
191 				  int portnumber,
192 				  int IsProxy)
193 {
194     HTAAServer *server;
195 
196     if ((server = typecalloc(HTAAServer)) == 0)
197 	  outofmem(__FILE__, "HTAAServer_new");
198 
199     assert(server != NULL);
200 
201     server->hostname = NULL;
202     server->portnumber = (portnumber > 0 ? portnumber : 80);
203     server->IsProxy = (BOOLEAN) IsProxy;
204     server->setups = HTList_new();
205     server->realms = HTList_new();
206 
207     if (hostname)
208 	StrAllocCopy(server->hostname, hostname);
209 
210     if (!server_table)
211 	server_table = HTList_new();
212 
213     HTList_addObject(server_table, (void *) server);
214 
215     return server;
216 }
217 
218 /* static						HTAAServer_delete()
219  *
220  *	DELETE THE ENTRY FOR THE SERVER FROM THE HOST TABLE,
221  *	AND FREE THE MEMORY USED BY IT.
222  *
223  * ON ENTRY:
224  *	killme		points to the HTAAServer to be freed.
225  *
226  * ON EXIT:
227  *	returns		nothing.
228  */
HTAAServer_delete(HTAAServer * killme)229 static void HTAAServer_delete(HTAAServer *killme)
230 {
231     int n, i;
232     HTAASetup *setup;
233     HTAARealm *realm;
234     HTList *cur;
235 
236     if (killme) {
237 	if (killme->setups != NULL) {
238 	    n = HTList_count(killme->setups);
239 	    for (i = (n - 1); i >= 0; i--) {
240 		if ((setup = (HTAASetup *) HTList_objectAt(killme->setups,
241 							   i)) != NULL) {
242 		    HTAASetup_delete(setup);
243 		    setup = NULL;
244 		}
245 	    }
246 	    HTList_delete(killme->setups);
247 	    killme->setups = NULL;
248 	}
249 
250 	cur = killme->realms;
251 	while (NULL != (realm = (HTAARealm *) HTList_nextObject(cur))) {
252 	    FREE(realm->realmname);
253 	    FREE(realm->username);
254 	    FREE(realm->password);
255 	    FREE(realm);
256 	}
257 	HTList_delete(killme->realms);
258 	killme->realms = NULL;
259 
260 	FREE(killme->hostname);
261 
262 	HTList_removeObject(server_table, (void *) killme);
263 	FREE(killme);
264     }
265 }
266 
267 /* static						HTAAServer_lookup()
268  *		LOOK UP SERVER BY HOSTNAME AND PORTNUMBER
269  * ON ENTRY:
270  *	hostname	obvious.
271  *	portnumber	if non-positive defaults to 80.
272  *	IsProxy		should be TRUE if this is a proxy.
273  *
274  *	Looks up the server in the module-global server_table.
275  *
276  * ON EXIT:
277  *	returns		pointer to a HTAAServer structure
278  *			representing the looked-up server.
279  *			NULL, if not found.
280  */
HTAAServer_lookup(const char * hostname,int portnumber,int IsProxy)281 static HTAAServer *HTAAServer_lookup(const char *hostname,
282 				     int portnumber,
283 				     int IsProxy)
284 {
285     if (hostname) {
286 	HTList *cur = server_table;
287 	HTAAServer *server;
288 
289 	if (portnumber <= 0)
290 	    portnumber = 80;
291 
292 	while (NULL != (server = (HTAAServer *) HTList_nextObject(cur))) {
293 	    if (server->portnumber == portnumber &&
294 		0 == strcmp(server->hostname, hostname) &&
295 		server->IsProxy == IsProxy)
296 		return server;
297 	}
298     }
299     return NULL;		/* NULL parameter, or not found */
300 }
301 
302 /*************************** HTAASetup *******************************/
303 
304 /* static						HTAASetup_lookup()
305  *	FIGURE OUT WHICH AUTHENTICATION SETUP THE SERVER
306  *	IS USING FOR A GIVEN FILE ON A GIVEN HOST AND PORT
307  *
308  * ON ENTRY:
309  *	hostname	is the name of the server host machine.
310  *	portnumber	is the port that the server is running in.
311  *	docname		is the (URL-)pathname of the document we
312  *			are trying to access.
313  *	IsProxy		should be TRUE if this is a proxy.
314  *
315  *	This function goes through the information known about
316  *	all the setups of the server, and finds out if the given
317  *	filename resides in one of the protected directories.
318  *
319  * ON EXIT:
320  *	returns		NULL if no match.
321  *			Otherwise, a HTAASetup structure representing
322  *			the protected server setup on the corresponding
323  *			document tree.
324  *
325  */
HTAASetup_lookup(const char * hostname,int portnumber,const char * docname,int IsProxy)326 static HTAASetup *HTAASetup_lookup(const char *hostname,
327 				   int portnumber,
328 				   const char *docname,
329 				   int IsProxy)
330 {
331     HTAAServer *server;
332     HTAASetup *setup;
333 
334     if (portnumber <= 0)
335 	portnumber = 80;
336 
337     if (hostname && docname && *hostname && *docname &&
338 	NULL != (server = HTAAServer_lookup(hostname,
339 					    portnumber,
340 					    IsProxy))) {
341 
342 	HTList *cur = server->setups;
343 
344 	CTRACE((tfp, "%s %s (%s:%d:%s)\n",
345 		"HTAASetup_lookup: resolving setup for",
346 		(IsProxy ? "proxy" : "server"),
347 		hostname, portnumber, docname));
348 
349 	while (NULL != (setup = (HTAASetup *) HTList_nextObject(cur))) {
350 	    if (HTAA_templateMatch(setup->ctemplate, docname)) {
351 		CTRACE((tfp, "%s `%s' %s `%s'\n",
352 			"HTAASetup_lookup:", docname,
353 			"matched template", setup->ctemplate));
354 		return setup;
355 	    } else {
356 		CTRACE((tfp, "%s `%s' %s `%s'\n",
357 			"HTAASetup_lookup:", docname,
358 			"did NOT match template", setup->ctemplate));
359 	    }
360 	}			/* while setups remain */
361     }
362     /* if valid parameters and server found */
363     CTRACE((tfp, "%s `%s' %s\n",
364 	    "HTAASetup_lookup: No template matched",
365 	    NONNULL(docname),
366 	    "(so probably not protected)"));
367 
368     return NULL;		/* NULL in parameters, or not found */
369 }
370 
371 /* static						HTAASetup_new()
372  *			CREATE A NEW SETUP NODE
373  * ON ENTRY:
374  *	server		is a pointer to a HTAAServer structure
375  *			to which this setup belongs.
376  *	ctemplate	documents matching this template
377  *			are protected according to this setup.
378  *	valid_schemes	a list containing all valid authentication
379  *			schemes for this setup.
380  *			If NULL, all schemes are disallowed.
381  *	scheme_specifics is an array of assoc lists, which
382  *			contain scheme specific parameters given
383  *			by server in Authenticate: fields.
384  *			If NULL, all scheme specifics are
385  *			set to NULL.
386  * ON EXIT:
387  *	returns		a new HTAASetup node, and also adds it as
388  *			part of the HTAAServer given as parameter.
389  */
HTAASetup_new(HTAAServer * server,char * ctemplate,HTList * valid_schemes,HTAssocList ** scheme_specifics)390 static HTAASetup *HTAASetup_new(HTAAServer *server, char *ctemplate,
391 				HTList *valid_schemes,
392 				HTAssocList **scheme_specifics)
393 {
394     HTAASetup *setup;
395 
396     if (!server || isEmpty(ctemplate))
397 	return NULL;
398 
399     if ((setup = typecalloc(HTAASetup)) == 0)
400 	outofmem(__FILE__, "HTAASetup_new");
401 
402     assert(setup != NULL);
403 
404     setup->retry = NO;
405     setup->server = server;
406     setup->ctemplate = NULL;
407     if (ctemplate)
408 	StrAllocCopy(setup->ctemplate, ctemplate);
409     setup->valid_schemes = valid_schemes;
410     setup->scheme_specifics = scheme_specifics;
411 
412     HTList_addObject(server->setups, (void *) setup);
413 
414     return setup;
415 }
416 
417 /* static						HTAASetup_delete()
418  *			FREE A HTAASetup STRUCTURE
419  * ON ENTRY:
420  *	killme		is a pointer to the structure to free().
421  *
422  * ON EXIT:
423  *	returns		nothing.
424  */
HTAASetup_delete(HTAASetup * killme)425 static void HTAASetup_delete(HTAASetup * killme)
426 {
427     int scheme;
428 
429     if (killme) {
430 	FREE(killme->ctemplate);
431 	if (killme->valid_schemes) {
432 	    HTList_delete(killme->valid_schemes);
433 	    killme->valid_schemes = NULL;
434 	}
435 	for (scheme = 0; scheme < HTAA_MAX_SCHEMES; scheme++)
436 	    if (killme->scheme_specifics[scheme])
437 		HTAssocList_delete(killme->scheme_specifics[scheme]);
438 	FREE(killme->scheme_specifics);
439 	FREE(killme);
440     }
441 }
442 
443 /* static					HTAASetup_updateSpecifics()
444  *		COPY SCHEME SPECIFIC PARAMETERS
445  *		TO HTAASetup STRUCTURE
446  * ON ENTRY:
447  *	setup		destination setup structure.
448  *	specifics	string array containing scheme
449  *			specific parameters for each scheme.
450  *			If NULL, all the scheme specific
451  *			parameters are set to NULL.
452  *
453  * ON EXIT:
454  *	returns		nothing.
455  */
HTAASetup_updateSpecifics(HTAASetup * setup,HTAssocList ** specifics)456 static void HTAASetup_updateSpecifics(HTAASetup * setup, HTAssocList **specifics)
457 {
458     int scheme;
459 
460     if (setup) {
461 	if (setup->scheme_specifics) {
462 	    for (scheme = 0; scheme < HTAA_MAX_SCHEMES; scheme++) {
463 		if (setup->scheme_specifics[scheme])
464 		    HTAssocList_delete(setup->scheme_specifics[scheme]);
465 	    }
466 	    FREE(setup->scheme_specifics);
467 	}
468 	setup->scheme_specifics = specifics;
469     }
470 }
471 
472 /*************************** HTAARealm **********************************/
473 
474 /* static						HTAARealm_lookup()
475  *		LOOKUP HTAARealm STRUCTURE BY REALM NAME
476  * ON ENTRY:
477  *	realm_table	a list of realm objects.
478  *	realmname	is the name of realm to look for.
479  *
480  * ON EXIT:
481  *	returns		the realm.  NULL, if not found.
482  */
HTAARealm_lookup(HTList * realm_table,const char * realmname)483 static HTAARealm *HTAARealm_lookup(HTList *realm_table,
484 				   const char *realmname)
485 {
486     if (realm_table && realmname) {
487 	HTList *cur = realm_table;
488 	HTAARealm *realm;
489 
490 	while (NULL != (realm = (HTAARealm *) HTList_nextObject(cur))) {
491 	    if (0 == strcmp(realm->realmname, realmname))
492 		return realm;
493 	}
494     }
495     return NULL;		/* No table, NULL param, or not found */
496 }
497 
498 /* static						HTAARealm_new()
499  *		CREATE A NODE CONTAINING USERNAME AND
500  *		PASSWORD USED FOR THE GIVEN REALM.
501  *		IF REALM ALREADY EXISTS, CHANGE
502  *		USERNAME/PASSWORD.
503  * ON ENTRY:
504  *	realm_table	a list of realms to where to add
505  *			the new one, too.
506  *	realmname	is the name of the password domain.
507  *	username	and
508  *	password	are what you can expect them to be.
509  *
510  * ON EXIT:
511  *	returns		the created realm.
512  */
HTAARealm_new(HTList * realm_table,const char * realmname,const char * username,const char * password)513 static HTAARealm *HTAARealm_new(HTList *realm_table,
514 				const char *realmname,
515 				const char *username,
516 				const char *password)
517 {
518     HTAARealm *realm;
519 
520     realm = HTAARealm_lookup(realm_table, realmname);
521 
522     if (!realm) {
523 	if ((realm = typecalloc(HTAARealm)) == 0)
524 	      outofmem(__FILE__, "HTAARealm_new");
525 
526 	assert(realm != NULL);
527 
528 	realm->realmname = NULL;
529 	realm->username = NULL;
530 	realm->password = NULL;
531 	StrAllocCopy(realm->realmname, realmname);
532 	if (realm_table)
533 	    HTList_addObject(realm_table, (void *) realm);
534     }
535     if (username)
536 	StrAllocCopy(realm->username, username);
537     if (password)
538 	StrAllocCopy(realm->password, password);
539 
540     return realm;
541 }
542 
543 /***************** Basic and Pubkey Authentication ************************/
544 
545 /* static						compose_auth_string()
546  *
547  *		COMPOSE Basic OR Pubkey AUTHENTICATION STRING;
548  *		PROMPTS FOR USERNAME AND PASSWORD IF NEEDED
549  *
550  * ON ENTRY:
551  *	scheme		is either HTAA_BASIC or HTAA_PUBKEY.
552  *	setup		is the current server setup.
553  *	IsProxy		should be TRUE if this is a proxy.
554  *
555  * ON EXIT:
556  *	returns		a newly composed authorization string,
557  *			(with, of course, a newly generated secret
558  *			key and fresh timestamp, if Pubkey-scheme
559  *			is being used).
560  *			NULL, if something fails.
561  * NOTE:
562  *	Like throughout the entire AA package, no string or structure
563  *	returned by AA package needs to (or should) be freed.
564  *
565  */
compose_auth_string(HTAAScheme scheme,HTAASetup * setup,int IsProxy)566 static char *compose_auth_string(HTAAScheme scheme, HTAASetup * setup, int IsProxy)
567 {
568     char *cleartext = NULL;	/* Cleartext presentation */
569     char *ciphertext = NULL;	/* Encrypted presentation */
570     size_t len;
571     char *msg = NULL;
572     char *username = NULL;
573     char *password = NULL;
574     char *realmname = NULL;
575     char *theHost = NULL;
576     char *proxiedHost = NULL;
577     char *thePort = NULL;
578     HTAARealm *realm;
579     const char *i_net_addr = "0.0.0.0";		/* Change... @@@@ */
580     const char *timestamp = "42";	/* ... these @@@@ */
581 
582     FREE(compose_auth_stringResult);	/* From previous call */
583 
584     if ((scheme != HTAA_BASIC && scheme != HTAA_PUBKEY) || !setup ||
585 	!setup->scheme_specifics || !setup->scheme_specifics[scheme] ||
586 	!setup->server || !setup->server->realms)
587 	return NULL;
588 
589     realmname = HTAssocList_lookup(setup->scheme_specifics[scheme], "realm");
590     if (!realmname)
591 	return NULL;
592 
593     realm = HTAARealm_lookup(setup->server->realms, realmname);
594     if (!(realm &&
595 	  realm->username && *realm->username &&
596 	  realm->password) || setup->retry) {
597 	if (!realm) {
598 	    CTRACE((tfp, "%s `%s' %s\n",
599 		    "compose_auth_string: realm:", realmname,
600 		    "not found -- creating"));
601 	    realm = HTAARealm_new(setup->server->realms,
602 				  realmname, NULL, NULL);
603 	}
604 	/*
605 	 * The template should be either the '*' global for everything on the
606 	 * server (always true for proxy authorization setups), or a path for
607 	 * the start of a protected limb, with no host field, but we'll check
608 	 * for a host anyway in case a WWW-Protection-Template header set an
609 	 * absolute URL instead of a path.  If we do get a host from this, it
610 	 * will include the port.  - FM
611 	 */
612 	if ((!IsProxy) && using_proxy && setup->ctemplate) {
613 	    proxiedHost = HTParse(setup->ctemplate, "", PARSE_HOST);
614 	    if (proxiedHost && *proxiedHost != '\0') {
615 		theHost = proxiedHost;
616 	    }
617 	}
618 	/*
619 	 * If we didn't get a host field from the template, set up the host
620 	 * name and port from the setup->server elements.  - FM
621 	 */
622 	if (!theHost)
623 	    theHost = setup->server->hostname;
624 	if (setup->server->portnumber > 0 &&
625 	    setup->server->portnumber != 80) {
626 	    HTSprintf0(&thePort, ":%d", setup->server->portnumber);
627 	}
628 	/*
629 	 * Set up the message for the username prompt, and then issue the
630 	 * prompt.  The default username is included in the call to the
631 	 * prompting function, but the password is NULL-ed and always replaced.
632 	 * - FM
633 	 */
634 	HTSprintf0(&msg, gettext("Username for '%s' at %s '%s%s':"),
635 		   realm->realmname,
636 		   (IsProxy ? "proxy" : "server"),
637 		   (theHost ? theHost : "??"),
638 		   NonNull(thePort));
639 	FREE(proxiedHost);
640 	FREE(thePort);
641 	StrAllocCopy(username, realm->username);
642 	password = NULL;
643 	HTPromptUsernameAndPassword(msg, &username, &password, IsProxy);
644 
645 	FREE(msg);
646 	FREE(realm->username);
647 	FREE(realm->password);
648 	realm->username = username;
649 	realm->password = password;
650 
651 	if (!realm->username || !realm->password) {
652 	    /*
653 	     * Signals to retry.  - FM
654 	     */
655 	    return NULL;
656 	} else if (*realm->username == '\0') {
657 	    /*
658 	     * Signals to abort.  - FM
659 	     */
660 	    StrAllocCopy(compose_auth_stringResult, "");
661 	    return compose_auth_stringResult;
662 	}
663     }
664 
665     len = (strlen(NonNull(realm->username)) +
666 	   strlen(NonNull(realm->password)) + 3);
667 
668     if (scheme == HTAA_PUBKEY) {
669 #ifdef PUBKEY
670 	/* Generate new secret key */
671 	StrAllocCopy(secret_key, HTAA_generateRandomKey());
672 #endif /* PUBKEY */
673 	/* Room for secret key, timestamp and inet address */
674 	len += strlen(NonNull(secret_key)) + 30;
675     } else {
676 	FREE(secret_key);
677     }
678 
679     if ((cleartext = typecallocn(char, len)) == 0)
680 	  outofmem(__FILE__, "compose_auth_string");
681 
682     assert(cleartext != NULL);
683 
684     if (realm->username)
685 	strcpy(cleartext, realm->username);
686     else
687 	*cleartext = '\0';
688 
689     strcat(cleartext, ":");
690 
691     if (realm->password)
692 	strcat(cleartext, realm->password);
693 
694     if (scheme == HTAA_PUBKEY) {
695 	strcat(cleartext, ":");
696 	strcat(cleartext, i_net_addr);
697 	strcat(cleartext, ":");
698 	strcat(cleartext, timestamp);
699 	strcat(cleartext, ":");
700 	if (secret_key)
701 	    strcat(cleartext, secret_key);
702 
703 	if (!((ciphertext = typecallocn(char, 2 * len)) &&
704 	        (compose_auth_stringResult = typecallocn(char, 3 * len))))
705 	      outofmem(__FILE__, "compose_auth_string");
706 
707 #ifdef PUBKEY
708 	HTPK_encrypt(cleartext, ciphertext, server->public_key);
709 	HTUU_encode((unsigned char *) ciphertext, strlen(ciphertext),
710 		    compose_auth_stringResult);
711 #endif /* PUBKEY */
712 	FREE(cleartext);
713 	FREE(ciphertext);
714     } else {			/* scheme == HTAA_BASIC */
715 	if (!(compose_auth_stringResult =
716 	      typecallocn(char, (4 * ((len + 2) / 3)) + 1)))
717 	      outofmem(__FILE__, "compose_auth_string");
718 
719 	HTUU_encode((unsigned char *) cleartext, strlen(cleartext),
720 		    compose_auth_stringResult);
721 	FREE(cleartext);
722     }
723     return compose_auth_stringResult;
724 }
725 
726 /* BROWSER static					HTAA_selectScheme()
727  *		SELECT THE AUTHENTICATION SCHEME TO USE
728  * ON ENTRY:
729  *	setup	is the server setup structure which can
730  *		be used to make the decision about the
731  *		used scheme.
732  *
733  *	When new authentication methods are added to library
734  *	this function makes the decision about which one to
735  *	use at a given time.  This can be done by inspecting
736  *	environment variables etc.
737  *
738  *	Currently only searches for the first valid scheme,
739  *	and if nothing found suggests Basic scheme;
740  *
741  * ON EXIT:
742  *	returns	the authentication scheme to use.
743  */
HTAA_selectScheme(HTAASetup * setup)744 static HTAAScheme HTAA_selectScheme(HTAASetup * setup)
745 {
746     int scheme;
747 
748     if (setup && setup->valid_schemes) {
749 	for (scheme = HTAA_BASIC; scheme < HTAA_MAX_SCHEMES; scheme++) {
750 	    void *object = (void *) (intptr_t) scheme;
751 
752 	    if (-1 < HTList_indexOf(setup->valid_schemes, object))
753 		return (HTAAScheme) scheme;
754 	}
755     }
756     return HTAA_BASIC;
757 }
758 
759 /*
760  *  Purpose:	Free off all module globals.
761  *  Arguments:	void
762  *  Return Value:	void
763  *  Remarks/Portability/Dependencies/Restrictions:
764  *	To be used at program exit.
765  *  Revision History:
766  *	06-19-96	created - FM
767  */
free_HTAAGlobals(void)768 static void free_HTAAGlobals(void)
769 {
770     HTAAServer *server;
771     int n, i;
772 
773     if (server_table != NULL) {
774 	n = HTList_count(server_table);
775 	for (i = (n - 1); i >= 0; i--) {
776 	    if ((server = (HTAAServer *) HTList_objectAt(server_table,
777 							 i)) != NULL) {
778 		HTAAServer_delete(server);
779 		server = NULL;
780 	    }
781 	}
782 	HTList_delete(server_table);
783 	server_table = NULL;
784     }
785 
786     HTAAForwardAuth_reset();
787     FREE(HTAA_composeAuthResult);
788     FREE(current_hostname);
789     FREE(current_docname);
790     FREE(proxy_hostname);
791     FREE(proxy_docname);
792     FREE(compose_auth_stringResult);
793     FREE(secret_key);
794 }
795 
796 /* BROWSER PUBLIC					HTAA_composeAuth()
797  *
798  *	SELECT THE AUTHENTICATION SCHEME AND
799  *	COMPOSE THE ENTIRE AUTHORIZATION HEADER LINE
800  *	IF WE ALREADY KNOW THAT THE HOST REQUIRES AUTHENTICATION
801  *
802  * ON ENTRY:
803  *	hostname	is the hostname of the server.
804  *	portnumber	is the portnumber in which the server runs.
805  *	docname		is the pathname of the document (as in URL)
806  *	IsProxy		should be TRUE if this is a proxy.
807  *
808  * ON EXIT:
809  *	returns	NULL, if no authorization seems to be needed, or
810  *		if it is the entire Authorization: line, e.g.
811  *
812  *		   "Authorization: Basic username:password"
813  *
814  *		As usual, this string is automatically freed.
815  */
HTAA_composeAuth(const char * hostname,const int portnumber,const char * docname,int IsProxy)816 char *HTAA_composeAuth(const char *hostname,
817 		       const int portnumber,
818 		       const char *docname,
819 		       int IsProxy)
820 {
821     char *auth_string;
822     BOOL retry;
823     HTAAScheme scheme;
824     size_t len;
825 
826     /*
827      * Setup atexit() freeing if not done already.  - FM
828      */
829     if (!free_HTAAGlobalsSet) {
830 #ifdef LY_FIND_LEAKS
831 	atexit(free_HTAAGlobals);
832 #endif
833 	free_HTAAGlobalsSet = TRUE;
834     }
835 
836     /*
837      * Make gateway httpds pass authorization field as it was received.  (This
838      * still doesn't really work because Authenticate:  headers from remote
839      * server are not forwarded to client yet so it cannot really know that it
840      * should send authorization; I will not implement it yet because I feel we
841      * will soon change radically the way requests are represented to allow
842      * multithreading on server-side.  Life is hard.)
843      */
844     if (HTAAForwardAuth) {
845 	CTRACE((tfp, "HTAA_composeAuth: %s\n",
846 		"Forwarding received authorization"));
847 	StrAllocCopy(HTAA_composeAuthResult, HTAAForwardAuth);
848 	HTAAForwardAuth_reset();	/* Just a precaution */
849 	return HTAA_composeAuthResult;
850     }
851 
852     FREE(HTAA_composeAuthResult);	/* From previous call */
853 
854     if (IsProxy) {
855 	/*
856 	 * Proxy Authorization required.  - AJL
857 	 */
858 
859 	CTRACE((tfp, "Composing Proxy Authorization for %s:%d/%s\n",
860 		hostname, portnumber, docname));
861 
862 	if (proxy_portnumber != portnumber ||
863 	    !proxy_hostname || !proxy_docname ||
864 	    !hostname || !docname ||
865 	    0 != strcmp(proxy_hostname, hostname) ||
866 	    0 != strcmp(proxy_docname, docname)) {
867 
868 	    retry = NO;
869 
870 	    proxy_portnumber = portnumber;
871 
872 	    if (hostname)
873 		StrAllocCopy(proxy_hostname, hostname);
874 	    else
875 		FREE(proxy_hostname);
876 
877 	    if (docname)
878 		StrAllocCopy(proxy_docname, docname);
879 	    else
880 		FREE(proxy_docname);
881 	} else {
882 	    retry = YES;
883 	}
884 
885 	if (!proxy_setup || !retry)
886 	    proxy_setup = HTAASetup_lookup(hostname, portnumber,
887 					   docname, IsProxy);
888 
889 	if (!proxy_setup)
890 	    return NULL;
891 
892 	switch (scheme = HTAA_selectScheme(proxy_setup)) {
893 	case HTAA_BASIC:
894 	case HTAA_PUBKEY:
895 	    auth_string = compose_auth_string(scheme, proxy_setup, IsProxy);
896 	    break;
897 	case HTAA_KERBEROS_V4:
898 	    /* OTHER AUTHENTICATION ROUTINES ARE CALLED HERE */
899 	default:
900 	    {
901 		char *msg = NULL;
902 
903 		HTSprintf0(&msg, "%s `%s'",
904 			   gettext("This client doesn't know how to compose proxy authorization information for scheme"),
905 			   HTAAScheme_name(scheme));
906 		HTAlert(msg);
907 		FREE(msg);
908 		auth_string = NULL;
909 	    }
910 	}			/* switch scheme */
911 
912 	proxy_setup->retry = NO;
913 
914 	if (!auth_string)
915 	    /*
916 	     * Signal a failure.  - FM
917 	     */
918 	    return NULL;	/* Added by marca. */
919 	if (*auth_string == '\0') {
920 	    /*
921 	     * Signal an abort.  - FM
922 	     */
923 	    StrAllocCopy(HTAA_composeAuthResult, "");
924 	    return (HTAA_composeAuthResult);
925 	}
926 	len = strlen(auth_string) + strlen(HTAAScheme_name(scheme)) + 26;
927 	if ((HTAA_composeAuthResult = typecallocn(char, len)) == 0)
928 	      outofmem(__FILE__, "HTAA_composeAuth");
929 
930 	assert(HTAA_composeAuthResult != NULL);
931 
932 	strcpy(HTAA_composeAuthResult, "Proxy-Authorization: ");
933 
934     } else {
935 	/*
936 	 * Normal WWW authorization.
937 	 */
938 	CTRACE((tfp, "Composing Authorization for %s:%d/%s\n",
939 		hostname, portnumber, docname));
940 
941 	if (current_portnumber != portnumber ||
942 	    !current_hostname || !current_docname ||
943 	    !hostname || !docname ||
944 	    0 != strcmp(current_hostname, hostname) ||
945 	    0 != strcmp(current_docname, docname)) {
946 
947 	    retry = NO;
948 
949 	    current_portnumber = portnumber;
950 
951 	    if (hostname)
952 		StrAllocCopy(current_hostname, hostname);
953 	    else
954 		FREE(current_hostname);
955 
956 	    if (docname)
957 		StrAllocCopy(current_docname, docname);
958 	    else
959 		FREE(current_docname);
960 	} else {
961 	    retry = YES;
962 	}
963 
964 	if (!current_setup || !retry)
965 	    current_setup = HTAASetup_lookup(hostname, portnumber,
966 					     docname, IsProxy);
967 
968 	if (!current_setup)
969 	    return NULL;
970 
971 	switch (scheme = HTAA_selectScheme(current_setup)) {
972 	case HTAA_BASIC:
973 	case HTAA_PUBKEY:
974 	    auth_string = compose_auth_string(scheme, current_setup, IsProxy);
975 	    break;
976 	case HTAA_KERBEROS_V4:
977 	    /* OTHER AUTHENTICATION ROUTINES ARE CALLED HERE */
978 	default:
979 	    {
980 		char *msg = 0;
981 
982 		HTSprintf0(&msg, "%s `%s'",
983 			   gettext("This client doesn't know how to compose authorization information for scheme"),
984 			   HTAAScheme_name(scheme));
985 		HTAlert(msg);
986 		FREE(msg);
987 		auth_string = NULL;
988 	    }
989 	}			/* switch scheme */
990 
991 	current_setup->retry = NO;
992 
993 	if (!auth_string)
994 	    /*
995 	     * Signal a failure.  - FM
996 	     */
997 	    return NULL;	/* Added by marca. */
998 	if (*auth_string == '\0') {
999 	    /*
1000 	     * Signal an abort.  - FM
1001 	     */
1002 	    StrAllocCopy(HTAA_composeAuthResult, "");
1003 	    return (HTAA_composeAuthResult);
1004 	}
1005 
1006 	len = strlen(auth_string) + strlen(HTAAScheme_name(scheme)) + 20;
1007 	if ((HTAA_composeAuthResult = typecallocn(char, len)) == 0)
1008 	      outofmem(__FILE__, "HTAA_composeAuth");
1009 
1010 	assert(HTAA_composeAuthResult != NULL);
1011 
1012 	strcpy(HTAA_composeAuthResult, "Authorization: ");
1013     }
1014 
1015     strcat(HTAA_composeAuthResult, HTAAScheme_name(scheme));
1016     strcat(HTAA_composeAuthResult, " ");
1017     strcat(HTAA_composeAuthResult, auth_string);
1018     return HTAA_composeAuthResult;
1019 }
1020 
1021 /* BROWSER PUBLIC				HTAA_shouldRetryWithAuth()
1022  *
1023  *		DETERMINES IF WE SHOULD RETRY THE SERVER
1024  *		WITH AUTHORIZATION
1025  *		(OR IF ALREADY RETRIED, WITH A DIFFERENT
1026  *		USERNAME AND/OR PASSWORD (IF MISSPELLED))
1027  * ON ENTRY:
1028  *	start_of_headers is the first block already read from socket,
1029  *			but status line skipped; i.e., points to the
1030  *			start of the header section.
1031  *	length		is the remaining length of the first block.
1032  *	soc		is the socket to read the rest of server reply.
1033  *	IsProxy		should be TRUE if this is a proxy.
1034  *
1035  *			This function should only be called when
1036  *			server has replied with a 401 (Unauthorized)
1037  *			status code.
1038  * ON EXIT:
1039  *	returns		YES, if connection should be retried.
1040  *			     The node containing all the necessary
1041  *			     information is
1042  *				* either constructed if it does not exist
1043  *				* or password is reset to NULL to indicate
1044  *				  that username and password should be
1045  *				  reprompted when composing Authorization:
1046  *				  field (in function HTAA_composeAuth()).
1047  *			NO, otherwise.
1048  */
HTAA_shouldRetryWithAuth(char * start_of_headers,size_t length,int soc,int IsProxy)1049 BOOL HTAA_shouldRetryWithAuth(char *start_of_headers,
1050 			      size_t length,
1051 			      int soc,
1052 			      int IsProxy)
1053 {
1054     HTAAScheme scheme;
1055     char *line = NULL;
1056     int num_schemes = 0;
1057     HTList *valid_schemes = HTList_new();
1058     HTAssocList **scheme_specifics = NULL;
1059     char *ctemplate = NULL;
1060     char *temp = NULL;
1061     BOOL result = NO;
1062 
1063     /*
1064      * Setup atexit() freeing if not done already.  - FM
1065      */
1066     if (!free_HTAAGlobalsSet) {
1067 #ifdef LY_FIND_LEAKS
1068 	atexit(free_HTAAGlobals);
1069 #endif
1070 	free_HTAAGlobalsSet = TRUE;
1071     }
1072 
1073     /*
1074      * Read server reply header lines
1075      */
1076     CTRACE((tfp, "Server reply header lines:\n"));
1077 
1078     HTAA_setupReader(start_of_headers, length, soc);
1079     while (NULL != (line = HTAA_getUnfoldedLine()) && *line != '\0') {
1080 	CTRACE((tfp, "%s\n", line));
1081 
1082 	if (strchr(line, ':')) {	/* Valid header line */
1083 
1084 	    char *p = line;
1085 	    char *fieldname = HTNextField(&p);
1086 	    char *arg1 = HTNextField(&p);
1087 	    char *args = p;
1088 
1089 	    if ((IsProxy &&
1090 		 0 == strcasecomp(fieldname, "Proxy-Authenticate:")) ||
1091 		(!IsProxy &&
1092 		 0 == strcasecomp(fieldname, "WWW-Authenticate:"))) {
1093 		if (isEmpty(arg1) || isEmpty(args)) {
1094 		    HTSprintf0(&temp, gettext("Invalid header '%s%s%s%s%s'"), line,
1095 			       (non_empty(arg1) ? " " : ""),
1096 			       NonNull(arg1),
1097 			       (non_empty(args) ? " " : ""),
1098 			       NonNull(args));
1099 		    HTAlert(temp);
1100 		    FREE(temp);
1101 		} else if (HTAA_UNKNOWN != (scheme = HTAAScheme_enum(arg1))) {
1102 		    HTList_addObject(valid_schemes, (void *) scheme);
1103 		    if (!scheme_specifics) {
1104 			int i;
1105 
1106 			scheme_specifics =
1107 			    typecallocn(HTAssocList *, HTAA_MAX_SCHEMES);
1108 
1109 			if (!scheme_specifics)
1110 			    outofmem(__FILE__, "HTAA_shouldRetryWithAuth");
1111 
1112 			assert(scheme_specifics != NULL);
1113 
1114 			for (i = 0; i < HTAA_MAX_SCHEMES; i++)
1115 			    scheme_specifics[i] = NULL;
1116 		    }
1117 		    scheme_specifics[scheme] = HTAA_parseArgList(args);
1118 		    num_schemes++;
1119 		} else {
1120 		    CTRACE((tfp, "Unknown scheme `%s' %s\n",
1121 			    NONNULL(arg1),
1122 			    (IsProxy ?
1123 			     "in Proxy-Authenticate: field" :
1124 			     "in WWW-Authenticate: field")));
1125 		}
1126 	    }
1127 
1128 	    else if (!IsProxy &&
1129 		     0 == strcasecomp(fieldname, "WWW-Protection-Template:")) {
1130 		CTRACE((tfp, "Protection template set to `%s'\n", arg1));
1131 		StrAllocCopy(ctemplate, arg1);
1132 	    }
1133 
1134 	} else {
1135 	    CTRACE((tfp, "Invalid header line `%s' ignored\n", line));
1136 	}
1137 
1138 	FREE(line);
1139     }				/* while header lines remain */
1140     FREE(line);
1141 
1142     /*
1143      * So should we retry with authorization?
1144      */
1145     if (IsProxy) {
1146 	if (num_schemes == 0) {
1147 	    /*
1148 	     * No proxy authorization valid
1149 	     */
1150 	    proxy_setup = NULL;
1151 	    result = NO;
1152 	}
1153 	/*
1154 	 * Doing it for proxy.  -AJL
1155 	 */
1156 	else if (proxy_setup && proxy_setup->server) {
1157 	    /*
1158 	     * We have already tried with proxy authorization.  Either we don't
1159 	     * have access or username or password was misspelled.
1160 	     *
1161 	     * Update scheme-specific parameters (in case they have expired by
1162 	     * chance).
1163 	     */
1164 	    HTAASetup_updateSpecifics(proxy_setup, scheme_specifics);
1165 
1166 	    if (NO == HTConfirm(AUTH_FAILED_PROMPT)) {
1167 		proxy_setup = NULL;
1168 		result = NO;
1169 	    } else {
1170 		/*
1171 		 * Re-ask username+password (if misspelled).
1172 		 */
1173 		HTList_delete(valid_schemes);
1174 		proxy_setup->retry = YES;
1175 		result = YES;
1176 	    }
1177 	} else {
1178 	    /*
1179 	     * proxy_setup == NULL, i.e., we have a first connection to a
1180 	     * protected server or the server serves a wider set of documents
1181 	     * than we expected so far.
1182 	     */
1183 	    HTAAServer *server = HTAAServer_lookup(proxy_hostname,
1184 						   proxy_portnumber,
1185 						   IsProxy);
1186 
1187 	    if (!server) {
1188 		server = HTAAServer_new(proxy_hostname,
1189 					proxy_portnumber,
1190 					IsProxy);
1191 	    }
1192 	    if (!ctemplate)	/* Proxy matches everything  -AJL */
1193 		StrAllocCopy(ctemplate, "*");
1194 	    proxy_setup = HTAASetup_new(server,
1195 					ctemplate,
1196 					valid_schemes,
1197 					scheme_specifics);
1198 	    FREE(ctemplate);
1199 
1200 	    HTAlert(gettext("Proxy authorization required -- retrying"));
1201 	    result = YES;
1202 	}
1203     }
1204     /*
1205      * Normal WWW authorization.
1206      */
1207     else if (num_schemes == 0) {
1208 	/*
1209 	 * No authorization valid.
1210 	 */
1211 	current_setup = NULL;
1212 	result = NO;
1213     } else if (current_setup && current_setup->server) {
1214 	/*
1215 	 * So we have already tried with WWW authorization.  Either we don't
1216 	 * have access or username or password was misspelled.
1217 	 *
1218 	 * Update scheme-specific parameters (in case they have expired by
1219 	 * chance).
1220 	 */
1221 	HTAASetup_updateSpecifics(current_setup, scheme_specifics);
1222 
1223 	if (NO == HTConfirm(AUTH_FAILED_PROMPT)) {
1224 	    current_setup = NULL;
1225 	    result = NO;
1226 	} else {
1227 	    /*
1228 	     * Re-ask username+password (if misspelled).
1229 	     */
1230 	    current_setup->retry = YES;
1231 	    result = YES;
1232 	}
1233     } else {
1234 	/*
1235 	 * current_setup == NULL, i.e., we have a first connection to a
1236 	 * protected server or the server serves a wider set of documents than
1237 	 * we expected so far.
1238 	 */
1239 	HTAAServer *server = HTAAServer_lookup(current_hostname,
1240 					       current_portnumber,
1241 					       IsProxy);
1242 
1243 	if (!server) {
1244 	    server = HTAAServer_new(current_hostname,
1245 				    current_portnumber,
1246 				    IsProxy);
1247 	}
1248 	if (!ctemplate)
1249 	    ctemplate = HTAA_makeProtectionTemplate(current_docname);
1250 	current_setup = HTAASetup_new(server,
1251 				      ctemplate,
1252 				      valid_schemes,
1253 				      scheme_specifics);
1254 	FREE(ctemplate);
1255 
1256 	HTAlert(gettext("Access without authorization denied -- retrying"));
1257 	result = YES;
1258     }
1259 
1260     if (result == NO) {
1261 	HTList_delete(valid_schemes);
1262     }
1263     return result;
1264 }
1265 
1266 /*
1267  *  This function clears all authorization information by
1268  *  invoking the free_HTAAGlobals() function, which normally
1269  *  is invoked at exit.  It allows a browser command to do
1270  *  this at any time, for example, if the user is leaving
1271  *  the terminal for a period of time, but does not want
1272  *  to end the current session.  - FM
1273  */
HTClearHTTPAuthInfo(void)1274 void HTClearHTTPAuthInfo(void)
1275 {
1276     /*
1277      * Need code to check cached documents against the protection templates,
1278      * and do something to ensure that any protected documents no longer can be
1279      * accessed without a new retrieval.  - FM
1280      */
1281 
1282     /*
1283      * Now free all of the authorization info, and reset the
1284      * free_HTAAGlobalsSet flag.  - FM
1285      */
1286     free_HTAAGlobals();
1287     free_HTAAGlobalsSet = FALSE;
1288 }
1289