authsrv.c 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914
  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. /* not reached */
  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;
  311. int n;
  312. if(domain)
  313. return domain;
  314. if(*sysname)
  315. return sysname;
  316. domain = csgetvalue(0, "sys", sysname, "dom", nil);
  317. if(domain)
  318. return domain;
  319. n = readfile("/dev/sysname", sysname, sizeof(sysname)-1);
  320. if(n < 0){
  321. strcpy(sysname, "kremvax");
  322. return sysname;
  323. }
  324. sysname[n] = 0;
  325. return sysname;
  326. }
  327. static int
  328. h2b(char c)
  329. {
  330. if(c >= '0' && c <= '9')
  331. return c - '0';
  332. if(c >= 'A' && c <= 'F')
  333. return c - 'A' + 10;
  334. if(c >= 'a' && c <= 'f')
  335. return c - 'a' + 10;
  336. return 0;
  337. }
  338. void
  339. apop(Ticketreq *tr, int type)
  340. {
  341. int challen, i, tries;
  342. char *secret, *hkey, *p;
  343. Ticketreq treq;
  344. DigestState *s;
  345. char sbuf[SECRETLEN], hbuf[DESKEYLEN];
  346. char tbuf[TICKREQLEN];
  347. char buf[MD5dlen*2];
  348. uchar digest[MD5dlen], resp[MD5dlen];
  349. ulong rb[4];
  350. char chal[256];
  351. USED(tr);
  352. /*
  353. * Create a challenge and send it.
  354. */
  355. randombytes((uchar*)rb, sizeof(rb));
  356. p = chal;
  357. p += snprint(p, sizeof(chal), "<%lux%lux.%lux%lux@%s>",
  358. rb[0], rb[1], rb[2], rb[3], domainname());
  359. challen = p - chal;
  360. print("%c%-5d%s", AuthOKvar, challen, chal);
  361. /* give user a few attempts */
  362. for(tries = 0; ; tries++) {
  363. /*
  364. * get ticket request
  365. */
  366. if(readn(0, tbuf, TICKREQLEN) < 0)
  367. exits(0);
  368. convM2TR(tbuf, &treq);
  369. tr = &treq;
  370. if(tr->type != type)
  371. exits(0);
  372. /*
  373. * read response
  374. */
  375. if(readn(0, buf, MD5dlen*2) < 0)
  376. exits(0);
  377. for(i = 0; i < MD5dlen; i++)
  378. resp[i] = (h2b(buf[2*i])<<4)|h2b(buf[2*i+1]);
  379. /*
  380. * lookup
  381. */
  382. secret = findsecret(KEYDB, tr->uid, sbuf);
  383. hkey = findkey(KEYDB, tr->hostid, hbuf);
  384. if(hkey == 0 || secret == 0){
  385. replyerror("apop-fail bad response %s", raddr);
  386. logfail(tr->uid);
  387. if(tries > 5)
  388. return;
  389. continue;
  390. }
  391. /*
  392. * check for match
  393. */
  394. if(type == AuthCram){
  395. hmac_md5((uchar*)chal, challen,
  396. (uchar*)secret, strlen(secret),
  397. digest, nil);
  398. } else {
  399. s = md5((uchar*)chal, challen, 0, 0);
  400. md5((uchar*)secret, strlen(secret), digest, s);
  401. }
  402. if(memcmp(digest, resp, MD5dlen) != 0){
  403. replyerror("apop-fail bad response %s", raddr);
  404. logfail(tr->uid);
  405. if(tries > 5)
  406. return;
  407. continue;
  408. }
  409. break;
  410. }
  411. succeed(tr->uid);
  412. /*
  413. * reply with ticket & authenticator
  414. */
  415. if(tickauthreply(tr, hkey) < 0)
  416. exits(0);
  417. if(debug){
  418. if(type == AuthCram)
  419. syslog(0, AUTHLOG, "cram-ok %s %s", tr->uid, raddr);
  420. else
  421. syslog(0, AUTHLOG, "apop-ok %s %s", tr->uid, raddr);
  422. }
  423. }
  424. enum {
  425. VNCchallen= 16,
  426. };
  427. /* VNC reverses the bits of each byte before using as a des key */
  428. uchar swizzletab[256] = {
  429. 0x0, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
  430. 0x8, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
  431. 0x4, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
  432. 0xc, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
  433. 0x2, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
  434. 0xa, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
  435. 0x6, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
  436. 0xe, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
  437. 0x1, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
  438. 0x9, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
  439. 0x5, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
  440. 0xd, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
  441. 0x3, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
  442. 0xb, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
  443. 0x7, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
  444. 0xf, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff,
  445. };
  446. void
  447. vnc(Ticketreq *tr)
  448. {
  449. uchar chal[VNCchallen+6];
  450. uchar reply[VNCchallen];
  451. char *secret, *hkey;
  452. char sbuf[SECRETLEN], hbuf[DESKEYLEN];
  453. DESstate s;
  454. int i;
  455. /*
  456. * Create a challenge and send it.
  457. */
  458. randombytes(chal+6, VNCchallen);
  459. chal[0] = AuthOKvar;
  460. sprint((char*)chal+1, "%-5d", VNCchallen);
  461. if(write(1, chal, sizeof(chal)) != sizeof(chal))
  462. return;
  463. /*
  464. * lookup keys (and swizzle bits)
  465. */
  466. memset(sbuf, 0, sizeof(sbuf));
  467. secret = findsecret(KEYDB, tr->uid, sbuf);
  468. if(secret == 0){
  469. randombytes((uchar*)sbuf, sizeof(sbuf));
  470. secret = sbuf;
  471. }
  472. for(i = 0; i < 8; i++)
  473. secret[i] = swizzletab[(uchar)secret[i]];
  474. hkey = findkey(KEYDB, tr->hostid, hbuf);
  475. if(hkey == 0){
  476. randombytes((uchar*)hbuf, sizeof(hbuf));
  477. hkey = hbuf;
  478. }
  479. /*
  480. * get response
  481. */
  482. if(readn(0, reply, sizeof(reply)) != sizeof(reply))
  483. return;
  484. /*
  485. * decrypt response and compare
  486. */
  487. setupDESstate(&s, (uchar*)secret, nil);
  488. desECBdecrypt(reply, sizeof(reply), &s);
  489. if(memcmp(reply, chal+6, VNCchallen) != 0){
  490. replyerror("vnc-fail bad response %s", raddr);
  491. logfail(tr->uid);
  492. return;
  493. }
  494. succeed(tr->uid);
  495. /*
  496. * reply with ticket & authenticator
  497. */
  498. if(tickauthreply(tr, hkey) < 0)
  499. exits(0);
  500. if(debug)
  501. syslog(0, AUTHLOG, "vnc-ok %s %s", tr->uid, raddr);
  502. }
  503. void
  504. chap(Ticketreq *tr)
  505. {
  506. char *secret, *hkey;
  507. DigestState *s;
  508. char sbuf[SECRETLEN], hbuf[DESKEYLEN];
  509. uchar digest[MD5dlen];
  510. char chal[CHALLEN];
  511. OChapreply reply;
  512. /*
  513. * Create a challenge and send it.
  514. */
  515. randombytes((uchar*)chal, sizeof(chal));
  516. write(1, chal, sizeof(chal));
  517. /*
  518. * get chap reply
  519. */
  520. if(readn(0, &reply, sizeof(reply)) < 0)
  521. exits(0);
  522. safecpy(tr->uid, reply.uid, sizeof(tr->uid));
  523. /*
  524. * lookup
  525. */
  526. secret = findsecret(KEYDB, tr->uid, sbuf);
  527. hkey = findkey(KEYDB, tr->hostid, hbuf);
  528. if(hkey == 0 || secret == 0){
  529. replyerror("chap-fail bad response %s", raddr);
  530. logfail(tr->uid);
  531. exits(0);
  532. }
  533. /*
  534. * check for match
  535. */
  536. s = md5(&reply.id, 1, 0, 0);
  537. md5((uchar*)secret, strlen(secret), 0, s);
  538. md5((uchar*)chal, sizeof(chal), digest, s);
  539. if(memcmp(digest, reply.resp, MD5dlen) != 0){
  540. replyerror("chap-fail bad response %s", raddr);
  541. logfail(tr->uid);
  542. exits(0);
  543. }
  544. succeed(tr->uid);
  545. /*
  546. * reply with ticket & authenticator
  547. */
  548. if(tickauthreply(tr, hkey) < 0)
  549. exits(0);
  550. if(debug)
  551. syslog(0, AUTHLOG, "chap-ok %s %s", tr->uid, raddr);
  552. }
  553. void
  554. printresp(uchar resp[MSresplen])
  555. {
  556. char buf[200], *p;
  557. int i;
  558. p = buf;
  559. for(i=0; i<MSresplen; i++)
  560. p += sprint(p, "%.2ux ", resp[i]);
  561. syslog(0, AUTHLOG, "resp = %s", buf);
  562. }
  563. void
  564. mschap(Ticketreq *tr)
  565. {
  566. char *secret, *hkey;
  567. char sbuf[SECRETLEN], hbuf[DESKEYLEN];
  568. uchar chal[CHALLEN];
  569. uchar hash[MShashlen];
  570. uchar hash2[MShashlen];
  571. uchar resp[MSresplen];
  572. OMSchapreply reply;
  573. int dupe, lmok, ntok;
  574. DigestState *s;
  575. uchar digest[SHA1dlen];
  576. /*
  577. * Create a challenge and send it.
  578. */
  579. randombytes((uchar*)chal, sizeof(chal));
  580. write(1, chal, sizeof(chal));
  581. /*
  582. * get chap reply
  583. */
  584. if(readn(0, &reply, sizeof(reply)) < 0)
  585. exits(0);
  586. safecpy(tr->uid, reply.uid, sizeof(tr->uid));
  587. /*
  588. * lookup
  589. */
  590. secret = findsecret(KEYDB, tr->uid, sbuf);
  591. hkey = findkey(KEYDB, tr->hostid, hbuf);
  592. if(hkey == 0 || secret == 0){
  593. replyerror("mschap-fail bad response %s/%s(%s)",
  594. tr->uid, tr->hostid, raddr);
  595. logfail(tr->uid);
  596. exits(0);
  597. }
  598. lmhash(hash, secret);
  599. mschalresp(resp, hash, chal);
  600. lmok = memcmp(resp, reply.LMresp, MSresplen) == 0;
  601. nthash(hash, secret);
  602. mschalresp(resp, hash, chal);
  603. ntok = memcmp(resp, reply.NTresp, MSresplen) == 0;
  604. dupe = memcmp(reply.LMresp, reply.NTresp, MSresplen) == 0;
  605. /*
  606. * It is valid to send the same response in both the LM and NTLM
  607. * fields provided one of them is correct, if neither matches,
  608. * or the two fields are different and either fails to match,
  609. * the whole sha-bang fails.
  610. *
  611. * This is an improvement in security as it allows clients who
  612. * wish to do NTLM auth (which is insecure) not to send
  613. * LM tokens (which is very insecure).
  614. *
  615. * Windows servers supports clients doing this also though
  616. * windows clients don't seem to use the feature.
  617. */
  618. if((!ntok && !lmok) || ((!ntok || !lmok) && !dupe)){
  619. replyerror("mschap-fail bad response %s/%s(%s) %d,%d,%d",
  620. tr->uid, tr->hostid, raddr, dupe, lmok, ntok);
  621. logfail(tr->uid);
  622. exits(0);
  623. }
  624. succeed(tr->uid);
  625. /*
  626. * reply with ticket & authenticator
  627. */
  628. if(tickauthreply(tr, hkey) < 0)
  629. exits(0);
  630. if(debug)
  631. replyerror("mschap-ok %s/%s(%s) %ux",
  632. tr->uid, tr->hostid, raddr);
  633. nthash(hash, secret);
  634. md4(hash, 16, hash2, 0);
  635. s = sha1(hash2, 16, 0, 0);
  636. sha1(hash2, 16, 0, s);
  637. sha1(chal, 8, digest, s);
  638. if(write(1, digest, 16) < 0)
  639. exits(0);
  640. }
  641. void
  642. nthash(uchar hash[MShashlen], char *passwd)
  643. {
  644. uchar buf[512];
  645. int i;
  646. for (i = 0; *passwd && i + 1 < sizeof(buf);) {
  647. Rune r;
  648. passwd += chartorune(&r, passwd);
  649. buf[i++] = r;
  650. buf[i++] = r >> 8;
  651. }
  652. memset(hash, 0, 16);
  653. md4(buf, i, hash, 0);
  654. }
  655. void
  656. lmhash(uchar hash[MShashlen], char *passwd)
  657. {
  658. uchar buf[14];
  659. char *stdtext = "KGS!@#$%";
  660. int i;
  661. strncpy((char*)buf, passwd, sizeof(buf));
  662. for(i=0; i<sizeof(buf); i++)
  663. if(buf[i] >= 'a' && buf[i] <= 'z')
  664. buf[i] += 'A' - 'a';
  665. memset(hash, 0, 16);
  666. memcpy(hash, stdtext, 8);
  667. memcpy(hash+8, stdtext, 8);
  668. desencrypt(hash, buf);
  669. desencrypt(hash+8, buf+7);
  670. }
  671. void
  672. mschalresp(uchar resp[MSresplen], uchar hash[MShashlen], uchar chal[MSchallen])
  673. {
  674. int i;
  675. uchar buf[21];
  676. memset(buf, 0, sizeof(buf));
  677. memcpy(buf, hash, MShashlen);
  678. for(i=0; i<3; i++) {
  679. memmove(resp+i*MSchallen, chal, MSchallen);
  680. desencrypt(resp+i*MSchallen, buf+i*7);
  681. }
  682. }
  683. void
  684. desencrypt(uchar data[8], uchar key[7])
  685. {
  686. ulong ekey[32];
  687. key_setup(key, ekey);
  688. block_cipher(ekey, data, 0);
  689. }
  690. /*
  691. * return true of the speaker may speak for the user
  692. *
  693. * a speaker may always speak for himself/herself
  694. */
  695. int
  696. speaksfor(char *speaker, char *user)
  697. {
  698. Ndbtuple *tp, *ntp;
  699. Ndbs s;
  700. int ok;
  701. char notuser[Maxpath];
  702. if(strcmp(speaker, user) == 0)
  703. return 1;
  704. if(db == 0)
  705. return 0;
  706. tp = ndbsearch(db, &s, "hostid", speaker);
  707. if(tp == 0)
  708. return 0;
  709. ok = 0;
  710. snprint(notuser, sizeof notuser, "!%s", user);
  711. for(ntp = tp; ntp; ntp = ntp->entry)
  712. if(strcmp(ntp->attr, "uid") == 0){
  713. if(strcmp(ntp->val, notuser) == 0){
  714. ok = 0;
  715. break;
  716. }
  717. if(*ntp->val == '*' || strcmp(ntp->val, user) == 0)
  718. ok = 1;
  719. }
  720. ndbfree(tp);
  721. return ok;
  722. }
  723. /*
  724. * return an error reply
  725. */
  726. void
  727. replyerror(char *fmt, ...)
  728. {
  729. char buf[AERRLEN+1];
  730. va_list arg;
  731. memset(buf, 0, sizeof(buf));
  732. va_start(arg, fmt);
  733. vseprint(buf + 1, buf + sizeof(buf), fmt, arg);
  734. va_end(arg);
  735. buf[AERRLEN] = 0;
  736. buf[0] = AuthErr;
  737. write(1, buf, AERRLEN+1);
  738. syslog(0, AUTHLOG, buf+1);
  739. }
  740. void
  741. getraddr(char *dir)
  742. {
  743. int n;
  744. char *cp;
  745. char file[Maxpath];
  746. raddr[0] = 0;
  747. snprint(file, sizeof(file), "%s/remote", dir);
  748. n = readfile(file, raddr, sizeof(raddr)-1);
  749. if(n < 0)
  750. return;
  751. raddr[n] = 0;
  752. cp = strchr(raddr, '\n');
  753. if(cp)
  754. *cp = 0;
  755. cp = strchr(raddr, '!');
  756. if(cp)
  757. *cp = 0;
  758. }
  759. void
  760. mkkey(char *k)
  761. {
  762. randombytes((uchar*)k, DESKEYLEN);
  763. }
  764. void
  765. randombytes(uchar *buf, int len)
  766. {
  767. int i;
  768. if(readfile("/dev/random", (char*)buf, len) >= 0)
  769. return;
  770. for(i = 0; i < len; i++)
  771. buf[i] = rand();
  772. }
  773. /*
  774. * reply with ticket and authenticator
  775. */
  776. int
  777. tickauthreply(Ticketreq *tr, char *hkey)
  778. {
  779. Ticket t;
  780. Authenticator a;
  781. char buf[TICKETLEN+AUTHENTLEN+1];
  782. memset(&t, 0, sizeof(t));
  783. memmove(t.chal, tr->chal, CHALLEN);
  784. safecpy(t.cuid, tr->uid, sizeof t.cuid);
  785. safecpy(t.suid, tr->uid, sizeof t.suid);
  786. mkkey(t.key);
  787. buf[0] = AuthOK;
  788. t.num = AuthTs;
  789. convT2M(&t, buf+1, hkey);
  790. memmove(a.chal, t.chal, CHALLEN);
  791. a.num = AuthAc;
  792. a.id = 0;
  793. convA2M(&a, buf+TICKETLEN+1, t.key);
  794. if(write(1, buf, TICKETLEN+AUTHENTLEN+1) < 0)
  795. return -1;
  796. return 0;
  797. }
  798. void
  799. safecpy(char *to, char *from, int len)
  800. {
  801. strncpy(to, from, len);
  802. to[len-1] = 0;
  803. }