secureidcheck.c 9.6 KB

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