1 /*
2 * CHAP, MSCHAP
3 *
4 * The client does not authenticate the server, hence no CAI
5 *
6 * Client protocol:
7 * write Chapchal
8 * read response Chapreply or MSchaprely structure
9 *
10 * Server protocol:
11 * read challenge: 8 bytes binary
12 * write user: utf8
13 * write response: Chapreply or MSchapreply structure
14 */
15
16 #include <ctype.h>
17 #include "dat.h"
18
19 enum {
20 ChapChallen = 8,
21 ChapResplen = 16,
22 MSchapResplen = 24,
23 };
24
25 static int dochal(State*);
26 static int doreply(State*, void*, int);
27 static void doLMchap(char *, uchar [ChapChallen], uchar [MSchapResplen]);
28 static void doNTchap(char *, uchar [ChapChallen], uchar [MSchapResplen]);
29 static void dochap(char *, int, char [ChapChallen], uchar [ChapResplen]);
30
31
32 struct State
33 {
34 char *protoname;
35 int astype;
36 int asfd;
37 Key *key;
38 Ticket t;
39 Ticketreq tr;
40 char chal[ChapChallen];
41 MSchapreply mcr;
42 char cr[ChapResplen];
43 char err[ERRMAX];
44 char user[64];
45 uchar secret[16]; /* for mschap */
46 int nsecret;
47 };
48
49 enum
50 {
51 CNeedChal,
52 CHaveResp,
53
54 SHaveChal,
55 SNeedUser,
56 SNeedResp,
57 SHaveZero,
58 SHaveCAI,
59
60 Maxphase
61 };
62
63 static char *phasenames[Maxphase] =
64 {
65 [CNeedChal] "CNeedChal",
66 [CHaveResp] "CHaveResp",
67
68 [SHaveChal] "SHaveChal",
69 [SNeedUser] "SNeedUser",
70 [SNeedResp] "SNeedResp",
71 [SHaveZero] "SHaveZero",
72 [SHaveCAI] "SHaveCAI",
73 };
74
75 static int
chapinit(Proto * p,Fsstate * fss)76 chapinit(Proto *p, Fsstate *fss)
77 {
78 int iscli, ret;
79 State *s;
80
81 if((iscli = isclient(_strfindattr(fss->attr, "role"))) < 0)
82 return failure(fss, nil);
83
84 s = emalloc(sizeof *s);
85 fss->phasename = phasenames;
86 fss->maxphase = Maxphase;
87 s->asfd = -1;
88 if(p == &chap){
89 s->astype = AuthChap;
90 s->protoname = "chap";
91 }else{
92 s->astype = AuthMSchap;
93 s->protoname = "mschap";
94 }
95
96 if(iscli)
97 fss->phase = CNeedChal;
98 else{
99 if((ret = findp9authkey(&s->key, fss)) != RpcOk){
100 free(s);
101 return ret;
102 }
103 if(dochal(s) < 0){
104 free(s);
105 return failure(fss, nil);
106 }
107 fss->phase = SHaveChal;
108 }
109
110 fss->ps = s;
111 return RpcOk;
112 }
113
114 static void
chapclose(Fsstate * fss)115 chapclose(Fsstate *fss)
116 {
117 State *s;
118
119 s = fss->ps;
120 if(s->asfd >= 0){
121 close(s->asfd);
122 s->asfd = -1;
123 }
124 free(s);
125 }
126
127
128 static int
chapwrite(Fsstate * fss,void * va,uint n)129 chapwrite(Fsstate *fss, void *va, uint n)
130 {
131 int ret, nreply;
132 char *a, *v;
133 void *reply;
134 Key *k;
135 Keyinfo ki;
136 State *s;
137 Chapreply cr;
138 MSchapreply mcr;
139 OChapreply ocr;
140 OMSchapreply omcr;
141
142 s = fss->ps;
143 a = va;
144 switch(fss->phase){
145 default:
146 return phaseerror(fss, "write");
147
148 case CNeedChal:
149 ret = findkey(&k, mkkeyinfo(&ki, fss, nil), "%s", fss->proto->keyprompt);
150 if(ret != RpcOk)
151 return ret;
152 v = _strfindattr(k->privattr, "!password");
153 if(v == nil)
154 return failure(fss, "key has no password");
155 setattrs(fss->attr, k->attr);
156 switch(s->astype){
157 default:
158 abort();
159 case AuthMSchap:
160 doLMchap(v, (uchar *)a, (uchar *)s->mcr.LMresp);
161 doNTchap(v, (uchar *)a, (uchar *)s->mcr.NTresp);
162 break;
163 case AuthChap:
164 dochap(v, *a, a+1, (uchar *)s->cr);
165 break;
166 }
167 closekey(k);
168 fss->phase = CHaveResp;
169 return RpcOk;
170
171 case SNeedUser:
172 if(n >= sizeof s->user)
173 return failure(fss, "user name too long");
174 memmove(s->user, va, n);
175 s->user[n] = '\0';
176 fss->phase = SNeedResp;
177 return RpcOk;
178
179 case SNeedResp:
180 switch(s->astype){
181 default:
182 return failure(fss, "chap internal botch");
183 case AuthChap:
184 if(n != sizeof(Chapreply))
185 return failure(fss, "did not get Chapreply");
186 memmove(&cr, va, sizeof cr);
187 ocr.id = cr.id;
188 memmove(ocr.resp, cr.resp, sizeof ocr.resp);
189 memset(omcr.uid, 0, sizeof(omcr.uid));
190 strecpy(ocr.uid, ocr.uid+sizeof ocr.uid, s->user);
191 reply = &ocr;
192 nreply = sizeof ocr;
193 break;
194 case AuthMSchap:
195 if(n != sizeof(MSchapreply))
196 return failure(fss, "did not get MSchapreply");
197 memmove(&mcr, va, sizeof mcr);
198 memmove(omcr.LMresp, mcr.LMresp, sizeof omcr.LMresp);
199 memmove(omcr.NTresp, mcr.NTresp, sizeof omcr.NTresp);
200 memset(omcr.uid, 0, sizeof(omcr.uid));
201 strecpy(omcr.uid, omcr.uid+sizeof omcr.uid, s->user);
202 reply = &omcr;
203 nreply = sizeof omcr;
204 break;
205 }
206 if(doreply(s, reply, nreply) < 0)
207 return failure(fss, nil);
208 fss->phase = Established;
209 fss->ai.cuid = s->t.cuid;
210 fss->ai.suid = s->t.suid;
211 fss->ai.secret = s->secret;
212 fss->ai.nsecret = s->nsecret;
213 fss->haveai = 1;
214 return RpcOk;
215 }
216 }
217
218 static int
chapread(Fsstate * fss,void * va,uint * n)219 chapread(Fsstate *fss, void *va, uint *n)
220 {
221 State *s;
222
223 s = fss->ps;
224 switch(fss->phase){
225 default:
226 return phaseerror(fss, "read");
227
228 case CHaveResp:
229 switch(s->astype){
230 default:
231 phaseerror(fss, "write");
232 break;
233 case AuthMSchap:
234 if(*n > sizeof(MSchapreply))
235 *n = sizeof(MSchapreply);
236 memmove(va, &s->mcr, *n);
237 break;
238 case AuthChap:
239 if(*n > ChapResplen)
240 *n = ChapResplen;
241 memmove(va, s->cr, ChapResplen);
242 break;
243 }
244 fss->phase = Established;
245 fss->haveai = 0;
246 return RpcOk;
247
248 case SHaveChal:
249 if(*n > sizeof s->chal)
250 *n = sizeof s->chal;
251 memmove(va, s->chal, *n);
252 fss->phase = SNeedUser;
253 return RpcOk;
254 }
255 }
256
257 static int
dochal(State * s)258 dochal(State *s)
259 {
260 char *dom, *user;
261 char trbuf[TICKREQLEN];
262
263 s->asfd = -1;
264
265 /* send request to authentication server and get challenge */
266 if((dom = _strfindattr(s->key->attr, "dom")) == nil
267 || (user = _strfindattr(s->key->attr, "user")) == nil){
268 werrstr("chap/dochal cannot happen");
269 goto err;
270 }
271 s->asfd = _authdial(nil, dom);
272 if(s->asfd < 0)
273 goto err;
274
275 memset(&s->tr, 0, sizeof(s->tr));
276 s->tr.type = s->astype;
277 safecpy(s->tr.authdom, dom, sizeof s->tr.authdom);
278 safecpy(s->tr.hostid, user, sizeof(s->tr.hostid));
279 convTR2M(&s->tr, trbuf);
280
281 if(write(s->asfd, trbuf, TICKREQLEN) != TICKREQLEN)
282 goto err;
283
284 /* readn, not _asrdresp. needs to match auth.srv.c. */
285 if(readn(s->asfd, s->chal, sizeof s->chal) != sizeof s->chal)
286 goto err;
287 return 0;
288
289 err:
290 if(s->asfd >= 0)
291 close(s->asfd);
292 s->asfd = -1;
293 return -1;
294 }
295
296 static int
doreply(State * s,void * reply,int nreply)297 doreply(State *s, void *reply, int nreply)
298 {
299 char ticket[TICKETLEN+AUTHENTLEN];
300 int n;
301 Authenticator a;
302
303 if((n=write(s->asfd, reply, nreply)) != nreply){
304 if(n >= 0)
305 werrstr("short write to auth server");
306 goto err;
307 }
308
309 if(_asrdresp(s->asfd, ticket, TICKETLEN+AUTHENTLEN) < 0){
310 /* leave connection open so we can try again */
311 return -1;
312 }
313 s->nsecret = readn(s->asfd, s->secret, sizeof s->secret);
314 if(s->nsecret < 0)
315 s->nsecret = 0;
316 close(s->asfd);
317 s->asfd = -1;
318 convM2T(ticket, &s->t, s->key->priv);
319 if(s->t.num != AuthTs
320 || memcmp(s->t.chal, s->tr.chal, sizeof(s->t.chal)) != 0){
321 if(s->key->successes == 0)
322 disablekey(s->key);
323 werrstr(Easproto);
324 return -1;
325 }
326 s->key->successes++;
327 convM2A(ticket+TICKETLEN, &a, s->t.key);
328 if(a.num != AuthAc
329 || memcmp(a.chal, s->tr.chal, sizeof(a.chal)) != 0
330 || a.id != 0){
331 werrstr(Easproto);
332 return -1;
333 }
334
335 return 0;
336 err:
337 if(s->asfd >= 0)
338 close(s->asfd);
339 s->asfd = -1;
340 return -1;
341 }
342
343 Proto chap = {
344 .name= "chap",
345 .init= chapinit,
346 .write= chapwrite,
347 .read= chapread,
348 .close= chapclose,
349 .addkey= replacekey,
350 .keyprompt= "!password?"
351 };
352
353 Proto mschap = {
354 .name= "mschap",
355 .init= chapinit,
356 .write= chapwrite,
357 .read= chapread,
358 .close= chapclose,
359 .addkey= replacekey,
360 .keyprompt= "!password?"
361 };
362
363 static void
hash(uchar pass[16],uchar c8[ChapChallen],uchar p24[MSchapResplen])364 hash(uchar pass[16], uchar c8[ChapChallen], uchar p24[MSchapResplen])
365 {
366 int i;
367 uchar p21[21];
368 ulong schedule[32];
369
370 memset(p21, 0, sizeof p21 );
371 memmove(p21, pass, 16);
372
373 for(i=0; i<3; i++) {
374 key_setup(p21+i*7, schedule);
375 memmove(p24+i*8, c8, 8);
376 block_cipher(schedule, p24+i*8, 0);
377 }
378 }
379
380 static void
doNTchap(char * pass,uchar chal[ChapChallen],uchar reply[MSchapResplen])381 doNTchap(char *pass, uchar chal[ChapChallen], uchar reply[MSchapResplen])
382 {
383 Rune r;
384 int i, n;
385 uchar digest[MD4dlen];
386 uchar *w, unipass[256];
387
388 // Standard says unlimited length, experience says 128 max
389 if ((n = strlen(pass)) > 128)
390 n = 128;
391
392 for(i=0, w=unipass; i < n; i++) {
393 pass += chartorune(&r, pass);
394 *w++ = r & 0xff;
395 *w++ = r >> 8;
396 }
397
398 memset(digest, 0, sizeof digest);
399 md4(unipass, w-unipass, digest, nil);
400 memset(unipass, 0, sizeof unipass);
401 hash(digest, chal, reply);
402 }
403
404 static void
doLMchap(char * pass,uchar chal[ChapChallen],uchar reply[MSchapResplen])405 doLMchap(char *pass, uchar chal[ChapChallen], uchar reply[MSchapResplen])
406 {
407 int i;
408 ulong schedule[32];
409 uchar p14[15], p16[16];
410 uchar s8[8] = {0x4b, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25};
411 int n = strlen(pass);
412
413 if(n > 14){
414 // let prudent people avoid the LM vulnerability
415 // and protect the loop below from buffer overflow
416 memset(reply, 0, MSchapResplen);
417 return;
418 }
419
420 // Spec says space padded, experience says otherwise
421 memset(p14, 0, sizeof p14 -1);
422 p14[sizeof p14 - 1] = '\0';
423
424 // NT4 requires uppercase, Win XP doesn't care
425 for (i = 0; pass[i]; i++)
426 p14[i] = islower(pass[i])? toupper(pass[i]): pass[i];
427
428 for(i=0; i<2; i++) {
429 key_setup(p14+i*7, schedule);
430 memmove(p16+i*8, s8, 8);
431 block_cipher(schedule, p16+i*8, 0);
432 }
433
434 memset(p14, 0, sizeof p14);
435 hash(p16, chal, reply);
436 }
437
438 static void
dochap(char * pass,int id,char chal[ChapChallen],uchar resp[ChapResplen])439 dochap(char *pass, int id, char chal[ChapChallen], uchar resp[ChapResplen])
440 {
441 char buf[1+ChapChallen+MAXNAMELEN+1];
442 int n = strlen(pass);
443
444 *buf = id;
445 if (n > MAXNAMELEN)
446 n = MAXNAMELEN-1;
447 memset(buf, 0, sizeof buf);
448 strncpy(buf+1, pass, n);
449 memmove(buf+1+n, chal, ChapChallen);
450 md5((uchar*)buf, 1+n+ChapChallen, resp, nil);
451 }
452
453