secureidcheck.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482
  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. /* Portions of this file are Copyright (C) 2015-2018 Giacomo Tesio <giacomo@tesio.it>
  10. * See /doc/license/gpl-2.0.txt for details about the licensing.
  11. */
  12. /* Portions of this file are Copyright (C) 9front's team.
  13. * See /doc/license/9front-mit for details about the licensing.
  14. * See http://code.9front.org/hg/plan9front/ for a list of authors.
  15. */
  16. /*
  17. * This code uses RADIUS as a portable way to validate tokens such as SecurID.
  18. * It is relatively simple to send a UDP packet and get a response, but various
  19. * things can go wrong. Speaking the proprietary ACE protocol would allow
  20. * handling "next token code" and other error messages. More importantly, the
  21. * timeout threshold is inherently hard to pick. We observe responses taking
  22. * longer than 10 seconds in normal times. That is a int32_t time to wait before
  23. * retrying on a second server. Moreover, if the UDP response is lost, retrying
  24. * on a second server will also fail because the valid token code may be
  25. * presented only once. This whole approach is flawed, but best we can do.
  26. */
  27. /* RFC2138 */
  28. #include <u.h>
  29. #include <lib9.h>
  30. #include <ip.h>
  31. #include <chartypes.h>
  32. #include <mp.h>
  33. #include <libsec.h>
  34. #include <bio.h>
  35. #include <ndb.h>
  36. #define AUTHLOG "auth"
  37. enum{
  38. R_AccessRequest =1, /* Packet code */
  39. R_AccessAccept =2,
  40. R_AccessReject =3,
  41. R_AccessChallenge=11,
  42. R_UserName =1,
  43. R_UserPassword =2,
  44. R_NASIPAddress =4,
  45. R_ReplyMessage =18,
  46. R_State =24,
  47. R_NASIdentifier =32,
  48. };
  49. typedef struct Secret{
  50. uint8_t *s;
  51. int len;
  52. } Secret;
  53. typedef struct Attribute{
  54. struct Attribute *next;
  55. uint8_t type;
  56. uint8_t len; /* number of bytes in value */
  57. uint8_t val[256];
  58. } Attribute;
  59. typedef struct Packet{
  60. uint8_t code, ID;
  61. uint8_t authenticator[16];
  62. Attribute first;
  63. } Packet;
  64. /* assumes pass is at most 16 chars */
  65. void
  66. hide(Secret *shared, uint8_t *auth, Secret *pass, uint8_t *x)
  67. {
  68. DigestState *M;
  69. int i, n = pass->len;
  70. M = md5(shared->s, shared->len, nil, nil);
  71. md5(auth, 16, x, M);
  72. if(n > 16)
  73. n = 16;
  74. for(i = 0; i < n; i++)
  75. x[i] ^= pass->s[i];
  76. }
  77. int
  78. authcmp(Secret *shared, uint8_t *buf, int m, uint8_t *auth)
  79. {
  80. DigestState *M;
  81. uint8_t x[16];
  82. M = md5(buf, 4, nil, nil); /* Code+ID+Length */
  83. M = md5(auth, 16, nil, M); /* RequestAuth */
  84. M = md5(buf+20, m-20, nil, M); /* Attributes */
  85. md5(shared->s, shared->len, x, M);
  86. return memcmp(x, buf+4, 16);
  87. }
  88. Packet*
  89. newRequest(uint8_t *auth)
  90. {
  91. static uint8_t ID = 0;
  92. Packet *p;
  93. p = (Packet*)malloc(sizeof(*p));
  94. if(p == nil)
  95. return nil;
  96. p->code = R_AccessRequest;
  97. p->ID = ++ID;
  98. memmove(p->authenticator, auth, 16);
  99. p->first.next = nil;
  100. p->first.type = 0;
  101. return p;
  102. }
  103. void
  104. freePacket(Packet *p)
  105. {
  106. Attribute *a, *x;
  107. if(!p)
  108. return;
  109. a = p->first.next;
  110. while(a){
  111. x = a;
  112. a = a->next;
  113. free(x);
  114. }
  115. free(p);
  116. }
  117. int
  118. ding(void* _, char *msg)
  119. {
  120. syslog(0, AUTHLOG, "ding %s", msg);
  121. if(strstr(msg, "alarm"))
  122. return 1;
  123. return 0;
  124. }
  125. Packet *
  126. rpc(char *dest, Secret *shared, Packet *req)
  127. {
  128. uint8_t buf[4096], buf2[4096], *b, *e;
  129. Packet *resp;
  130. Attribute *a;
  131. int m, n, fd, try;
  132. /* marshal request */
  133. e = buf + sizeof buf;
  134. buf[0] = req->code;
  135. buf[1] = req->ID;
  136. memmove(buf+4, req->authenticator, 16);
  137. b = buf+20;
  138. for(a = &req->first; a; a = a->next){
  139. if(b + 2 + a->len > e)
  140. return nil;
  141. *b++ = a->type;
  142. *b++ = 2 + a->len;
  143. memmove(b, a->val, a->len);
  144. b += a->len;
  145. }
  146. n = b-buf;
  147. buf[2] = n>>8;
  148. buf[3] = n;
  149. /* send request, wait for reply */
  150. fd = dial(dest, 0, 0, 0);
  151. if(fd < 0){
  152. syslog(0, AUTHLOG, "%s: rpc can't get udp channel", dest);
  153. return nil;
  154. }
  155. atnotify(ding, 1);
  156. m = -1;
  157. for(try = 0; try < 2; try++){
  158. /*
  159. * increased timeout from 4sec to 15sec because
  160. * corporate server really takes that int32_t.
  161. */
  162. sys_alarm(15000);
  163. m = jehanne_write(fd, buf, n);
  164. if(m != n){
  165. syslog(0, AUTHLOG, "%s: rpc write err %d %d: %r",
  166. dest, m, n);
  167. m = -1;
  168. break;
  169. }
  170. m = jehanne_read(fd, buf2, sizeof buf2);
  171. sys_alarm(0);
  172. if(m < 0){
  173. syslog(0, AUTHLOG, "%s rpc read err %d: %r", dest, m);
  174. break; /* failure */
  175. }
  176. if(m == 0 || buf2[1] != buf[1]){ /* need matching ID */
  177. syslog(0, AUTHLOG, "%s unmatched reply %d", dest, m);
  178. continue;
  179. }
  180. if(authcmp(shared, buf2, m, buf+4) == 0)
  181. break;
  182. syslog(0, AUTHLOG, "%s bad rpc chksum", dest);
  183. }
  184. sys_close(fd);
  185. if(m <= 0)
  186. return nil;
  187. /* unmarshal reply */
  188. b = buf2;
  189. e = buf2+m;
  190. resp = (Packet*)malloc(sizeof(*resp));
  191. if(resp == nil)
  192. return nil;
  193. resp->code = *b++;
  194. resp->ID = *b++;
  195. n = *b++;
  196. n = (n<<8) | *b++;
  197. if(m != n){
  198. syslog(0, AUTHLOG, "rpc got %d bytes, length said %d", m, n);
  199. if(m > n)
  200. e = buf2+n;
  201. }
  202. memmove(resp->authenticator, b, 16);
  203. b += 16;
  204. a = &resp->first;
  205. a->type = 0;
  206. for(;;){
  207. if(b >= e){
  208. a->next = nil;
  209. break; /* exit loop */
  210. }
  211. a->type = *b++;
  212. a->len = (*b++) - 2;
  213. if(b + a->len > e){ /* corrupt packet */
  214. a->next = nil;
  215. freePacket(resp);
  216. return nil;
  217. }
  218. memmove(a->val, b, a->len);
  219. b += a->len;
  220. if(b < e){ /* any more attributes? */
  221. a->next = (Attribute*)malloc(sizeof(*a));
  222. if(a->next == nil){
  223. free(req);
  224. return nil;
  225. }
  226. a = a->next;
  227. }
  228. }
  229. return resp;
  230. }
  231. int
  232. setAttribute(Packet *p, uint8_t type, uint8_t *s, int n)
  233. {
  234. Attribute *a;
  235. a = &p->first;
  236. if(a->type != 0){
  237. a = (Attribute*)malloc(sizeof(*a));
  238. if(a == nil)
  239. return -1;
  240. a->next = p->first.next;
  241. p->first.next = a;
  242. }
  243. a->type = type;
  244. a->len = n;
  245. if(a->len > 253) /* RFC2138, section 5 */
  246. a->len = 253;
  247. memmove(a->val, s, a->len);
  248. return 0;
  249. }
  250. /* return a reply message attribute string */
  251. char*
  252. replymsg(Packet *p)
  253. {
  254. Attribute *a;
  255. static char buf[255];
  256. for(a = &p->first; a; a = a->next)
  257. if(a->type == R_ReplyMessage){
  258. if(a->len >= sizeof buf)
  259. a->len = sizeof(buf)-1;
  260. memmove(buf, a->val, a->len);
  261. buf[a->len] = 0;
  262. }
  263. return buf;
  264. }
  265. /* for convenience while debugging */
  266. char *replymess;
  267. Attribute *stateattr;
  268. void
  269. logPacket(Packet *p)
  270. {
  271. int i;
  272. char *np, *e;
  273. char buf[255], pbuf[4*1024];
  274. uint8_t *au = p->authenticator;
  275. Attribute *a;
  276. e = pbuf + sizeof(pbuf);
  277. np = seprint(pbuf, e, "Packet ID=%d auth=%x %x %x... ",
  278. p->ID, au[0], au[1], au[2]);
  279. switch(p->code){
  280. case R_AccessRequest:
  281. np = seprint(np, e, "request\n");
  282. break;
  283. case R_AccessAccept:
  284. np = seprint(np, e, "accept\n");
  285. break;
  286. case R_AccessReject:
  287. np = seprint(np, e, "reject\n");
  288. break;
  289. case R_AccessChallenge:
  290. np = seprint(np, e, "challenge\n");
  291. break;
  292. default:
  293. np = seprint(np, e, "code=%d\n", p->code);
  294. break;
  295. }
  296. replymess = "0000000";
  297. for(a = &p->first; a; a = a->next){
  298. if(a->len > 253 )
  299. a->len = 253;
  300. memmove(buf, a->val, a->len);
  301. np = seprint(np, e, " [%d]", a->type);
  302. for(i = 0; i < a->len; i++)
  303. if(isprint(a->val[i]))
  304. np = seprint(np, e, "%c", a->val[i]);
  305. else
  306. np = seprint(np, e, "\\%o", a->val[i]);
  307. np = seprint(np, e, "\n");
  308. buf[a->len] = 0;
  309. if(a->type == R_ReplyMessage)
  310. replymess = strdup(buf);
  311. else if(a->type == R_State)
  312. stateattr = a;
  313. }
  314. syslog(0, AUTHLOG, "%s", pbuf);
  315. }
  316. static uint8_t*
  317. getipv4addr(void)
  318. {
  319. Ipifc *nifc;
  320. Iplifc *lifc;
  321. static Ipifc *ifc;
  322. ifc = readipifc("/net", ifc, -1);
  323. for(nifc = ifc; nifc; nifc = nifc->next)
  324. for(lifc = nifc->lifc; lifc; lifc = lifc->next)
  325. if (ipcmp(lifc->ip, IPnoaddr) != 0 &&
  326. ipcmp(lifc->ip, v4prefix) != 0)
  327. return lifc->ip;
  328. return nil;
  329. }
  330. extern Ndb *db;
  331. /* returns 0 on success, error message on failure */
  332. char*
  333. secureidcheck(char *user, char *response)
  334. {
  335. char *radiussecret = nil;
  336. char *rv = "authentication failed";
  337. char dest[3*IPaddrlen+20], ruser[64];
  338. uint8_t *ip;
  339. uint8_t x[16];
  340. uint32_t u[4];
  341. Ndbs s;
  342. Ndbtuple *t = nil, *nt, *tt;
  343. Packet *req = nil, *resp = nil;
  344. Secret shared, pass;
  345. static Ndb *netdb;
  346. if(netdb == nil)
  347. netdb = ndbopen(0);
  348. /* bad responses make them disable the fob, avoid silly checks */
  349. if(strlen(response) < 4 || strpbrk(response,"abcdefABCDEF") != nil)
  350. goto out;
  351. /* get radius secret */
  352. radiussecret = ndbgetvalue(db, &s, "radius", "lra-radius", "secret", &t);
  353. if(radiussecret == nil){
  354. syslog(0, AUTHLOG, "secureidcheck: nil radius secret: %r");
  355. goto out;
  356. }
  357. /* translate user name if we have to */
  358. strcpy(ruser, user);
  359. for(nt = t; nt; nt = nt->entry)
  360. if(strcmp(nt->attr, "uid") == 0 && strcmp(nt->val, user) == 0)
  361. for(tt = nt->line; tt != nt; tt = tt->line)
  362. if(strcmp(tt->attr, "rid") == 0){
  363. strcpy(ruser, tt->val);
  364. break;
  365. }
  366. ndbfree(t);
  367. t = nil;
  368. u[0] = fastrand();
  369. u[1] = fastrand();
  370. u[2] = fastrand();
  371. u[3] = fastrand();
  372. req = newRequest((uint8_t*)u);
  373. if(req == nil)
  374. goto out;
  375. shared.s = (uint8_t*)radiussecret;
  376. shared.len = strlen(radiussecret);
  377. ip = getipv4addr();
  378. if(ip == nil){
  379. syslog(0, AUTHLOG, "no interfaces: %r");
  380. goto out;
  381. }
  382. if(setAttribute(req, R_NASIPAddress, ip + IPv4off, 4) < 0)
  383. goto out;
  384. if(setAttribute(req, R_UserName, (uint8_t*)ruser, strlen(ruser)) < 0)
  385. goto out;
  386. pass.s = (uint8_t*)response;
  387. pass.len = strlen(response);
  388. hide(&shared, req->authenticator, &pass, x);
  389. if(setAttribute(req, R_UserPassword, x, 16) < 0)
  390. goto out;
  391. t = ndbsearch(netdb, &s, "sys", "lra-radius");
  392. if(t == nil){
  393. syslog(0, AUTHLOG, "secureidcheck: nil radius sys search: %r");
  394. goto out;
  395. }
  396. for(nt = t; nt; nt = nt->entry){
  397. if(strcmp(nt->attr, "ip") != 0)
  398. continue;
  399. snprint(dest, sizeof dest, "udp!%s!radius", nt->val);
  400. resp = rpc(dest, &shared, req);
  401. if(resp == nil){
  402. syslog(0, AUTHLOG, "%s nil response", dest);
  403. continue;
  404. }
  405. if(resp->ID != req->ID){
  406. syslog(0, AUTHLOG, "%s mismatched ID req=%d resp=%d",
  407. dest, req->ID, resp->ID);
  408. freePacket(resp);
  409. resp = nil;
  410. continue;
  411. }
  412. switch(resp->code){
  413. case R_AccessAccept:
  414. syslog(0, AUTHLOG, "%s accepted ruser=%s", dest, ruser);
  415. rv = nil;
  416. break;
  417. case R_AccessReject:
  418. syslog(0, AUTHLOG, "%s rejected ruser=%s %s",
  419. dest, ruser, replymsg(resp));
  420. rv = "secureid failed";
  421. break;
  422. case R_AccessChallenge:
  423. syslog(0, AUTHLOG, "%s challenge ruser=%s %s",
  424. dest, ruser, replymsg(resp));
  425. rv = "secureid out of sync";
  426. break;
  427. default:
  428. syslog(0, AUTHLOG, "%s code=%d ruser=%s %s",
  429. dest, resp->code, ruser, replymsg(resp));
  430. break;
  431. }
  432. break; /* we have a proper reply, no need to ask again */
  433. }
  434. out:
  435. if (t)
  436. ndbfree(t);
  437. free(radiussecret);
  438. freePacket(req);
  439. freePacket(resp);
  440. return rv;
  441. }