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