about summary refs log blame commit diff stats
path: root/.travis.yml
blob: 5d955bc0d7ba17bca37da22de4447e03d6be43a2 (plain) (tree)
1
2
3
4
            

              
         













                                                                                



                               
               
                            

                               
             

                                                            





                                                                                 

               







                                                                                                                   
      

       
                                            
                                           

                                          
                  




                    
dist: bionic
language: bash

services:
  - docker # Linux tests are run in Docker containers.

addons:
  homebrew:
    brewfile: Brewfile.travis # mac OS dependencies.
    # libsignal-protocol-c is still not in the Travis CI Homebrew snapshot, the
    # line below could be removed when the snapshot has been updated to speed up
    # the OSX job.
    update: true

matrix:
  include:
    - os: linux
      env: BUILD_FLAVOR=tumbleweed
#    - os: linux
#      env: BUILD_FLAVOR=debian
#    - os: linux
#      env: BUILD_FLAVOR=ubuntu
    - os: linux
      env: BUILD_FLAVOR=arch
#    - os: linux
#      env: BUILD_FLAVOR=fedora
    - os: osx
      env:
        # Ensure that "keg-only" Homebrew versions are used.
        - PKG_CONFIG_PATH="/usr/local/opt/ncurses/lib/pkgconfig:$PKG_CONFIG_PATH"
        - PKG_CONFIG_PATH="/usr/local/opt/expat/lib/pkgconfig:$PKG_CONFIG_PATH"
        - PKG_CONFIG_PATH="/usr/local/opt/curl/lib/pkgconfig:$PKG_CONFIG_PATH"
        - PKG_CONFIG_PATH="/usr/local/opt/openssl/lib/pkgconfig:$PKG_CONFIG_PATH"
        - PKG_CONFIG_PATH="/usr/local/opt/libffi/lib/pkgconfig:$PKG_CONFIG_PATH"
        - PKG_CONFIG_PATH="/usr/local/opt/sqlite/lib/pkgconfig:$PKG_CONFIG_PATH"

before_install:
  - >
    if [ "$TRAVIS_OS_NAME" = "linux" ]; then
      # Ensure that Travis uses the latest version of Docker.
      curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
      sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
      sudo apt-get update
      sudo apt-get -y -o Dpkg::Options::="--force-confnew" install docker-ce
      docker build -f Dockerfile."$BUILD_FLAVOR" -t profanity .
    fi

script:
  - if [ "$TRAVIS_OS_NAME" = "linux" ]; then
    docker run -it profanity ./ci-build.sh;
    fi
  - if [ "$TRAVIS_OS_NAME" = "osx" ]; then
    ./ci-build.sh;
    fi

after_failure:
  - cat ./config.log
  - env
href='#n771'>771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284
/*
 * $LynxId: HTAABrow.c,v 1.34 2010/09/24 08:27:42 tom Exp $
 *
 * MODULE							HTAABrow.c
 *		BROWSER SIDE ACCESS AUTHORIZATION MODULE
 *
 *	Contains the code for keeping track on server hostnames,
 *	port numbers, scheme names, usernames, passwords
 *	(and servers' public keys).
 *
 * IMPORTANT:
 *	Routines in this module use dynamic allocation, but free
 *	automatically all the memory reserved by them.
 *
 *	Therefore the caller never has to (and never should)
 *	free() any object returned by these functions.
 *
 *	Therefore also all the strings returned by this package
 *	are only valid until the next call to the same function
 *	is made.  This approach is selected, because of the nature
 *	of access authorization: no string returned by the package
 *	needs to be valid longer than until the next call.
 *
 *	This also makes it easy to plug the AA package in:
 *	you don't have to ponder whether to free() something
 *	here or is it done somewhere else (because it is always
 *	done somewhere else).
 *
 *	The strings that the package needs to store are copied
 *	so the original strings given as parameters to AA
 *	functions may be freed or modified with no side effects.
 *
 *	The AA package does not free() anything else than what
 *	it has itself allocated.
 *
 * AUTHORS:
 *	AL	Ari Luotonen	luotonen@dxcern.cern.ch
 *
 * HISTORY:
 *	Oct 17	AL	Made corrections suggested by marca:
 *			Added  if (!realm->username) return NULL;
 *			Changed some ""s to NULLs.
 *			Now doing calloc() to init uuencode source;
 *			otherwise HTUU_encode() reads uninitialized memory
 *			every now and then (not a real bug but not pretty).
 *			Corrected the formula for uuencode destination size.
 *
 * 28 Apr 1997	AJL	Do Proxy Authorisation.
 *
 * BUGS:
 *
 *
 */

#include <HTUtils.h>
#include <HTString.h>
#include <HTParse.h>		/* URL parsing function         */
#include <HTList.h>		/* HTList object                */
#include <HTAlert.h>		/* HTConfirm(), HTPrompt()      */
#include <HTAAUtil.h>		/* AA common to both sides      */
#include <HTAssoc.h>		/* Assoc list                   */
#include <HTAccess.h>		/* Are we using an HTTP gateway? */
#include <HTAABrow.h>		/* Implemented here             */
#include <HTUU.h>		/* Uuencoding and uudecoding    */

#include <LYLeaks.h>

/*
 *  Local datatype definitions
 *
 *  HTAAServer contains all the information about one server.
 */
typedef struct {

    char *hostname;		/* Host's name                  */
    int portnumber;		/* Port number                  */
    BOOL IsProxy;		/* Is it a proxy?               */
    HTList *setups;		/* List of protection setups 
				   on this server; i.e., valid
				   authentication schemes and
				   templates when to use them.
				   This is actually a list of
				   HTAASetup objects.           */
    HTList *realms;		/* Information about passwords  */
} HTAAServer;

/*
 *  HTAASetup contains information about one server's one
 *  protected tree of documents.
 */
typedef struct {
    HTAAServer *server;		/* Which server serves this tree             */
    char *ctemplate;		/* Template for this tree                    */
    HTList *valid_schemes;	/* Valid authentic.schemes                   */
    HTAssocList **scheme_specifics;	/* Scheme specific params                  */
    BOOL retry;			/* Failed last time -- reprompt (or whatever) */
} HTAASetup;

/*
 *  Information about usernames and passwords in
 *  Basic and Pubkey authentication schemes;
 */
typedef struct {
    char *realmname;		/* Password domain name         */
    char *username;		/* Username in that domain      */
    char *password;		/* Corresponding password       */
} HTAARealm;

/*
 *  To free off all globals. - FM
 */
static void free_HTAAGlobals(void);
static BOOL free_HTAAGlobalsSet = FALSE;
static char *HTAA_composeAuthResult = NULL;
static char *compose_auth_stringResult = NULL;	/* Uuencoded presentation */

/*
 *  Module-wide global variables
 */
static HTList *server_table = NULL;	/* Browser's info about servers      */
static char *secret_key = NULL;	/* Browser's latest secret key       */
static HTAASetup *current_setup = NULL;		/* The server setup we are currently */

					/* talking to                        */
static char *current_hostname = NULL;	/* The server's name and portnumber  */
static int current_portnumber = 80;	/* where we are currently trying to  */

					/* connect.                          */
static char *current_docname = NULL;	/* The document's name we are        */

					/* trying to access.                 */
static char *HTAAForwardAuth = NULL;	/* Authorization: line to forward    */

					/* (used by gateway httpds)          */
static HTAASetup *proxy_setup = NULL;	/* Same as above, but for Proxy -AJL */
static char *proxy_hostname = NULL;
static char *proxy_docname = NULL;
static int proxy_portnumber = 80;

/*** HTAAForwardAuth for enabling gateway-httpds to forward Authorization ***/

void HTAAForwardAuth_set(const char *scheme_name,
			 const char *scheme_specifics)
{
    size_t len = (20
		  + (scheme_name ? strlen(scheme_name) : 0)
		  + (scheme_specifics ? strlen(scheme_specifics) : 0));

    FREE(HTAAForwardAuth);
    if ((HTAAForwardAuth = typecallocn(char, len)) == 0)
	  outofmem(__FILE__, "HTAAForwardAuth_set");

    assert(HTAAForwardAuth != 0);

    strcpy(HTAAForwardAuth, "Authorization: ");
    if (scheme_name) {
	strcat(HTAAForwardAuth, scheme_name);
	strcat(HTAAForwardAuth, " ");
	if (scheme_specifics) {
	    strcat(HTAAForwardAuth, scheme_specifics);
	}
    }
}

void HTAAForwardAuth_reset(void)
{
    FREE(HTAAForwardAuth);
}

/**************************** HTAAServer ***********************************/

static void HTAASetup_delete(HTAASetup * killme);	/* Forward */

/* static						HTAAServer_new()
 *		ALLOCATE A NEW NODE TO HOLD SERVER INFO
 *		AND ADD IT TO THE LIST OF SERVERS
 * ON ENTRY:
 *	hostname	is the name of the host that the server
 *			is running in.
 *	portnumber	is the portnumber which the server listens.
 *	IsProxy		should be TRUE if this is a proxy.
 *
 * ON EXIT:
 *	returns		the newly-allocated node with all the strings
 *			duplicated.
 *			Strings will be automatically freed by
 *			the function HTAAServer_delete(), which also
 *			frees the node itself.
 */
static HTAAServer *HTAAServer_new(const char *hostname,
				  int portnumber,
				  int IsProxy)
{
    HTAAServer *server;

    if ((server = typecalloc(HTAAServer)) == 0)
	  outofmem(__FILE__, "HTAAServer_new");

    assert(server != NULL);

    server->hostname = NULL;
    server->portnumber = (portnumber > 0 ? portnumber : 80);
    server->IsProxy = (BOOLEAN) IsProxy;
    server->setups = HTList_new();
    server->realms = HTList_new();

    if (hostname)
	StrAllocCopy(server->hostname, hostname);

    if (!server_table)
	server_table = HTList_new();

    HTList_addObject(server_table, (void *) server);

    return server;
}

/* static						HTAAServer_delete()
 *
 *	DELETE THE ENTRY FOR THE SERVER FROM THE HOST TABLE,
 *	AND FREE THE MEMORY USED BY IT.
 *
 * ON ENTRY:
 *	killme		points to the HTAAServer to be freed.
 *
 * ON EXIT:
 *	returns		nothing.
 */
static void HTAAServer_delete(HTAAServer *killme)
{
    int n, i;
    HTAASetup *setup;
    HTAARealm *realm;
    HTList *cur;

    if (killme) {
	if (killme->setups != NULL) {
	    n = HTList_count(killme->setups);
	    for (i = (n - 1); i >= 0; i--) {
		if ((setup = (HTAASetup *) HTList_objectAt(killme->setups,
							   i)) != NULL) {
		    HTAASetup_delete(setup);
		    setup = NULL;
		}
	    }
	    HTList_delete(killme->setups);
	    killme->setups = NULL;
	}

	cur = killme->realms;
	while (NULL != (realm = (HTAARealm *) HTList_nextObject(cur))) {
	    FREE(realm->realmname);
	    FREE(realm->username);
	    FREE(realm->password);
	    FREE(realm);
	}
	HTList_delete(killme->realms);
	killme->realms = NULL;

	FREE(killme->hostname);

	HTList_removeObject(server_table, (void *) killme);
	FREE(killme);
    }
}

/* static						HTAAServer_lookup()
 *		LOOK UP SERVER BY HOSTNAME AND PORTNUMBER
 * ON ENTRY:
 *	hostname	obvious.
 *	portnumber	if non-positive defaults to 80.
 *	IsProxy		should be TRUE if this is a proxy.
 *
 *	Looks up the server in the module-global server_table.
 *
 * ON EXIT:
 *	returns		pointer to a HTAAServer structure
 *			representing the looked-up server.
 *			NULL, if not found.
 */
static HTAAServer *HTAAServer_lookup(const char *hostname,
				     int portnumber,
				     int IsProxy)
{
    if (hostname) {
	HTList *cur = server_table;
	HTAAServer *server;

	if (portnumber <= 0)
	    portnumber = 80;

	while (NULL != (server = (HTAAServer *) HTList_nextObject(cur))) {
	    if (server->portnumber == portnumber &&
		0 == strcmp(server->hostname, hostname) &&
		server->IsProxy == IsProxy)
		return server;
	}
    }
    return NULL;		/* NULL parameter, or not found */
}

/*************************** HTAASetup *******************************/

/* static						HTAASetup_lookup()
 *	FIGURE OUT WHICH AUTHENTICATION SETUP THE SERVER
 *	IS USING FOR A GIVEN FILE ON A GIVEN HOST AND PORT
 *
 * ON ENTRY:
 *	hostname	is the name of the server host machine.
 *	portnumber	is the port that the server is running in.
 *	docname		is the (URL-)pathname of the document we
 *			are trying to access.
 *	IsProxy		should be TRUE if this is a proxy.
 *
 *	This function goes through the information known about
 *	all the setups of the server, and finds out if the given
 *	filename resides in one of the protected directories.
 *
 * ON EXIT:
 *	returns		NULL if no match.
 *			Otherwise, a HTAASetup structure representing
 *			the protected server setup on the corresponding
 *			document tree.
 *
 */
static HTAASetup *HTAASetup_lookup(const char *hostname,
				   int portnumber,
				   const char *docname,
				   int IsProxy)
{
    HTAAServer *server;
    HTAASetup *setup;

    if (portnumber <= 0)
	portnumber = 80;

    if (hostname && docname && *hostname && *docname &&
	NULL != (server = HTAAServer_lookup(hostname,
					    portnumber,
					    IsProxy))) {

	HTList *cur = server->setups;

	CTRACE((tfp, "%s %s (%s:%d:%s)\n",
		"HTAASetup_lookup: resolving setup for",
		(IsProxy ? "proxy" : "server"),
		hostname, portnumber, docname));

	while (NULL != (setup = (HTAASetup *) HTList_nextObject(cur))) {
	    if (HTAA_templateMatch(setup->ctemplate, docname)) {
		CTRACE((tfp, "%s `%s' %s `%s'\n",
			"HTAASetup_lookup:", docname,
			"matched template", setup->ctemplate));
		return setup;
	    } else {
		CTRACE((tfp, "%s `%s' %s `%s'\n",
			"HTAASetup_lookup:", docname,
			"did NOT match template", setup->ctemplate));
	    }
	}			/* while setups remain */
    }
    /* if valid parameters and server found */
    CTRACE((tfp, "%s `%s' %s\n",
	    "HTAASetup_lookup: No template matched",
	    NONNULL(docname),
	    "(so probably not protected)"));

    return NULL;		/* NULL in parameters, or not found */
}

/* static						HTAASetup_new()
 *			CREATE A NEW SETUP NODE
 * ON ENTRY:
 *	server		is a pointer to a HTAAServer structure
 *			to which this setup belongs.
 *	ctemplate	documents matching this template
 *			are protected according to this setup.
 *	valid_schemes	a list containing all valid authentication
 *			schemes for this setup.
 *			If NULL, all schemes are disallowed.
 *	scheme_specifics is an array of assoc lists, which
 *			contain scheme specific parameters given
 *			by server in Authenticate: fields.
 *			If NULL, all scheme specifics are
 *			set to NULL.
 * ON EXIT:
 *	returns		a new HTAASetup node, and also adds it as
 *			part of the HTAAServer given as parameter.
 */
static HTAASetup *HTAASetup_new(HTAAServer *server, char *ctemplate,
				HTList *valid_schemes,
				HTAssocList **scheme_specifics)
{
    HTAASetup *setup;

    if (!server || isEmpty(ctemplate))
	return NULL;

    if ((setup = typecalloc(HTAASetup)) == 0)
	outofmem(__FILE__, "HTAASetup_new");

    assert(setup != NULL);

    setup->retry = NO;
    setup->server = server;
    setup->ctemplate = NULL;
    if (ctemplate)
	StrAllocCopy(setup->ctemplate, ctemplate);
    setup->valid_schemes = valid_schemes;
    setup->scheme_specifics = scheme_specifics;

    HTList_addObject(server->setups, (void *) setup);

    return setup;
}

/* static						HTAASetup_delete()
 *			FREE A HTAASetup STRUCTURE
 * ON ENTRY:
 *	killme		is a pointer to the structure to free().
 *
 * ON EXIT:
 *	returns		nothing.
 */
static void HTAASetup_delete(HTAASetup * killme)
{
    int scheme;

    if (killme) {
	FREE(killme->ctemplate);
	if (killme->valid_schemes) {
	    HTList_delete(killme->valid_schemes);
	    killme->valid_schemes = NULL;
	}
	for (scheme = 0; scheme < HTAA_MAX_SCHEMES; scheme++)
	    if (killme->scheme_specifics[scheme])
		HTAssocList_delete(killme->scheme_specifics[scheme]);
	FREE(killme->scheme_specifics);
	FREE(killme);
    }
}

/* static					HTAASetup_updateSpecifics()
 *		COPY SCHEME SPECIFIC PARAMETERS
 *		TO HTAASetup STRUCTURE
 * ON ENTRY:
 *	setup		destination setup structure.
 *	specifics	string array containing scheme
 *			specific parameters for each scheme.
 *			If NULL, all the scheme specific
 *			parameters are set to NULL.
 *
 * ON EXIT:
 *	returns		nothing.
 */
static void HTAASetup_updateSpecifics(HTAASetup * setup, HTAssocList **specifics)
{
    int scheme;

    if (setup) {
	if (setup->scheme_specifics) {
	    for (scheme = 0; scheme < HTAA_MAX_SCHEMES; scheme++) {
		if (setup->scheme_specifics[scheme])
		    HTAssocList_delete(setup->scheme_specifics[scheme]);
	    }
	    FREE(setup->scheme_specifics);
	}
	setup->scheme_specifics = specifics;
    }
}

/*************************** HTAARealm **********************************/

/* static						HTAARealm_lookup()
 *		LOOKUP HTAARealm STRUCTURE BY REALM NAME
 * ON ENTRY:
 *	realm_table	a list of realm objects.
 *	realmname	is the name of realm to look for.
 *
 * ON EXIT:
 *	returns		the realm.  NULL, if not found.
 */
static HTAARealm *HTAARealm_lookup(HTList *realm_table,
				   const char *realmname)
{
    if (realm_table && realmname) {
	HTList *cur = realm_table;
	HTAARealm *realm;

	while (NULL != (realm = (HTAARealm *) HTList_nextObject(cur))) {
	    if (0 == strcmp(realm->realmname, realmname))
		return realm;
	}
    }
    return NULL;		/* No table, NULL param, or not found */
}

/* static						HTAARealm_new()
 *		CREATE A NODE CONTAINING USERNAME AND
 *		PASSWORD USED FOR THE GIVEN REALM.
 *		IF REALM ALREADY EXISTS, CHANGE
 *		USERNAME/PASSWORD.
 * ON ENTRY:
 *	realm_table	a list of realms to where to add
 *			the new one, too.
 *	realmname	is the name of the password domain.
 *	username	and
 *	password	are what you can expect them to be.
 *
 * ON EXIT:
 *	returns		the created realm.
 */
static HTAARealm *HTAARealm_new(HTList *realm_table,
				const char *realmname,
				const char *username,
				const char *password)
{
    HTAARealm *realm;

    realm = HTAARealm_lookup(realm_table, realmname);

    if (!realm) {
	if ((realm = typecalloc(HTAARealm)) == 0)
	      outofmem(__FILE__, "HTAARealm_new");

	assert(realm != NULL);

	realm->realmname = NULL;
	realm->username = NULL;
	realm->password = NULL;
	StrAllocCopy(realm->realmname, realmname);
	if (realm_table)
	    HTList_addObject(realm_table, (void *) realm);
    }
    if (username)
	StrAllocCopy(realm->username, username);
    if (password)
	StrAllocCopy(realm->password, password);

    return realm;
}

/***************** Basic and Pubkey Authentication ************************/

/* static						compose_auth_string()
 *
 *		COMPOSE Basic OR Pubkey AUTHENTICATION STRING;
 *		PROMPTS FOR USERNAME AND PASSWORD IF NEEDED
 *
 * ON ENTRY:
 *	scheme		is either HTAA_BASIC or HTAA_PUBKEY.
 *	setup		is the current server setup.
 *	IsProxy		should be TRUE if this is a proxy.
 *
 * ON EXIT:
 *	returns		a newly composed authorization string,
 *			(with, of course, a newly generated secret
 *			key and fresh timestamp, if Pubkey-scheme
 *			is being used).
 *			NULL, if something fails.
 * NOTE:
 *	Like throughout the entire AA package, no string or structure
 *	returned by AA package needs to (or should) be freed.
 *
 */
static char *compose_auth_string(HTAAScheme scheme, HTAASetup * setup, int IsProxy)
{
    char *cleartext = NULL;	/* Cleartext presentation */
    char *ciphertext = NULL;	/* Encrypted presentation */
    size_t len;
    char *msg = NULL;
    char *username = NULL;
    char *password = NULL;
    char *realmname = NULL;
    char *theHost = NULL;
    char *proxiedHost = NULL;
    char *thePort = NULL;
    HTAARealm *realm;
    const char *i_net_addr = "0.0.0.0";		/* Change... @@@@ */
    const char *timestamp = "42";	/* ... these @@@@ */

    FREE(compose_auth_stringResult);	/* From previous call */

    if ((scheme != HTAA_BASIC && scheme != HTAA_PUBKEY) || !setup ||
	!setup->scheme_specifics || !setup->scheme_specifics[scheme] ||
	!setup->server || !setup->server->realms)
	return NULL;

    realmname = HTAssocList_lookup(setup->scheme_specifics[scheme], "realm");
    if (!realmname)
	return NULL;

    realm = HTAARealm_lookup(setup->server->realms, realmname);
    if (!(realm &&
	  realm->username && *realm->username &&
	  realm->password) || setup->retry) {
	if (!realm) {
	    CTRACE((tfp, "%s `%s' %s\n",
		    "compose_auth_string: realm:", realmname,
		    "not found -- creating"));
	    realm = HTAARealm_new(setup->server->realms,
				  realmname, NULL, NULL);
	}
	/*
	 * The template should be either the '*' global for everything on the
	 * server (always true for proxy authorization setups), or a path for
	 * the start of a protected limb, with no host field, but we'll check
	 * for a host anyway in case a WWW-Protection-Template header set an
	 * absolute URL instead of a path.  If we do get a host from this, it
	 * will include the port.  - FM
	 */
	if ((!IsProxy) && using_proxy && setup->ctemplate) {
	    proxiedHost = HTParse(setup->ctemplate, "", PARSE_HOST);
	    if (proxiedHost && *proxiedHost != '\0') {
		theHost = proxiedHost;
	    }
	}
	/*
	 * If we didn't get a host field from the template, set up the host
	 * name and port from the setup->server elements.  - FM
	 */
	if (!theHost)
	    theHost = setup->server->hostname;
	if (setup->server->portnumber > 0 &&
	    setup->server->portnumber != 80) {
	    HTSprintf0(&thePort, ":%d", setup->server->portnumber);
	}
	/*
	 * Set up the message for the username prompt, and then issue the
	 * prompt.  The default username is included in the call to the
	 * prompting function, but the password is NULL-ed and always replaced. 
	 * - FM
	 */
	HTSprintf0(&msg, gettext("Username for '%s' at %s '%s%s':"),
		   realm->realmname,
		   (IsProxy ? "proxy" : "server"),
		   (theHost ? theHost : "??"),
		   NonNull(thePort));
	FREE(proxiedHost);
	FREE(thePort);
	username = realm->username;
	password = NULL;
	HTPromptUsernameAndPassword(msg, &username, &password, IsProxy);

	FREE(msg);
	FREE(realm->username);
	FREE(realm->password);
	realm->username = username;
	realm->password = password;

	if (!realm->username || !realm->password) {
	    /*
	     * Signals to retry.  - FM
	     */
	    return NULL;
	} else if (*realm->username == '\0') {
	    /*
	     * Signals to abort.  - FM
	     */
	    StrAllocCopy(compose_auth_stringResult, "");
	    return compose_auth_stringResult;
	}
    }

    len = (strlen(NonNull(realm->username)) +
	   strlen(NonNull(realm->password)) + 3);

    if (scheme == HTAA_PUBKEY) {
#ifdef PUBKEY
	/* Generate new secret key */
	StrAllocCopy(secret_key, HTAA_generateRandomKey());
#endif /* PUBKEY */
	/* Room for secret key, timestamp and inet address */
	len += strlen(NonNull(secret_key)) + 30;
    } else {
	FREE(secret_key);
    }

    if ((cleartext = typecallocn(char, len)) == 0)
	  outofmem(__FILE__, "compose_auth_string");

    assert(cleartext != NULL);

    if (realm->username)
	strcpy(cleartext, realm->username);
    else
	*cleartext = '\0';

    strcat(cleartext, ":");

    if (realm->password)
	strcat(cleartext, realm->password);

    if (scheme == HTAA_PUBKEY) {
	strcat(cleartext, ":");
	strcat(cleartext, i_net_addr);
	strcat(cleartext, ":");
	strcat(cleartext, timestamp);
	strcat(cleartext, ":");
	if (secret_key)
	    strcat(cleartext, secret_key);

	if (!((ciphertext = typecallocn(char, 2 * len)) &&
	        (compose_auth_stringResult = typecallocn(char, 3 * len))))
	      outofmem(__FILE__, "compose_auth_string");

#ifdef PUBKEY
	HTPK_encrypt(cleartext, ciphertext, server->public_key);
	HTUU_encode((unsigned char *) ciphertext, strlen(ciphertext),
		    compose_auth_stringResult);
#endif /* PUBKEY */
	FREE(cleartext);
	FREE(ciphertext);
    } else {			/* scheme == HTAA_BASIC */
	if (!(compose_auth_stringResult =
	      typecallocn(char, (4 * ((len + 2) / 3)) + 1)))
	      outofmem(__FILE__, "compose_auth_string");

	HTUU_encode((unsigned char *) cleartext, strlen(cleartext),
		    compose_auth_stringResult);
	FREE(cleartext);
    }
    return compose_auth_stringResult;
}

/* BROWSER static					HTAA_selectScheme()
 *		SELECT THE AUTHENTICATION SCHEME TO USE
 * ON ENTRY:
 *	setup	is the server setup structure which can
 *		be used to make the decision about the
 *		used scheme.
 *
 *	When new authentication methods are added to library
 *	this function makes the decision about which one to
 *	use at a given time.  This can be done by inspecting
 *	environment variables etc.
 *
 *	Currently only searches for the first valid scheme,
 *	and if nothing found suggests Basic scheme;
 *
 * ON EXIT:
 *	returns	the authentication scheme to use.
 */
static HTAAScheme HTAA_selectScheme(HTAASetup * setup)
{
    int scheme;

    if (setup && setup->valid_schemes) {
	for (scheme = HTAA_BASIC; scheme < HTAA_MAX_SCHEMES; scheme++) {
	    void *object = (void *) scheme;

	    if (-1 < HTList_indexOf(setup->valid_schemes, object))
		return (HTAAScheme) scheme;
	}
    }
    return HTAA_BASIC;
}

/*
 *  Purpose:	Free off all module globals.
 *  Arguments:	void
 *  Return Value:	void
 *  Remarks/Portability/Dependencies/Restrictions:
 *	To be used at program exit.
 *  Revision History:
 *	06-19-96	created - FM
 */
static void free_HTAAGlobals(void)
{
    HTAAServer *server;
    int n, i;

    if (server_table != NULL) {
	n = HTList_count(server_table);
	for (i = (n - 1); i >= 0; i--) {
	    if ((server = (HTAAServer *) HTList_objectAt(server_table,
							 i)) != NULL) {
		HTAAServer_delete(server);
		server = NULL;
	    }
	}
	HTList_delete(server_table);
	server_table = NULL;
    }

    HTAAForwardAuth_reset();
    FREE(HTAA_composeAuthResult);
    FREE(current_hostname);
    FREE(current_docname);
    FREE(proxy_hostname);
    FREE(proxy_docname);
    FREE(compose_auth_stringResult);
    FREE(secret_key);
}

/* BROWSER PUBLIC					HTAA_composeAuth()
 *
 *	SELECT THE AUTHENTICATION SCHEME AND
 *	COMPOSE THE ENTIRE AUTHORIZATION HEADER LINE
 *	IF WE ALREADY KNOW THAT THE HOST REQUIRES AUTHENTICATION
 *
 * ON ENTRY:
 *	hostname	is the hostname of the server.
 *	portnumber	is the portnumber in which the server runs.
 *	docname		is the pathname of the document (as in URL)
 *	IsProxy		should be TRUE if this is a proxy.
 *
 * ON EXIT:
 *	returns	NULL, if no authorization seems to be needed, or
 *		if it is the entire Authorization: line, e.g.
 *
 *		   "Authorization: Basic username:password"
 *
 *		As usual, this string is automatically freed.
 */
char *HTAA_composeAuth(const char *hostname,
		       const int portnumber,
		       const char *docname,
		       int IsProxy)
{
    char *auth_string;
    BOOL retry;
    HTAAScheme scheme;
    size_t len;

    /*
     * Setup atexit() freeing if not done already.  - FM
     */
    if (!free_HTAAGlobalsSet) {
#ifdef LY_FIND_LEAKS
	atexit(free_HTAAGlobals);
#endif
	free_HTAAGlobalsSet = TRUE;
    }

    /*
     * Make gateway httpds pass authorization field as it was received.  (This
     * still doesn't really work because Authenticate:  headers from remote
     * server are not forwarded to client yet so it cannot really know that it
     * should send authorization; I will not implement it yet because I feel we
     * will soon change radically the way requests are represented to allow
     * multithreading on server-side.  Life is hard.)
     */
    if (HTAAForwardAuth) {
	CTRACE((tfp, "HTAA_composeAuth: %s\n",
		"Forwarding received authorization"));
	StrAllocCopy(HTAA_composeAuthResult, HTAAForwardAuth);
	HTAAForwardAuth_reset();	/* Just a precaution */
	return HTAA_composeAuthResult;
    }

    FREE(HTAA_composeAuthResult);	/* From previous call */

    if (IsProxy) {
	/*
	 * Proxy Authorization required.  - AJL
	 */

	CTRACE((tfp, "Composing Proxy Authorization for %s:%d/%s\n",
		hostname, portnumber, docname));

	if (proxy_portnumber != portnumber ||
	    !proxy_hostname || !proxy_docname ||
	    !hostname || !docname ||
	    0 != strcmp(proxy_hostname, hostname) ||
	    0 != strcmp(proxy_docname, docname)) {

	    retry = NO;

	    proxy_portnumber = portnumber;

	    if (hostname)
		StrAllocCopy(proxy_hostname, hostname);
	    else
		FREE(proxy_hostname);

	    if (docname)
		StrAllocCopy(proxy_docname, docname);
	    else
		FREE(proxy_docname);
	} else {
	    retry = YES;
	}

	if (!proxy_setup || !retry)
	    proxy_setup = HTAASetup_lookup(hostname, portnumber,
					   docname, IsProxy);

	if (!proxy_setup)
	    return NULL;

	switch (scheme = HTAA_selectScheme(proxy_setup)) {
	case HTAA_BASIC:
	case HTAA_PUBKEY:
	    auth_string = compose_auth_string(scheme, proxy_setup, IsProxy);
	    break;
	case HTAA_KERBEROS_V4:
	    /* OTHER AUTHENTICATION ROUTINES ARE CALLED HERE */
	default:
	    {
		char *msg = NULL;

		HTSprintf0(&msg, "%s `%s'",
			   gettext("This client doesn't know how to compose proxy authorization information for scheme"),
			   HTAAScheme_name(scheme));
		HTAlert(msg);
		FREE(msg);
		auth_string = NULL;
	    }
	}			/* switch scheme */

	proxy_setup->retry = NO;

	if (!auth_string)
	    /*
	     * Signal a failure.  - FM
	     */
	    return NULL;	/* Added by marca. */
	if (*auth_string == '\0') {
	    /*
	     * Signal an abort.  - FM
	     */
	    StrAllocCopy(HTAA_composeAuthResult, "");
	    return (HTAA_composeAuthResult);
	}
	len = strlen(auth_string) + strlen(HTAAScheme_name(scheme)) + 26;
	if ((HTAA_composeAuthResult = typecallocn(char, len)) == 0)
	      outofmem(__FILE__, "HTAA_composeAuth");

	assert(HTAA_composeAuthResult != NULL);

	strcpy(HTAA_composeAuthResult, "Proxy-Authorization: ");

    } else {
	/*
	 * Normal WWW authorization.
	 */
	CTRACE((tfp, "Composing Authorization for %s:%d/%s\n",
		hostname, portnumber, docname));

	if (current_portnumber != portnumber ||
	    !current_hostname || !current_docname ||
	    !hostname || !docname ||
	    0 != strcmp(current_hostname, hostname) ||
	    0 != strcmp(current_docname, docname)) {

	    retry = NO;

	    current_portnumber = portnumber;

	    if (hostname)
		StrAllocCopy(current_hostname, hostname);
	    else
		FREE(current_hostname);

	    if (docname)
		StrAllocCopy(current_docname, docname);
	    else
		FREE(current_docname);
	} else {
	    retry = YES;
	}

	if (!current_setup || !retry)
	    current_setup = HTAASetup_lookup(hostname, portnumber,
					     docname, IsProxy);

	if (!current_setup)
	    return NULL;

	switch (scheme = HTAA_selectScheme(current_setup)) {
	case HTAA_BASIC:
	case HTAA_PUBKEY:
	    auth_string = compose_auth_string(scheme, current_setup, IsProxy);
	    break;
	case HTAA_KERBEROS_V4:
	    /* OTHER AUTHENTICATION ROUTINES ARE CALLED HERE */
	default:
	    {
		char *msg = 0;

		HTSprintf0(&msg, "%s `%s'",
			   gettext("This client doesn't know how to compose authorization information for scheme"),
			   HTAAScheme_name(scheme));
		HTAlert(msg);
		FREE(msg);
		auth_string = NULL;
	    }
	}			/* switch scheme */

	current_setup->retry = NO;

	if (!auth_string)
	    /*
	     * Signal a failure.  - FM
	     */
	    return NULL;	/* Added by marca. */
	if (*auth_string == '\0') {
	    /*
	     * Signal an abort.  - FM
	     */
	    StrAllocCopy(HTAA_composeAuthResult, "");
	    return (HTAA_composeAuthResult);
	}

	len = strlen(auth_string) + strlen(HTAAScheme_name(scheme)) + 20;
	if ((HTAA_composeAuthResult = typecallocn(char, len)) == 0)
	      outofmem(__FILE__, "HTAA_composeAuth");

	assert(HTAA_composeAuthResult != NULL);

	strcpy(HTAA_composeAuthResult, "Authorization: ");
    }

    strcat(HTAA_composeAuthResult, HTAAScheme_name(scheme));
    strcat(HTAA_composeAuthResult, " ");
    strcat(HTAA_composeAuthResult, auth_string);
    return HTAA_composeAuthResult;
}

/* BROWSER PUBLIC				HTAA_shouldRetryWithAuth()
 *
 *		DETERMINES IF WE SHOULD RETRY THE SERVER
 *		WITH AUTHORIZATION
 *		(OR IF ALREADY RETRIED, WITH A DIFFERENT
 *		USERNAME AND/OR PASSWORD (IF MISSPELLED))
 * ON ENTRY:
 *	start_of_headers is the first block already read from socket,
 *			but status line skipped; i.e., points to the
 *			start of the header section.
 *	length		is the remaining length of the first block.
 *	soc		is the socket to read the rest of server reply.
 *	IsProxy		should be TRUE if this is a proxy.
 *
 *			This function should only be called when
 *			server has replied with a 401 (Unauthorized)
 *			status code.
 * ON EXIT:
 *	returns		YES, if connection should be retried.
 *			     The node containing all the necessary
 *			     information is
 *				* either constructed if it does not exist
 *				* or password is reset to NULL to indicate
 *				  that username and password should be
 *				  reprompted when composing Authorization:
 *				  field (in function HTAA_composeAuth()).
 *			NO, otherwise.
 */
BOOL HTAA_shouldRetryWithAuth(char *start_of_headers,
			      size_t length,
			      int soc,
			      int IsProxy)
{
    HTAAScheme scheme;
    char *line = NULL;
    int num_schemes = 0;
    HTList *valid_schemes = HTList_new();
    HTAssocList **scheme_specifics = NULL;
    char *ctemplate = NULL;
    char *temp = NULL;

    /*
     * Setup atexit() freeing if not done already.  - FM
     */
    if (!free_HTAAGlobalsSet) {
#ifdef LY_FIND_LEAKS
	atexit(free_HTAAGlobals);
#endif
	free_HTAAGlobalsSet = TRUE;
    }

    /*
     * Read server reply header lines
     */
    CTRACE((tfp, "Server reply header lines:\n"));

    HTAA_setupReader(start_of_headers, length, soc);
    while (NULL != (line = HTAA_getUnfoldedLine()) && *line != '\0') {
	CTRACE((tfp, "%s\n", line));

	if (strchr(line, ':')) {	/* Valid header line */

	    char *p = line;
	    char *fieldname = HTNextField(&p);
	    char *arg1 = HTNextField(&p);
	    char *args = p;

	    if ((IsProxy &&
		 0 == strcasecomp(fieldname, "Proxy-Authenticate:")) ||
		(!IsProxy &&
		 0 == strcasecomp(fieldname, "WWW-Authenticate:"))) {
		if (isEmpty(arg1) || isEmpty(args)) {
		    HTSprintf0(&temp, gettext("Invalid header '%s%s%s%s%s'"), line,
			       (non_empty(arg1) ? " " : ""),
			       NonNull(arg1),
			       (non_empty(args) ? " " : ""),
			       NonNull(args));
		    HTAlert(temp);
		    FREE(temp);
		} else if (HTAA_UNKNOWN != (scheme = HTAAScheme_enum(arg1))) {
		    HTList_addObject(valid_schemes, (void *) scheme);
		    if (!scheme_specifics) {
			int i;

			scheme_specifics =
			    typecallocn(HTAssocList *, HTAA_MAX_SCHEMES);

			if (!scheme_specifics)
			    outofmem(__FILE__, "HTAA_shouldRetryWithAuth");

			assert(scheme_specifics != NULL);

			for (i = 0; i < HTAA_MAX_SCHEMES; i++)
			    scheme_specifics[i] = NULL;
		    }
		    scheme_specifics[scheme] = HTAA_parseArgList(args);
		    num_schemes++;
		} else {
		    CTRACE((tfp, "Unknown scheme `%s' %s\n",
			    NONNULL(arg1),
			    (IsProxy ?
			     "in Proxy-Authenticate: field" :
			     "in WWW-Authenticate: field")));
		}
	    }

	    else if (!IsProxy &&
		     0 == strcasecomp(fieldname, "WWW-Protection-Template:")) {
		CTRACE((tfp, "Protection template set to `%s'\n", arg1));
		StrAllocCopy(ctemplate, arg1);
	    }

	} else {
	    CTRACE((tfp, "Invalid header line `%s' ignored\n", line));
	}

	FREE(line);
    }				/* while header lines remain */
    FREE(line);

    /*
     * So should we retry with authorization?
     */
    if (IsProxy) {
	if (num_schemes == 0) {
	    /*
	     * No proxy authorization valid
	     */
	    proxy_setup = NULL;
	    return NO;
	}
	/*
	 * Doing it for proxy.  -AJL
	 */
	if (proxy_setup && proxy_setup->server) {
	    /*
	     * We have already tried with proxy authorization.  Either we don't
	     * have access or username or password was misspelled.
	     *
	     * Update scheme-specific parameters (in case they have expired by
	     * chance).
	     */
	    HTAASetup_updateSpecifics(proxy_setup, scheme_specifics);

	    if (NO == HTConfirm(AUTH_FAILED_PROMPT)) {
		proxy_setup = NULL;
		return NO;
	    } else {
		/*
		 * Re-ask username+password (if misspelled).
		 */
		proxy_setup->retry = YES;
		return YES;
	    }
	} else {
	    /*
	     * proxy_setup == NULL, i.e., we have a first connection to a
	     * protected server or the server serves a wider set of documents
	     * than we expected so far.
	     */
	    HTAAServer *server = HTAAServer_lookup(proxy_hostname,
						   proxy_portnumber,
						   IsProxy);

	    if (!server) {
		server = HTAAServer_new(proxy_hostname,
					proxy_portnumber,
					IsProxy);
	    }
	    if (!ctemplate)	/* Proxy matches everything  -AJL */
		StrAllocCopy(ctemplate, "*");
	    proxy_setup = HTAASetup_new(server,
					ctemplate,
					valid_schemes,
					scheme_specifics);
	    FREE(ctemplate);

	    HTAlert(gettext("Proxy authorization required -- retrying"));
	    return YES;
	}
	/* Never reached */
    }
    /*
     * Normal WWW authorization.
     */
    if (num_schemes == 0) {
	/*
	 * No authorization valid.
	 */
	current_setup = NULL;
	return NO;
    }
    if (current_setup && current_setup->server) {
	/*
	 * So we have already tried with WWW authorization.  Either we don't
	 * have access or username or password was misspelled.
	 *
	 * Update scheme-specific parameters (in case they have expired by
	 * chance).
	 */
	HTAASetup_updateSpecifics(current_setup, scheme_specifics);

	if (NO == HTConfirm(AUTH_FAILED_PROMPT)) {
	    current_setup = NULL;
	    return NO;
	} else {
	    /*
	     * Re-ask username+password (if misspelled).
	     */
	    current_setup->retry = YES;
	    return YES;
	}
    } else {
	/*
	 * current_setup == NULL, i.e., we have a first connection to a
	 * protected server or the server serves a wider set of documents than
	 * we expected so far.
	 */
	HTAAServer *server = HTAAServer_lookup(current_hostname,
					       current_portnumber,
					       IsProxy);

	if (!server) {
	    server = HTAAServer_new(current_hostname,
				    current_portnumber,
				    IsProxy);
	}
	if (!ctemplate)
	    ctemplate = HTAA_makeProtectionTemplate(current_docname);
	current_setup = HTAASetup_new(server,
				      ctemplate,
				      valid_schemes,
				      scheme_specifics);
	FREE(ctemplate);

	HTAlert(gettext("Access without authorization denied -- retrying"));
	return YES;
    }
    /* Never reached */
}

/*
 *  This function clears all authorization information by
 *  invoking the free_HTAAGlobals() function, which normally
 *  is invoked at exit.  It allows a browser command to do
 *  this at any time, for example, if the user is leaving
 *  the terminal for a period of time, but does not want
 *  to end the current session.  - FM
 */
void HTClearHTTPAuthInfo(void)
{
    /*
     * Need code to check cached documents against the protection templates,
     * and do something to ensure that any protected documents no longer can be
     * accessed without a new retrieval.  - FM
     */

    /*
     * Now free all of the authorization info, and reset the
     * free_HTAAGlobalsSet flag.  - FM
     */
    free_HTAAGlobals();
    free_HTAAGlobalsSet = FALSE;
}