198 lines
4.0 KiB
C
198 lines
4.0 KiB
C
/*
|
|
* This file is part of the UCB release of Plan 9. It is subject to the license
|
|
* terms in the LICENSE file found in the top-level directory of this
|
|
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
|
|
* part of the UCB release of Plan 9, including this file, may be copied,
|
|
* modified, propagated, or distributed except according to the terms contained
|
|
* in the LICENSE file.
|
|
*/
|
|
/* Portions of this file are Copyright (C) 9front's team.
|
|
* See /doc/license/9front-mit for details about the licensing.
|
|
* See http://code.9front.org/hg/plan9front/ for a list of authors.
|
|
*/
|
|
|
|
/*
|
|
* HTTPDIGEST - MD5 challenge/response authentication (RFC 2617)
|
|
*
|
|
* Client protocol:
|
|
* write challenge: nonce method uri
|
|
* read response: 2*MD5dlen hex digits
|
|
*
|
|
* Server protocol:
|
|
* unimplemented
|
|
*/
|
|
#include "dat.h"
|
|
|
|
enum
|
|
{
|
|
CNeedChal,
|
|
CHaveResp,
|
|
|
|
Maxphase,
|
|
};
|
|
|
|
static char *phasenames[Maxphase] = {
|
|
[CNeedChal] "CNeedChal",
|
|
[CHaveResp] "CHaveResp",
|
|
};
|
|
|
|
struct State
|
|
{
|
|
char resp[MD5dlen*2+1];
|
|
};
|
|
|
|
static int
|
|
hdinit(Proto *p, Fsstate *fss)
|
|
{
|
|
int iscli;
|
|
State *s;
|
|
|
|
if((iscli = isclient(_strfindattr(fss->attr, "role"))) < 0)
|
|
return failure(fss, nil);
|
|
if(!iscli)
|
|
return failure(fss, "%s server not supported", p->name);
|
|
|
|
s = emalloc(sizeof *s);
|
|
fss->phasename = phasenames;
|
|
fss->maxphase = Maxphase;
|
|
fss->phase = CNeedChal;
|
|
fss->ps = s;
|
|
return RpcOk;
|
|
}
|
|
|
|
static void
|
|
strtolower(char *s)
|
|
{
|
|
while(*s){
|
|
*s = tolower(*s);
|
|
s++;
|
|
}
|
|
}
|
|
|
|
static void
|
|
digest(char *user, char *realm, char *passwd,
|
|
char *nonce, char *method, char *uri,
|
|
char *dig)
|
|
{
|
|
uint8_t b[MD5dlen];
|
|
char ha1[MD5dlen*2+1];
|
|
char ha2[MD5dlen*2+1];
|
|
DigestState *s;
|
|
|
|
/*
|
|
* H(A1) = MD5(uid + ":" + realm ":" + passwd)
|
|
*/
|
|
s = md5((uint8_t*)user, strlen(user), nil, nil);
|
|
md5((uint8_t*)":", 1, nil, s);
|
|
md5((uint8_t*)realm, strlen(realm), nil, s);
|
|
md5((uint8_t*)":", 1, nil, s);
|
|
md5((uint8_t*)passwd, strlen(passwd), b, s);
|
|
enc16(ha1, sizeof(ha1), b, MD5dlen);
|
|
strtolower(ha1);
|
|
|
|
/*
|
|
* H(A2) = MD5(method + ":" + uri)
|
|
*/
|
|
s = md5((uint8_t*)method, strlen(method), nil, nil);
|
|
md5((uint8_t*)":", 1, nil, s);
|
|
md5((uint8_t*)uri, strlen(uri), b, s);
|
|
enc16(ha2, sizeof(ha2), b, MD5dlen);
|
|
strtolower(ha2);
|
|
|
|
/*
|
|
* digest = MD5(H(A1) + ":" + nonce + ":" + H(A2))
|
|
*/
|
|
s = md5((uint8_t*)ha1, MD5dlen*2, nil, nil);
|
|
md5((uint8_t*)":", 1, nil, s);
|
|
md5((uint8_t*)nonce, strlen(nonce), nil, s);
|
|
md5((uint8_t*)":", 1, nil, s);
|
|
md5((uint8_t*)ha2, MD5dlen*2, b, s);
|
|
enc16(dig, MD5dlen*2+1, b, MD5dlen);
|
|
strtolower(dig);
|
|
}
|
|
|
|
static int
|
|
hdwrite(Fsstate *fss, void *va, uint32_t n)
|
|
{
|
|
State *s;
|
|
int ret;
|
|
char *a, *p, *r, *u, *t;
|
|
char *tok[4];
|
|
Key *k;
|
|
Keyinfo ki;
|
|
Attr *attr;
|
|
|
|
s = fss->ps;
|
|
a = va;
|
|
|
|
if(fss->phase != CNeedChal)
|
|
return phaseerror(fss, "write");
|
|
|
|
attr = _delattr(_copyattr(fss->attr), "role");
|
|
mkkeyinfo(&ki, fss, attr);
|
|
ret = findkey(&k, &ki, "%s", fss->proto->keyprompt);
|
|
_freeattr(attr);
|
|
if(ret != RpcOk)
|
|
return ret;
|
|
p = _strfindattr(k->privattr, "!password");
|
|
if(p == nil)
|
|
return failure(fss, "key has no password");
|
|
r = _strfindattr(k->attr, "realm");
|
|
if(r == nil)
|
|
return failure(fss, "key has no realm");
|
|
u = _strfindattr(k->attr, "user");
|
|
if(u == nil)
|
|
return failure(fss, "key has no user");
|
|
setattrs(fss->attr, k->attr);
|
|
|
|
/* copy in case a is not null-terminated */
|
|
t = emalloc(n+1);
|
|
memcpy(t, a, n);
|
|
t[n] = 0;
|
|
|
|
/* get nonce, method, uri */
|
|
if(tokenize(t, tok, 4) != 3)
|
|
return failure(fss, "bad challenge");
|
|
|
|
digest(u, r, p, tok[0], tok[1], tok[2], s->resp);
|
|
|
|
free(t);
|
|
closekey(k);
|
|
fss->phase = CHaveResp;
|
|
return RpcOk;
|
|
}
|
|
|
|
static int
|
|
hdread(Fsstate *fss, void *va, uint32_t *n)
|
|
{
|
|
State *s;
|
|
|
|
s = fss->ps;
|
|
if(fss->phase != CHaveResp)
|
|
return phaseerror(fss, "read");
|
|
if(*n > strlen(s->resp))
|
|
*n = strlen(s->resp);
|
|
memmove(va, s->resp, *n);
|
|
fss->phase = Established;
|
|
fss->haveai = 0;
|
|
return RpcOk;
|
|
}
|
|
|
|
static void
|
|
hdclose(Fsstate *fss)
|
|
{
|
|
State *s;
|
|
s = fss->ps;
|
|
free(s);
|
|
}
|
|
|
|
Proto httpdigest = {
|
|
.name= "httpdigest",
|
|
.init= hdinit,
|
|
.write= hdwrite,
|
|
.read= hdread,
|
|
.close= hdclose,
|
|
.addkey= replacekey,
|
|
.keyprompt= "user? realm? !password?"
|
|
};
|