xref: /plan9/sys/src/cmd/auth/factotum/httpdigest.c (revision 5ab4dd4c205ee4af2f09166e9f81c4aec6598a6e)
1 /*
2  * HTTPDIGEST - MD5 challenge/response authentication (RFC 2617)
3  *
4  * Client protocol:
5  *	write challenge: nonce method uri
6  *	read response: 2*MD5dlen hex digits
7  *
8  * Server protocol:
9  *	unimplemented
10  */
11 #include "dat.h"
12 
13 enum
14 {
15 	CNeedChal,
16 	CHaveResp,
17 
18 	Maxphase,
19 };
20 
21 static char *phasenames[Maxphase] = {
22 [CNeedChal]	"CNeedChal",
23 [CHaveResp]	"CHaveResp",
24 };
25 
26 struct State
27 {
28 	char resp[MD5dlen*2+1];
29 };
30 
31 static int
hdinit(Proto * p,Fsstate * fss)32 hdinit(Proto *p, Fsstate *fss)
33 {
34 	int iscli;
35 	State *s;
36 
37 	if((iscli = isclient(_strfindattr(fss->attr, "role"))) < 0)
38 		return failure(fss, nil);
39 	if(!iscli)
40 		return failure(fss, "%s server not supported", p->name);
41 
42 	s = emalloc(sizeof *s);
43 	fss->phasename = phasenames;
44 	fss->maxphase = Maxphase;
45 	fss->phase = CNeedChal;
46 	fss->ps = s;
47 	return RpcOk;
48 }
49 
50 static void
strtolower(char * s)51 strtolower(char *s)
52 {
53 	while(*s){
54 		*s = tolower(*s);
55 		s++;
56 	}
57 }
58 
59 static void
digest(char * user,char * realm,char * passwd,char * nonce,char * method,char * uri,char * dig)60 digest(char *user, char *realm, char *passwd,
61 	char *nonce, char *method, char *uri,
62 	char *dig)
63 {
64 	uchar b[MD5dlen];
65 	char ha1[MD5dlen*2+1];
66 	char ha2[MD5dlen*2+1];
67 	DigestState *s;
68 
69 	/*
70 	 *  H(A1) = MD5(uid + ":" + realm ":" + passwd)
71 	 */
72 	s = md5((uchar*)user, strlen(user), nil, nil);
73 	md5((uchar*)":", 1, nil, s);
74 	md5((uchar*)realm, strlen(realm), nil, s);
75 	md5((uchar*)":", 1, nil, s);
76 	md5((uchar*)passwd, strlen(passwd), b, s);
77 	enc16(ha1, sizeof(ha1), b, MD5dlen);
78 	strtolower(ha1);
79 
80 	/*
81 	 *  H(A2) = MD5(method + ":" + uri)
82 	 */
83 	s = md5((uchar*)method, strlen(method), nil, nil);
84 	md5((uchar*)":", 1, nil, s);
85 	md5((uchar*)uri, strlen(uri), b, s);
86 	enc16(ha2, sizeof(ha2), b, MD5dlen);
87 	strtolower(ha2);
88 
89 	/*
90 	 *  digest = MD5(H(A1) + ":" + nonce + ":" + H(A2))
91 	 */
92 	s = md5((uchar*)ha1, MD5dlen*2, nil, nil);
93 	md5((uchar*)":", 1, nil, s);
94 	md5((uchar*)nonce, strlen(nonce), nil, s);
95 	md5((uchar*)":", 1, nil, s);
96 	md5((uchar*)ha2, MD5dlen*2, b, s);
97 	enc16(dig, MD5dlen*2+1, b, MD5dlen);
98 	strtolower(dig);
99 }
100 
101 static int
hdwrite(Fsstate * fss,void * va,uint n)102 hdwrite(Fsstate *fss, void *va, uint n)
103 {
104 	State *s;
105 	int ret;
106 	char *a, *p, *r, *u, *t;
107 	char *tok[4];
108 	Key *k;
109 	Keyinfo ki;
110 	Attr *attr;
111 
112 	s = fss->ps;
113 	a = va;
114 
115 	if(fss->phase != CNeedChal)
116 		return phaseerror(fss, "write");
117 
118 	attr = _delattr(_copyattr(fss->attr), "role");
119 	mkkeyinfo(&ki, fss, attr);
120 	ret = findkey(&k, &ki, "%s", fss->proto->keyprompt);
121 	_freeattr(attr);
122 	if(ret != RpcOk)
123 		return ret;
124 	p = _strfindattr(k->privattr, "!password");
125 	if(p == nil)
126 		return failure(fss, "key has no password");
127 	r = _strfindattr(k->attr, "realm");
128 	if(r == nil)
129 		return failure(fss, "key has no realm");
130 	u = _strfindattr(k->attr, "user");
131 	if(u == nil)
132 		return failure(fss, "key has no user");
133 	setattrs(fss->attr, k->attr);
134 
135 	/* copy in case a is not null-terminated */
136 	t = emalloc(n+1);
137 	memcpy(t, a, n);
138 	t[n] = 0;
139 
140 	/* get nonce, method, uri */
141 	if(tokenize(t, tok, 4) != 3)
142 		return failure(fss, "bad challenge");
143 
144 	digest(u, r, p, tok[0], tok[1], tok[2], s->resp);
145 
146 	free(t);
147 	closekey(k);
148 	fss->phase = CHaveResp;
149 	return RpcOk;
150 }
151 
152 static int
hdread(Fsstate * fss,void * va,uint * n)153 hdread(Fsstate *fss, void *va, uint *n)
154 {
155 	State *s;
156 
157 	s = fss->ps;
158 	if(fss->phase != CHaveResp)
159 		return phaseerror(fss, "read");
160 	if(*n > strlen(s->resp))
161 		*n = strlen(s->resp);
162 	memmove(va, s->resp, *n);
163 	fss->phase = Established;
164 	fss->haveai = 0;
165 	return RpcOk;
166 }
167 
168 static void
hdclose(Fsstate * fss)169 hdclose(Fsstate *fss)
170 {
171 	State *s;
172 	s = fss->ps;
173 	free(s);
174 }
175 
176 Proto httpdigest = {
177 .name=		"httpdigest",
178 .init=		hdinit,
179 .write=		hdwrite,
180 .read=		hdread,
181 .close=		hdclose,
182 .addkey=	replacekey,
183 .keyprompt=	"user? realm? !password?"
184 };
185