Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

531 lines
14 KiB

/* simple.c -- Security Protocol Module. */
/* Jeff Hostetler, Spyglass, Inc., 1994. */
/* Copyright (C) 1994, Spyglass, Inc. All rights reserved. */
#include <module.h>
#include <rc.h>
#define MAXFIELD 64
#define MAXHASHOUTPUT 33
/*****************************************************************/
static unsigned char * x_GetSubValue(HTHeaderSVList * svl, unsigned char * name)
{
/* return pointer to value buffer in list */
while (svl)
{
if (strcasecomp(svl->name,name)==0)
return svl->value;
svl=svl->next;
}
return NULL;
}
/*****************************************************************/
static unsigned char * x_ComposeAuthResponse(F_UserInterface fpUI,
void * pvOpaqueOS,
unsigned char * szUserName,
unsigned char * szRealm,
unsigned char * szNonce,
unsigned char * szOpaque,
unsigned char * szPassword,
unsigned char * szMethod,
unsigned char * szURI)
{
/* NOTE: caller must free the string we return. */
unsigned char szResponseMD5[MAXHASHOUTPUT+1];
unsigned char szHash1[MAXHASHOUTPUT+1];
unsigned char szHash2[MAXHASHOUTPUT+1];
unsigned char * szFormat;
unsigned char * szTemp;
int len;
#define BASE_STRING "%s username=\"%s\", realm=\"%s\", nonce=\"%s\", response=\"%s\", uri=\"%s\""
#define OPAQUE_STRING ", opaque=\"%s\""
if (*szOpaque)
szFormat = BASE_STRING OPAQUE_STRING;
else
szFormat = BASE_STRING;
len = ( strlen(szFormat)
+ strlen(SCHEME_NAME) + strlen(szUserName) + strlen(szRealm)
+ strlen(szNonce) + MAXHASHOUTPUT + strlen(szURI)
+ strlen(szOpaque));
szTemp = spm_calloc(fpUI,pvOpaqueOS,1,len);
if (!szTemp)
return NULL;
sprintf(szTemp,"%s:%s:%s",szUserName,szRealm,szPassword); /* generate "U:R:P" */
md5(szTemp,szHash1); /* compute "H(U:R:P)" */
sprintf(szTemp,"%s:%s",szMethod,szURI); /* generate "M:U" */
md5(szTemp,szHash2); /* compute "H(M:U)" */
sprintf(szTemp,"%s:%s:%s",szHash1,szNonce,szHash2); /* generate "H(U:R:P):N:H(M:U)" */
md5(szTemp,szResponseMD5); /* compute "H(H(U:R:P):N:H(M:U))" */
if (*szOpaque)
sprintf(szTemp,
szFormat,
SCHEME_NAME,szUserName,szRealm,szNonce,szResponseMD5,szURI,szOpaque);
else
sprintf(szTemp,
szFormat,
SCHEME_NAME,szUserName,szRealm,szNonce,szResponseMD5,szURI);
return szTemp;
}
/*****************************************************************/
static HTSPMStatusCode x_QueryUserForInfo(F_UserInterface fpUI,
void * pvOpaqueOS,
HTHeaderList * hlProtocol,
unsigned char * szRealm,
unsigned char * szUserName,
unsigned char * szPassword)
{
HTSPMStatusCode hsc;
szUserName[0] = szPassword[0] = 0;
hsc = (Dialog_QueryUserForInfo(fpUI,pvOpaqueOS,szRealm,szUserName,szPassword,MAXFIELD));
if (hsc != HTSPM_STATUS_OK)
return hsc;
#ifdef DEBUG
{
unsigned char buf[4*MAXFIELD];
sprintf(buf,"Received [%s %s %s]",szRealm,szUserName,szPassword);
(*fpUI)(pvOpaqueOS,UI_SERVICE_DEBUG_MESSAGE,buf,NULL);
}
#endif /* DEBUG */
return hsc;
}
/*****************************************************************/
static HTSPMStatusCode module__ListAbilities(F_UserInterface fpUI,
void * pvOpaqueOS,
HTSPM * htspm,
HTHeader * hRequest)
{
/* state our existence. */
HTHeaderList * hlCur;
unsigned char buf[256];
sprintf(buf,EXTENSION_VALUE_SYNTAX,EXTENSION_CATEGORY,EXTENSION_CLASS);
hlCur = HL_AppendNewNameValue(fpUI,pvOpaqueOS,hRequest,EXTENSION_HEADER,buf);
return ( (hlCur != NULL) ? HTSPM_STATUS_OK : HTSPM_ERROR );
}
/*****************************************************************/
static HTSPMStatusCode module__ProcessResponse(F_UserInterface fpUI,
void * pvOpaqueOS,
HTSPM * htspm,
HTHeaderList * hlProtocol,
HTHeader * hRequest,
HTHeader * hResponse,
HTHeader ** phNewRequest,
unsigned int bNonBlock)
{
HTSPMStatusCode htsc;
HTHeaderList * hlCur;
unsigned char * szRealm;
unsigned char * szNonce;
unsigned char * szOpaque;
unsigned char * szOldNonce;
unsigned char szUserName[MAXFIELD];
unsigned char szPassword[MAXFIELD];
char szMsg[256];
unsigned char * szResponse = NULL;
unsigned long bResult;
PWCI * pwci;
/* get important fields from server response */
szRealm = x_GetSubValue(hlProtocol->sub_value,"realm");
szNonce = x_GetSubValue(hlProtocol->sub_value,"nonce");
szOpaque = x_GetSubValue(hlProtocol->sub_value,"opaque");
szOldNonce = x_GetSubValue(hlProtocol->sub_value,"stale");
/* see if we have a cache entry for this destination. */
pwci = pwc_Lookup(fpUI,pvOpaqueOS,htspm->pvOpaque,
hRequest->host,hRequest->uri,
szRealm);
/* see if there is already an Auth header in the request. */
hlCur = HL_FindHeader(hRequest, "Authorization");
/* see if cache is valid and if we can satisfy the request
* without bothering the user.
*/
if (pwci)
{
if (!hlCur)
{
/* we have a cache value and there was no auth in the
* previous request, so we must assume that the cache
* entry is OK. so let's use it.
*/
#ifdef DEBUG
{
unsigned char msg[200];
sprintf(msg,
"%s __ProcessResponse: Using cache value [%s %s %s]",
SCHEME_NAME,szRealm,pwci->szUserName,pwci->szPassword);
(*fpUI)(pvOpaqueOS,UI_SERVICE_DEBUG_MESSAGE,msg,NULL);
}
#endif /* DEBUG */
szResponse = x_ComposeAuthResponse(fpUI,pvOpaqueOS,
pwci->szUserName,
szRealm,
szNonce,
szOpaque,
pwci->szPassword,
hRequest->command,
hRequest->uri);
if (!szResponse)
goto FailError;
/* create new header for the auth */
hlCur = HL_AppendNewNameValue(fpUI,pvOpaqueOS,hRequest,"Authorization",szResponse);
spm_free(fpUI,pvOpaqueOS,szResponse);
szResponse = NULL;
bResult = (hlCur != NULL);
goto Finish;
}
/* we have a cache value, but there was an auth header
* in the request (that bounced). since we're the only
* ones that could have put in an auth header, we assume
* that we did so in a previous call using the current
* cache entry. before we assume that the cached value
* is stale, see if the server rejected it because of an
* old nonce value.
*/
if (szOldNonce && (*szOldNonce) && (strcasecomp(szOldNonce,"true")==0))
{
/* Server indicated that we should try again
* using the new nonce value supplied. (the
* server is *not* asserting that the password
* is valid -- just that it refuses to use old
* nonce values.
*/
#ifdef DEBUG
{
unsigned char msg[200];
sprintf(msg,
"%s __ProcessResponse: Server refused old nonce; retrying with cache value [%s %s %s]",
SCHEME_NAME,szRealm,pwci->szUserName,pwci->szPassword);
(*fpUI)(pvOpaqueOS,UI_SERVICE_DEBUG_MESSAGE,msg,NULL);
}
#endif /* DEBUG */
szResponse = x_ComposeAuthResponse(fpUI,pvOpaqueOS,
pwci->szUserName,
szRealm,
szNonce,
szOpaque,
pwci->szPassword,
hRequest->command,
hRequest->uri);
if (!szResponse)
goto FailError;
/* update existing auth header */
bResult = spm_CloneString(fpUI,pvOpaqueOS,&hlCur->value,szResponse);
spm_free(fpUI,pvOpaqueOS,szResponse);
szResponse = NULL;
goto Finish;
}
/* we must assume that our cache value is stale */
#ifdef DEBUG
{
unsigned char msg[200];
sprintf(msg,
"%s __ProcessResponse: Assuming cache stale [%s %s %s]",
SCHEME_NAME,szRealm,pwci->szUserName,pwci->szPassword);
(*fpUI)(pvOpaqueOS,UI_SERVICE_DEBUG_MESSAGE,msg,NULL);
}
#endif /* DEBUG */
}
/* fall-thru, we must ask user for information */
if (bNonBlock)
return HTSPM_STATUS_WOULD_BLOCK;
htsc = x_QueryUserForInfo(fpUI,pvOpaqueOS,hlProtocol,szRealm,szUserName,szPassword);
if (htsc != HTSPM_STATUS_OK)
return htsc;
/* update the cache */
pwc_Store(fpUI,pvOpaqueOS,htspm->pvOpaque,
hRequest->host,hRequest->uri,
szUserName,szRealm,szNonce,szOpaque,szPassword);
szResponse = x_ComposeAuthResponse(fpUI,pvOpaqueOS,
szUserName,szRealm,
szNonce,szOpaque,
szPassword,
hRequest->command,
hRequest->uri);
if (!szResponse)
goto FailError;
if (hlCur)
{
/* update existing auth header */
bResult = spm_CloneString(fpUI,pvOpaqueOS,&hlCur->value,szResponse);
}
else
{
/* create new header for the auth */
hlCur = HL_AppendNewNameValue(fpUI,pvOpaqueOS,hRequest,"Authorization",szResponse);
bResult = (hlCur != NULL);
}
spm_free(fpUI,pvOpaqueOS,szResponse);
szResponse = NULL;
Finish:
/* For this SPM, we always just update the original header
* and return. Other SPM's may need to create a new one.
*/
if (bResult)
return HTSPM_STATUS_RESUBMIT_OLD;
FailError:
(void)(*fpUI)(pvOpaqueOS,UI_SERVICE_ERROR_MESSAGE,
SEC_formatmsg(RES_STRING_SIMPLE4,szMsg,sizeof(szMsg)),NULL);
return HTSPM_ERROR;
}
/*****************************************************************/
/*****************************************************************/
static HTSPMStatusCode module__PreProcessRequest(F_UserInterface fpUI,
void * pvOpaqueOS,
HTSPM * htspm,
HTHeader * hRequest,
HTHeader ** phNewRequest)
{
/* Take a guess at whether we can prevent a 401
* server response, by pre-loading the request with
* the necessary security information.
*/
HTHeaderList * hlCur;
unsigned char * szResponse = NULL;
unsigned long bResult;
PWCI * pwci;
/* see if we have a cache entry for this destination
* (ignoring the Realm).
*/
pwci = pwc_Lookup(fpUI,pvOpaqueOS,htspm->pvOpaque,
hRequest->host,hRequest->uri,
NULL);
if (!pwci) /* no matching item in the cache, we could */
return HTSPM_ERROR; /* not help, let someone else try. */
/* let's make a guess and update the existing request */
#ifdef DEBUG
{
unsigned char msg[200];
sprintf(msg,
"%s __PreProcessResponse: Using cache value [%s %s %s]",
SCHEME_NAME,pwci->szRealm,pwci->szUserName,pwci->szPassword);
(*fpUI)(pvOpaqueOS,UI_SERVICE_DEBUG_MESSAGE,msg,NULL);
}
#endif /* DEBUG */
szResponse = x_ComposeAuthResponse(fpUI,pvOpaqueOS,
pwci->szUserName,pwci->szRealm,
pwci->szNonce,pwci->szOpaque,pwci->szPassword,
hRequest->command,
hRequest->uri);
if (!szResponse) /* could not allocate memory, give */
return HTSPM_ERROR; /* up and let someone else try. */
/* see if there is already an Auth header in the request. */
hlCur = HL_FindHeader(hRequest, "Authorization");
if (hlCur)
{
/* update existing auth header */
bResult = spm_CloneString(fpUI,pvOpaqueOS,&hlCur->value,szResponse);
}
else
{
/* create new header for the auth */
hlCur = HL_AppendNewNameValue(fpUI,pvOpaqueOS,hRequest,"Authorization",szResponse);
bResult = (hlCur != NULL);
}
spm_free(fpUI,pvOpaqueOS,szResponse);
szResponse = NULL;
/* we updated the original request, so tell the client to send it. */
return HTSPM_STATUS_RESUBMIT_OLD;
}
/*****************************************************************/
/*****************************************************************/
static HTSPMStatusCode module__Unload(F_UserInterface fpUI,
void * pvOpaqueOS,
HTSPM * htspm)
{
pwc_Destroy(fpUI,pvOpaqueOS,htspm->pvOpaque);
htspm->pvOpaque = NULL;
return HTSPM_STATUS_OK;
}
/*****************************************************************/
/*****************************************************************/
static HTSPMStatusCode module__DownCall(HTSPM_ServiceId sid, /* down-call service id */
F_UserInterface fpUI, /* common arg to all down calls */
void * pvOpaqueOS, /* common arg to all down calls */
HTSPM * htspm, /* common arg to all down calls */
void * pvMethodData) /* per-method data */
{
#ifdef DEBUG
{
unsigned char msg[200];
sprintf(msg,"Digest_DownCall: ServiceId [0x%x]\n",sid);
(*fpUI)(pvOpaqueOS,UI_SERVICE_DEBUG_MESSAGE,msg,NULL);
}
#endif /* DEBUG */
switch (sid)
{
case HTSPM_SERVICE_UNLOAD:
{
return module__Unload(fpUI,pvOpaqueOS,htspm);
}
case HTSPM_SERVICE_MENUCOMMAND:
{
/* REMOVE About/Config dialogs
D_MenuCommand * pmc = pvMethodData;
return Dialog_MenuCommand(fpUI,pvOpaqueOS,htspm,
pmc->pszMoreInfo);
*/
return HTSPM_ERROR_UNIMPLEMENTED;
}
case HTSPM_SERVICE_LISTABILITIES:
{
D_ListAbilities * pla = pvMethodData;
return module__ListAbilities(fpUI,pvOpaqueOS,htspm,
pla->hRequest);
}
case HTSPM_SERVICE_PROCESSRESPONSE:
{
D_ProcessResponse * ppr = pvMethodData;
return module__ProcessResponse(fpUI,pvOpaqueOS,htspm,
ppr->hlProtocol,
ppr->hRequest,
ppr->hResponse,
ppr->phNewRequest,
ppr->bNonBlock);
}
case HTSPM_SERVICE_PREPROCESSREQUEST:
{
D_PreProcessRequest * pppr = pvMethodData;
return module__PreProcessRequest(fpUI,pvOpaqueOS,htspm,
pppr->hRequest,
pppr->phNewRequest);
}
default:
return HTSPM_ERROR_UNIMPLEMENTED;
}
/*NOTREACHED*/
}
/*****************************************************************/
/*****************************************************************/
/* WARNING: the name of this function is exported and used in the
* WARNING: mosaic .ini file
*/
#ifdef WIN32
__declspec(dllexport)
#endif
HTSPMStatusCode Digest_Load(F_UserInterface fpUI,
void * pvOpaqueOS,
HTSPM * htspm)
{
if (htspm->ulStructureVersion != HTSPM_STRUCTURE_VERSION)
return HTSPM_ERROR_WRONG_VERSION;
/* we use the opaque field provided to us to store our password cache. */
htspm->pvOpaque = pwc_Create(fpUI,pvOpaqueOS);
htspm->f_downcall = module__DownCall;
SEC_formatmsg(RES_STRING_SIMPLE1,htspm->szMenuName,sizeof(htspm->szMenuName));
/* REMOVE About/Config dialogs
SEC_formatmsg(RES_STRING_SIMPLE2,htspm->szStatusText,sizeof(htspm->szStatusText));
*/
htspm->szStatusText[0] = 0;
{
UI_ProtocolId uid;
uid.htspm = htspm;
uid.szIdentHeader = "WWW-Authenticate"; /* note: no colon */
uid.szIdentValue = SCHEME_NAME;
uid.szIdentSubValue = NULL;
(*fpUI)(pvOpaqueOS,UI_SERVICE_REGISTER_PROTOCOL,&uid,NULL);
}
return HTSPM_STATUS_OK;
}