authsrv.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902
  1. #include <u.h>
  2. #include <libc.h>
  3. #include <bio.h>
  4. #include <ndb.h>
  5. #include <regexp.h>
  6. #include <mp.h>
  7. #include <libsec.h>
  8. #include <authsrv.h>
  9. #include "authcmdlib.h"
  10. int debug;
  11. Ndb *db;
  12. char raddr[128];
  13. /* Microsoft auth constants */
  14. enum {
  15. MShashlen = 16,
  16. MSchallen = 8,
  17. MSresplen = 24,
  18. };
  19. int ticketrequest(Ticketreq*);
  20. void challengebox(Ticketreq*);
  21. void changepasswd(Ticketreq*);
  22. void apop(Ticketreq*, int);
  23. void chap(Ticketreq*);
  24. void mschap(Ticketreq*);
  25. void http(Ticketreq*);
  26. void vnc(Ticketreq*);
  27. int speaksfor(char*, char*);
  28. void replyerror(char*, ...);
  29. void getraddr(char*);
  30. void mkkey(char*);
  31. void randombytes(uchar*, int);
  32. void nthash(uchar hash[MShashlen], char *passwd);
  33. void lmhash(uchar hash[MShashlen], char *passwd);
  34. void mschalresp(uchar resp[MSresplen], uchar hash[MShashlen], uchar chal[MSchallen]);
  35. void desencrypt(uchar data[8], uchar key[7]);
  36. int tickauthreply(Ticketreq*, char*);
  37. void safecpy(char*, char*, int);
  38. void
  39. main(int argc, char *argv[])
  40. {
  41. char buf[TICKREQLEN];
  42. Ticketreq tr;
  43. ARGBEGIN{
  44. case 'd':
  45. debug++;
  46. }ARGEND
  47. strcpy(raddr, "unknown");
  48. if(argc >= 1)
  49. getraddr(argv[argc-1]);
  50. alarm(10*60*1000); /* kill a connection after 10 minutes */
  51. db = ndbopen("/lib/ndb/auth");
  52. if(db == 0)
  53. syslog(0, AUTHLOG, "no /lib/ndb/auth");
  54. srand(time(0)*getpid());
  55. for(;;){
  56. if(readn(0, buf, TICKREQLEN) <= 0)
  57. exits(0);
  58. convM2TR(buf, &tr);
  59. switch(buf[0]){
  60. case AuthTreq:
  61. ticketrequest(&tr);
  62. break;
  63. case AuthChal:
  64. challengebox(&tr);
  65. break;
  66. case AuthPass:
  67. changepasswd(&tr);
  68. break;
  69. case AuthApop:
  70. apop(&tr, AuthApop);
  71. break;
  72. case AuthChap:
  73. chap(&tr);
  74. break;
  75. case AuthMSchap:
  76. mschap(&tr);
  77. break;
  78. case AuthCram:
  79. apop(&tr, AuthCram);
  80. break;
  81. case AuthHttp:
  82. http(&tr);
  83. break;
  84. case AuthVNC:
  85. vnc(&tr);
  86. break;
  87. default:
  88. syslog(0, AUTHLOG, "unknown ticket request type: %d", buf[0]);
  89. exits(0);
  90. }
  91. }
  92. exits(0);
  93. }
  94. int
  95. ticketrequest(Ticketreq *tr)
  96. {
  97. char akey[DESKEYLEN];
  98. char hkey[DESKEYLEN];
  99. Ticket t;
  100. char tbuf[2*TICKETLEN+1];
  101. if(findkey(KEYDB, tr->authid, akey) == 0){
  102. /* make one up so caller doesn't know it was wrong */
  103. mkkey(akey);
  104. if(debug)
  105. syslog(0, AUTHLOG, "tr-fail authid %s", raddr);
  106. }
  107. if(findkey(KEYDB, tr->hostid, hkey) == 0){
  108. /* make one up so caller doesn't know it was wrong */
  109. mkkey(hkey);
  110. if(debug)
  111. syslog(0, AUTHLOG, "tr-fail hostid %s(%s)", tr->hostid, raddr);
  112. }
  113. memset(&t, 0, sizeof(t));
  114. memmove(t.chal, tr->chal, CHALLEN);
  115. strcpy(t.cuid, tr->uid);
  116. if(speaksfor(tr->hostid, tr->uid))
  117. strcpy(t.suid, tr->uid);
  118. else {
  119. mkkey(akey);
  120. mkkey(hkey);
  121. if(debug)
  122. syslog(0, AUTHLOG, "tr-fail %s@%s(%s) -> %s@%s no speaks for",
  123. tr->uid, tr->hostid, raddr, tr->uid, tr->authid);
  124. }
  125. mkkey(t.key);
  126. tbuf[0] = AuthOK;
  127. t.num = AuthTc;
  128. convT2M(&t, tbuf+1, hkey);
  129. t.num = AuthTs;
  130. convT2M(&t, tbuf+1+TICKETLEN, akey);
  131. if(write(1, tbuf, 2*TICKETLEN+1) < 0){
  132. if(debug)
  133. syslog(0, AUTHLOG, "tr-fail %s@%s(%s): hangup",
  134. tr->uid, tr->hostid, raddr);
  135. exits(0);
  136. }
  137. if(debug)
  138. syslog(0, AUTHLOG, "tr-ok %s@%s(%s) -> %s@%s",
  139. tr->uid, tr->hostid, raddr, tr->uid, tr->authid);
  140. return 0;
  141. }
  142. void
  143. challengebox(Ticketreq *tr)
  144. {
  145. long chal;
  146. char *key, *netkey;
  147. char kbuf[DESKEYLEN], nkbuf[DESKEYLEN], hkey[DESKEYLEN];
  148. char buf[NETCHLEN+1];
  149. char *err;
  150. key = findkey(KEYDB, tr->uid, kbuf);
  151. netkey = findkey(NETKEYDB, tr->uid, nkbuf);
  152. if(key == 0 && netkey == 0){
  153. /* make one up so caller doesn't know it was wrong */
  154. mkkey(nkbuf);
  155. netkey = nkbuf;
  156. if(debug)
  157. syslog(0, AUTHLOG, "cr-fail uid %s@%s", tr->uid, raddr);
  158. }
  159. if(findkey(KEYDB, tr->hostid, hkey) == 0){
  160. /* make one up so caller doesn't know it was wrong */
  161. mkkey(hkey);
  162. if(debug)
  163. syslog(0, AUTHLOG, "cr-fail hostid %s %s@%s", tr->hostid,
  164. tr->uid, raddr);
  165. }
  166. /*
  167. * challenge-response
  168. */
  169. memset(buf, 0, sizeof(buf));
  170. buf[0] = AuthOK;
  171. chal = lnrand(MAXNETCHAL);
  172. sprint(buf+1, "%lud", chal);
  173. if(write(1, buf, NETCHLEN+1) < 0)
  174. exits(0);
  175. if(readn(0, buf, NETCHLEN) < 0)
  176. exits(0);
  177. if(!(key && netcheck(key, chal, buf))
  178. && !(netkey && netcheck(netkey, chal, buf))
  179. && (err = secureidcheck(tr->uid, buf)) != nil){
  180. replyerror("cr-fail %s %s %s", err, tr->uid, raddr);
  181. logfail(tr->uid);
  182. if(debug)
  183. syslog(0, AUTHLOG, "cr-fail %s@%s(%s): bad resp",
  184. tr->uid, tr->hostid, raddr);
  185. return;
  186. }
  187. succeed(tr->uid);
  188. /*
  189. * reply with ticket & authenticator
  190. */
  191. if(tickauthreply(tr, hkey) < 0){
  192. if(debug)
  193. syslog(0, AUTHLOG, "cr-fail %s@%s(%s): hangup",
  194. tr->uid, tr->hostid, raddr);
  195. exits(0);
  196. }
  197. if(debug)
  198. syslog(0, AUTHLOG, "cr-ok %s@%s(%s)",
  199. tr->uid, tr->hostid, raddr);
  200. }
  201. void
  202. changepasswd(Ticketreq *tr)
  203. {
  204. Ticket t;
  205. char tbuf[TICKETLEN+1];
  206. char prbuf[PASSREQLEN];
  207. Passwordreq pr;
  208. char okey[DESKEYLEN], nkey[DESKEYLEN];
  209. char *err;
  210. if(findkey(KEYDB, tr->uid, okey) == 0){
  211. /* make one up so caller doesn't know it was wrong */
  212. mkkey(okey);
  213. syslog(0, AUTHLOG, "cp-fail uid %s", raddr);
  214. }
  215. /* send back a ticket with a new key */
  216. memmove(t.chal, tr->chal, CHALLEN);
  217. mkkey(t.key);
  218. tbuf[0] = AuthOK;
  219. t.num = AuthTp;
  220. safecpy(t.cuid, tr->uid, sizeof(t.cuid));
  221. safecpy(t.suid, tr->uid, sizeof(t.suid));
  222. convT2M(&t, tbuf+1, okey);
  223. write(1, tbuf, sizeof(tbuf));
  224. /* loop trying passwords out */
  225. for(;;){
  226. if(readn(0, prbuf, PASSREQLEN) < 0)
  227. exits(0);
  228. convM2PR(prbuf, &pr, t.key);
  229. if(pr.num != AuthPass){
  230. replyerror("protocol botch1: %s", raddr);
  231. exits(0);
  232. }
  233. passtokey(nkey, pr.old);
  234. if(memcmp(nkey, okey, DESKEYLEN)){
  235. replyerror("protocol botch2: %s", raddr);
  236. continue;
  237. }
  238. if(*pr.new){
  239. err = okpasswd(pr.new);
  240. if(err){
  241. replyerror("%s %s", err, raddr);
  242. continue;
  243. }
  244. passtokey(nkey, pr.new);
  245. }
  246. if(pr.changesecret && setsecret(KEYDB, tr->uid, pr.secret) == 0){
  247. replyerror("can't write secret %s", raddr);
  248. continue;
  249. }
  250. if(*pr.new && setkey(KEYDB, tr->uid, nkey) == 0){
  251. replyerror("can't write key %s", raddr);
  252. continue;
  253. }
  254. break;
  255. }
  256. prbuf[0] = AuthOK;
  257. write(1, prbuf, 1);
  258. succeed(tr->uid);
  259. return;
  260. }
  261. void
  262. http(Ticketreq *tr)
  263. {
  264. Ticket t;
  265. char tbuf[TICKETLEN+1];
  266. char key[DESKEYLEN];
  267. char *p;
  268. Biobuf *b;
  269. int n;
  270. n = strlen(tr->uid);
  271. b = Bopen("/sys/lib/httppasswords", OREAD);
  272. if(b == nil){
  273. replyerror("no password file", raddr);
  274. return;
  275. }
  276. /* find key */
  277. for(;;){
  278. p = Brdline(b, '\n');
  279. if(p == nil)
  280. break;
  281. p[Blinelen(b)-1] = 0;
  282. if(strncmp(p, tr->uid, n) == 0)
  283. if(p[n] == ' ' || p[n] == '\t'){
  284. p += n;
  285. break;
  286. }
  287. }
  288. Bterm(b);
  289. if(p == nil) {
  290. randombytes((uchar*)key, DESKEYLEN);
  291. } else {
  292. while(*p == ' ' || *p == '\t')
  293. p++;
  294. passtokey(key, p);
  295. }
  296. /* send back a ticket encrypted with the key */
  297. randombytes((uchar*)t.chal, CHALLEN);
  298. mkkey(t.key);
  299. tbuf[0] = AuthOK;
  300. t.num = AuthHr;
  301. safecpy(t.cuid, tr->uid, sizeof(t.cuid));
  302. safecpy(t.suid, tr->uid, sizeof(t.suid));
  303. convT2M(&t, tbuf+1, key);
  304. write(1, tbuf, sizeof(tbuf));
  305. }
  306. static char*
  307. domainname(void)
  308. {
  309. static char sysname[Maxpath];
  310. static char domain[Ndbvlen];
  311. Ndbtuple *t;
  312. int n;
  313. if(*domain)
  314. return domain;
  315. if(*sysname)
  316. return sysname;
  317. n = readfile("/dev/sysname", sysname, sizeof(sysname)-1);
  318. if(n < 0){
  319. strcpy(sysname, "kremvax");
  320. return sysname;
  321. }
  322. sysname[n] = 0;
  323. t = csgetval(0, "sys", sysname, "dom", domain);
  324. if(t == 0)
  325. return sysname;
  326. ndbfree(t);
  327. return domain;
  328. }
  329. static int
  330. h2b(char c)
  331. {
  332. if(c >= '0' && c <= '9')
  333. return c - '0';
  334. if(c >= 'A' && c <= 'F')
  335. return c - 'A' + 10;
  336. if(c >= 'a' && c <= 'f')
  337. return c - 'a' + 10;
  338. return 0;
  339. }
  340. void
  341. apop(Ticketreq *tr, int type)
  342. {
  343. int challen, i, tries;
  344. char *secret, *hkey, *p;
  345. Ticketreq treq;
  346. DigestState *s;
  347. char sbuf[SECRETLEN], hbuf[DESKEYLEN];
  348. char tbuf[TICKREQLEN];
  349. char buf[MD5dlen*2];
  350. uchar digest[MD5dlen], resp[MD5dlen];
  351. ulong rb[4];
  352. char chal[256];
  353. USED(tr);
  354. /*
  355. * Create a challenge and send it.
  356. */
  357. randombytes((uchar*)rb, sizeof(rb));
  358. p = chal;
  359. p += snprint(p, sizeof(chal), "<%lux%lux.%lux%lux@%s>",
  360. rb[0], rb[1], rb[2], rb[3], domainname());
  361. challen = p - chal;
  362. print("%c%-5d%s", AuthOKvar, challen, chal);
  363. /* give user a few attempts */
  364. for(tries = 0; ; tries++) {
  365. /*
  366. * get ticket request
  367. */
  368. if(readn(0, tbuf, TICKREQLEN) < 0)
  369. exits(0);
  370. convM2TR(tbuf, &treq);
  371. tr = &treq;
  372. if(tr->type != type)
  373. exits(0);
  374. /*
  375. * read response
  376. */
  377. if(readn(0, buf, MD5dlen*2) < 0)
  378. exits(0);
  379. for(i = 0; i < MD5dlen; i++)
  380. resp[i] = (h2b(buf[2*i])<<4)|h2b(buf[2*i+1]);
  381. /*
  382. * lookup
  383. */
  384. secret = findsecret(KEYDB, tr->uid, sbuf);
  385. hkey = findkey(KEYDB, tr->hostid, hbuf);
  386. if(hkey == 0 || secret == 0){
  387. replyerror("apop-fail bad response %s", raddr);
  388. logfail(tr->uid);
  389. if(tries > 5)
  390. return;
  391. continue;
  392. }
  393. /*
  394. * check for match
  395. */
  396. if(type == AuthCram){
  397. hmac_md5((uchar*)chal, challen,
  398. (uchar*)secret, strlen(secret),
  399. digest, nil);
  400. } else {
  401. s = md5((uchar*)chal, challen, 0, 0);
  402. md5((uchar*)secret, strlen(secret), digest, s);
  403. }
  404. if(memcmp(digest, resp, MD5dlen) != 0){
  405. replyerror("apop-fail bad response %s", raddr);
  406. logfail(tr->uid);
  407. if(tries > 5)
  408. return;
  409. continue;
  410. }
  411. break;
  412. }
  413. succeed(tr->uid);
  414. /*
  415. * reply with ticket & authenticator
  416. */
  417. if(tickauthreply(tr, hkey) < 0)
  418. exits(0);
  419. if(debug){
  420. if(type == AuthCram)
  421. syslog(0, AUTHLOG, "cram-ok %s %s", tr->uid, raddr);
  422. else
  423. syslog(0, AUTHLOG, "apop-ok %s %s", tr->uid, raddr);
  424. }
  425. }
  426. enum {
  427. VNCchallen= 16,
  428. };
  429. /* VNC reverses the bits of each byte before using as a des key */
  430. uchar swizzletab[256] = {
  431. 0x0, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
  432. 0x8, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
  433. 0x4, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
  434. 0xc, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
  435. 0x2, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
  436. 0xa, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
  437. 0x6, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
  438. 0xe, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
  439. 0x1, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
  440. 0x9, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
  441. 0x5, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
  442. 0xd, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
  443. 0x3, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
  444. 0xb, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
  445. 0x7, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
  446. 0xf, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff,
  447. };
  448. void
  449. vnc(Ticketreq *tr)
  450. {
  451. uchar chal[VNCchallen+6];
  452. uchar reply[VNCchallen];
  453. char *secret, *hkey;
  454. char sbuf[SECRETLEN], hbuf[DESKEYLEN];
  455. DESstate s;
  456. int i;
  457. /*
  458. * Create a challenge and send it.
  459. */
  460. randombytes(chal+6, VNCchallen);
  461. chal[0] = AuthOKvar;
  462. sprint((char*)chal+1, "%-5d", VNCchallen);
  463. if(write(1, chal, sizeof(chal)) != sizeof(chal))
  464. return;
  465. /*
  466. * lookup keys (and swizzle bits)
  467. */
  468. memset(sbuf, 0, sizeof(sbuf));
  469. secret = findsecret(KEYDB, tr->uid, sbuf);
  470. if(secret == 0){
  471. randombytes((uchar*)sbuf, sizeof(sbuf));
  472. secret = sbuf;
  473. }
  474. for(i = 0; i < 8; i++)
  475. secret[i] = swizzletab[(uchar)secret[i]];
  476. hkey = findkey(KEYDB, tr->hostid, hbuf);
  477. if(hkey == 0){
  478. randombytes((uchar*)hbuf, sizeof(hbuf));
  479. hkey = hbuf;
  480. }
  481. /*
  482. * get response
  483. */
  484. if(readn(0, reply, sizeof(reply)) != sizeof(reply))
  485. return;
  486. /*
  487. * decrypt response and compare
  488. */
  489. setupDESstate(&s, (uchar*)secret, nil);
  490. desECBdecrypt(reply, sizeof(reply), &s);
  491. if(memcmp(reply, chal+6, VNCchallen) != 0){
  492. replyerror("vnc-fail bad response %s", raddr);
  493. logfail(tr->uid);
  494. return;
  495. }
  496. succeed(tr->uid);
  497. /*
  498. * reply with ticket & authenticator
  499. */
  500. if(tickauthreply(tr, hkey) < 0)
  501. exits(0);
  502. if(debug)
  503. syslog(0, AUTHLOG, "vnc-ok %s %s", tr->uid, raddr);
  504. }
  505. void
  506. chap(Ticketreq *tr)
  507. {
  508. char *secret, *hkey;
  509. DigestState *s;
  510. char sbuf[SECRETLEN], hbuf[DESKEYLEN];
  511. uchar digest[MD5dlen];
  512. char chal[CHALLEN];
  513. OChapreply reply;
  514. /*
  515. * Create a challenge and send it.
  516. */
  517. randombytes((uchar*)chal, sizeof(chal));
  518. write(1, chal, sizeof(chal));
  519. /*
  520. * get chap reply
  521. */
  522. if(readn(0, &reply, sizeof(reply)) < 0)
  523. exits(0);
  524. safecpy(tr->uid, reply.uid, sizeof(tr->uid));
  525. /*
  526. * lookup
  527. */
  528. secret = findsecret(KEYDB, tr->uid, sbuf);
  529. hkey = findkey(KEYDB, tr->hostid, hbuf);
  530. if(hkey == 0 || secret == 0){
  531. replyerror("chap-fail bad response %s", raddr);
  532. logfail(tr->uid);
  533. exits(0);
  534. }
  535. /*
  536. * check for match
  537. */
  538. s = md5(&reply.id, 1, 0, 0);
  539. md5((uchar*)secret, strlen(secret), 0, s);
  540. md5((uchar*)chal, sizeof(chal), digest, s);
  541. if(memcmp(digest, reply.resp, MD5dlen) != 0){
  542. replyerror("chap-fail bad response %s", raddr);
  543. logfail(tr->uid);
  544. exits(0);
  545. }
  546. succeed(tr->uid);
  547. /*
  548. * reply with ticket & authenticator
  549. */
  550. if(tickauthreply(tr, hkey) < 0)
  551. exits(0);
  552. if(debug)
  553. syslog(0, AUTHLOG, "chap-ok %s %s", tr->uid, raddr);
  554. }
  555. void
  556. printresp(uchar resp[MSresplen])
  557. {
  558. char buf[200], *p;
  559. int i;
  560. p = buf;
  561. for(i=0; i<MSresplen; i++)
  562. p += sprint(p, "%.2ux ", resp[i]);
  563. syslog(0, AUTHLOG, "resp = %s", buf);
  564. }
  565. void
  566. mschap(Ticketreq *tr)
  567. {
  568. char *secret, *hkey;
  569. char sbuf[SECRETLEN], hbuf[DESKEYLEN];
  570. uchar chal[CHALLEN];
  571. uchar hash[MShashlen];
  572. uchar hash2[MShashlen];
  573. uchar resp[MSresplen];
  574. OMSchapreply reply;
  575. int lmok, ntok;
  576. DigestState *s;
  577. uchar digest[SHA1dlen];
  578. /*
  579. * Create a challenge and send it.
  580. */
  581. randombytes((uchar*)chal, sizeof(chal));
  582. write(1, chal, sizeof(chal));
  583. /*
  584. * get chap reply
  585. */
  586. if(readn(0, &reply, sizeof(reply)) < 0)
  587. exits(0);
  588. safecpy(tr->uid, reply.uid, sizeof(tr->uid));
  589. /*
  590. * lookup
  591. */
  592. secret = findsecret(KEYDB, tr->uid, sbuf);
  593. hkey = findkey(KEYDB, tr->hostid, hbuf);
  594. if(hkey == 0 || secret == 0){
  595. replyerror("mschap-fail bad response %s", raddr);
  596. logfail(tr->uid);
  597. exits(0);
  598. }
  599. /*
  600. * check for match on LM algorithm
  601. */
  602. lmhash(hash, secret);
  603. mschalresp(resp, hash, chal);
  604. lmok = memcmp(resp, reply.LMresp, MSresplen) == 0;
  605. nthash(hash, secret);
  606. mschalresp(resp, hash, chal);
  607. ntok = memcmp(resp, reply.NTresp, MSresplen) == 0;
  608. if(!ntok){
  609. replyerror("mschap-fail bad response %s %ux", raddr, (lmok<<1)|ntok);
  610. logfail(tr->uid);
  611. exits(0);
  612. }
  613. succeed(tr->uid);
  614. /*
  615. * reply with ticket & authenticator
  616. */
  617. if(tickauthreply(tr, hkey) < 0)
  618. exits(0);
  619. if(debug)
  620. syslog(0, AUTHLOG, "mschap-ok %s %s %ux", tr->uid, raddr, (lmok<<1)|ntok);
  621. nthash(hash, secret);
  622. md4(hash, 16, hash2, 0);
  623. s = sha1(hash2, 16, 0, 0);
  624. sha1(hash2, 16, 0, s);
  625. sha1(chal, 8, digest, s);
  626. if(write(1, digest, 16) < 0)
  627. exits(0);
  628. }
  629. void
  630. nthash(uchar hash[MShashlen], char *passwd)
  631. {
  632. uchar buf[512];
  633. int i;
  634. for (i = 0; *passwd && i + 1 < sizeof(buf);) {
  635. Rune r;
  636. passwd += chartorune(&r, passwd);
  637. buf[i++] = r;
  638. buf[i++] = r >> 8;
  639. }
  640. memset(hash, 0, 16);
  641. md4(buf, i, hash, 0);
  642. }
  643. void
  644. lmhash(uchar hash[MShashlen], char *passwd)
  645. {
  646. uchar buf[14];
  647. char *stdtext = "KGS!@#$%";
  648. int i;
  649. strncpy((char*)buf, passwd, sizeof(buf));
  650. for(i=0; i<sizeof(buf); i++)
  651. if(buf[i] >= 'a' && buf[i] <= 'z')
  652. buf[i] += 'A' - 'a';
  653. memset(hash, 0, 16);
  654. memcpy(hash, stdtext, 8);
  655. memcpy(hash+8, stdtext, 8);
  656. desencrypt(hash, buf);
  657. desencrypt(hash+8, buf+7);
  658. }
  659. void
  660. mschalresp(uchar resp[MSresplen], uchar hash[MShashlen], uchar chal[MSchallen])
  661. {
  662. int i;
  663. uchar buf[21];
  664. memset(buf, 0, sizeof(buf));
  665. memcpy(buf, hash, MShashlen);
  666. for(i=0; i<3; i++) {
  667. memmove(resp+i*MSchallen, chal, MSchallen);
  668. desencrypt(resp+i*MSchallen, buf+i*7);
  669. }
  670. }
  671. void
  672. desencrypt(uchar data[8], uchar key[7])
  673. {
  674. ulong ekey[32];
  675. key_setup(key, ekey);
  676. block_cipher(ekey, data, 0);
  677. }
  678. /*
  679. * return true of the speaker may speak for the user
  680. *
  681. * a speaker may always speak for himself/herself
  682. */
  683. int
  684. speaksfor(char *speaker, char *user)
  685. {
  686. Ndbtuple *tp, *ntp;
  687. Ndbs s;
  688. int ok;
  689. char notuser[Maxpath];
  690. if(strcmp(speaker, user) == 0)
  691. return 1;
  692. if(db == 0)
  693. return 0;
  694. tp = ndbsearch(db, &s, "hostid", speaker);
  695. if(tp == 0)
  696. return 0;
  697. ok = 0;
  698. snprint(notuser, sizeof notuser, "!%s", user);
  699. for(ntp = tp; ntp; ntp = ntp->entry)
  700. if(strcmp(ntp->attr, "uid") == 0){
  701. if(strcmp(ntp->val, notuser) == 0)
  702. break;
  703. if(*ntp->val == '*' || strcmp(ntp->val, user) == 0)
  704. ok = 1;
  705. }
  706. ndbfree(tp);
  707. return ok;
  708. }
  709. /*
  710. * return an error reply
  711. */
  712. void
  713. replyerror(char *fmt, ...)
  714. {
  715. char buf[AERRLEN+1];
  716. va_list arg;
  717. memset(buf, 0, sizeof(buf));
  718. va_start(arg, fmt);
  719. vseprint(buf + 1, buf + sizeof(buf), fmt, arg);
  720. va_end(arg);
  721. buf[AERRLEN] = 0;
  722. buf[0] = AuthErr;
  723. write(1, buf, AERRLEN+1);
  724. syslog(0, AUTHLOG, buf+1);
  725. }
  726. void
  727. getraddr(char *dir)
  728. {
  729. int n;
  730. char *cp;
  731. char file[Maxpath];
  732. raddr[0] = 0;
  733. snprint(file, sizeof(file), "%s/remote", dir);
  734. n = readfile(file, raddr, sizeof(raddr)-1);
  735. if(n < 0)
  736. return;
  737. raddr[n] = 0;
  738. cp = strchr(raddr, '\n');
  739. if(cp)
  740. *cp = 0;
  741. cp = strchr(raddr, '!');
  742. if(cp)
  743. *cp = 0;
  744. }
  745. void
  746. mkkey(char *k)
  747. {
  748. randombytes((uchar*)k, DESKEYLEN);
  749. }
  750. void
  751. randombytes(uchar *buf, int len)
  752. {
  753. int i;
  754. if(readfile("/dev/random", (char*)buf, len) >= 0)
  755. return;
  756. for(i = 0; i < len; i++)
  757. buf[i] = rand();
  758. }
  759. /*
  760. * reply with ticket and authenticator
  761. */
  762. int
  763. tickauthreply(Ticketreq *tr, char *hkey)
  764. {
  765. Ticket t;
  766. Authenticator a;
  767. char buf[TICKETLEN+AUTHENTLEN+1];
  768. memset(&t, 0, sizeof(t));
  769. memmove(t.chal, tr->chal, CHALLEN);
  770. safecpy(t.cuid, tr->uid, sizeof t.cuid);
  771. safecpy(t.suid, tr->uid, sizeof t.suid);
  772. mkkey(t.key);
  773. buf[0] = AuthOK;
  774. t.num = AuthTs;
  775. convT2M(&t, buf+1, hkey);
  776. memmove(a.chal, t.chal, CHALLEN);
  777. a.num = AuthAc;
  778. a.id = 0;
  779. convA2M(&a, buf+TICKETLEN+1, t.key);
  780. if(write(1, buf, TICKETLEN+AUTHENTLEN+1) < 0)
  781. return -1;
  782. return 0;
  783. }
  784. void
  785. safecpy(char *to, char *from, int len)
  786. {
  787. strncpy(to, from, len);
  788. to[len-1] = 0;
  789. }