chap.c 9.3 KB

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