secureidcheck.c 9.7 KB

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