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.
21 #define AUTHLOG "auth"
24 R_AccessRequest =1, /* Packet code */
36 typedef struct Secret{
41 typedef struct Attribute{
42 struct Attribute *next;
44 uchar len; /* number of bytes in value */
48 typedef struct Packet{
50 uchar authenticator[16];
54 /* assumes pass is at most 16 chars */
56 hide(Secret *shared, uchar *auth, Secret *pass, uchar *x)
61 M = md5(shared->s, shared->len, nil, nil);
65 for(i = 0; i < n; i++)
70 authcmp(Secret *shared, uchar *buf, int m, uchar *auth)
75 M = md5(buf, 4, nil, nil); /* Code+ID+Length */
76 M = md5(auth, 16, nil, M); /* RequestAuth */
77 M = md5(buf+20, m-20, nil, M); /* Attributes */
78 md5(shared->s, shared->len, x, M);
79 return memcmp(x, buf+4, 16);
83 newRequest(uchar *auth)
88 p = (Packet*)malloc(sizeof(*p));
91 p->code = R_AccessRequest;
93 memmove(p->authenticator, auth, 16);
100 freePacket(Packet *p)
116 ding(void*, char *msg)
118 syslog(0, AUTHLOG, "ding %s", msg);
119 if(strstr(msg, "alarm"))
125 rpc(char *dest, Secret *shared, Packet *req)
127 uchar buf[4096], buf2[4096], *b, *e;
132 /* marshal request */
133 e = buf + sizeof buf;
136 memmove(buf+4, req->authenticator, 16);
138 for(a = &req->first; a; a = a->next){
139 if(b + 2 + a->len > e)
143 memmove(b, a->val, a->len);
150 /* send request, wait for reply */
151 fd = dial(dest, 0, 0, 0);
153 syslog(0, AUTHLOG, "%s: rpc can't get udp channel", dest);
158 for(try = 0; try < 2; try++){
160 * increased timeout from 4sec to 15sec because
161 * corporate server really takes that long.
164 m = write(fd, buf, n);
166 syslog(0, AUTHLOG, "%s: rpc write err %d %d: %r",
171 m = read(fd, buf2, sizeof buf2);
174 syslog(0, AUTHLOG, "%s rpc read err %d: %r", dest, m);
177 if(m == 0 || buf2[1] != buf[1]){ /* need matching ID */
178 syslog(0, AUTHLOG, "%s unmatched reply %d", dest, m);
181 if(authcmp(shared, buf2, m, buf+4) == 0)
183 syslog(0, AUTHLOG, "%s bad rpc chksum", dest);
189 /* unmarshal reply */
192 resp = (Packet*)malloc(sizeof(*resp));
200 syslog(0, AUTHLOG, "rpc got %d bytes, length said %d", m, n);
204 memmove(resp->authenticator, b, 16);
211 break; /* exit loop */
215 if(b + a->len > e){ /* corrupt packet */
220 memmove(a->val, b, a->len);
222 if(b < e){ /* any more attributes? */
223 a->next = (Attribute*)malloc(sizeof(*a));
235 setAttribute(Packet *p, uchar type, uchar *s, int n)
241 a = (Attribute*)malloc(sizeof(*a));
244 a->next = p->first.next;
249 if(a->len > 253) /* RFC2138, section 5 */
251 memmove(a->val, s, a->len);
255 /* return a reply message attribute string */
260 static char buf[255];
262 for(a = &p->first; a; a = a->next)
263 if(a->type == R_ReplyMessage){
264 if(a->len >= sizeof buf)
265 a->len = sizeof(buf)-1;
266 memmove(buf, a->val, a->len);
272 /* for convenience while debugging */
274 Attribute *stateattr;
281 char buf[255], pbuf[4*1024];
282 uchar *au = p->authenticator;
285 e = pbuf + sizeof(pbuf);
287 np = seprint(pbuf, e, "Packet ID=%d auth=%x %x %x... ",
288 p->ID, au[0], au[1], au[2]);
290 case R_AccessRequest:
291 np = seprint(np, e, "request\n");
294 np = seprint(np, e, "accept\n");
297 np = seprint(np, e, "reject\n");
299 case R_AccessChallenge:
300 np = seprint(np, e, "challenge\n");
303 np = seprint(np, e, "code=%d\n", p->code);
306 replymess = "0000000";
307 for(a = &p->first; a; a = a->next){
310 memmove(buf, a->val, a->len);
311 np = seprint(np, e, " [%d]", a->type);
312 for(i = 0; i < a->len; i++)
313 if(isprint(a->val[i]))
314 np = seprint(np, e, "%c", a->val[i]);
316 np = seprint(np, e, "\\%o", a->val[i]);
317 np = seprint(np, e, "\n");
319 if(a->type == R_ReplyMessage)
320 replymess = strdup(buf);
321 else if(a->type == R_State)
325 syslog(0, AUTHLOG, "%s", pbuf);
335 ifc = readipifc("/net", ifc, -1);
336 for(nifc = ifc; nifc; nifc = nifc->next)
337 for(lifc = nifc->lifc; lifc; lifc = lifc->next)
338 if (ipcmp(lifc->ip, IPnoaddr) != 0 &&
339 ipcmp(lifc->ip, v4prefix) != 0)
346 /* returns 0 on success, error message on failure */
348 secureidcheck(char *user, char *response)
350 char *radiussecret = nil;
351 char *rv = "authentication failed";
352 char dest[3*IPaddrlen+20], ruser[64];
357 Ndbtuple *t = nil, *nt, *tt;
358 Packet *req = nil, *resp = nil;
365 /* bad responses make them disable the fob, avoid silly checks */
366 if(strlen(response) < 4 || strpbrk(response,"abcdefABCDEF") != nil)
369 /* get radius secret */
370 radiussecret = ndbgetvalue(db, &s, "radius", "lra-radius", "secret", &t);
371 if(radiussecret == nil){
372 syslog(0, AUTHLOG, "secureidcheck: nil radius secret: %r");
376 /* translate user name if we have to */
378 for(nt = t; nt; nt = nt->entry)
379 if(strcmp(nt->attr, "uid") == 0 && strcmp(nt->val, user) == 0)
380 for(tt = nt->line; tt != nt; tt = tt->line)
381 if(strcmp(tt->attr, "rid") == 0){
382 strcpy(ruser, tt->val);
392 req = newRequest((uchar*)u);
395 shared.s = (uchar*)radiussecret;
396 shared.len = strlen(radiussecret);
399 syslog(0, AUTHLOG, "no interfaces: %r");
402 if(setAttribute(req, R_NASIPAddress, ip + IPv4off, 4) < 0)
405 if(setAttribute(req, R_UserName, (uchar*)ruser, strlen(ruser)) < 0)
407 pass.s = (uchar*)response;
408 pass.len = strlen(response);
409 hide(&shared, req->authenticator, &pass, x);
410 if(setAttribute(req, R_UserPassword, x, 16) < 0)
413 t = ndbsearch(netdb, &s, "sys", "lra-radius");
415 syslog(0, AUTHLOG, "secureidcheck: nil radius sys search: %r");
418 for(nt = t; nt; nt = nt->entry){
419 if(strcmp(nt->attr, "ip") != 0)
422 snprint(dest, sizeof dest, "udp!%s!radius", nt->val);
423 resp = rpc(dest, &shared, req);
425 syslog(0, AUTHLOG, "%s nil response", dest);
428 if(resp->ID != req->ID){
429 syslog(0, AUTHLOG, "%s mismatched ID req=%d resp=%d",
430 dest, req->ID, resp->ID);
438 syslog(0, AUTHLOG, "%s accepted ruser=%s", dest, ruser);
442 syslog(0, AUTHLOG, "%s rejected ruser=%s %s",
443 dest, ruser, replymsg(resp));
444 rv = "secureid failed";
446 case R_AccessChallenge:
447 syslog(0, AUTHLOG, "%s challenge ruser=%s %s",
448 dest, ruser, replymsg(resp));
449 rv = "secureid out of sync";
452 syslog(0, AUTHLOG, "%s code=%d ruser=%s %s",
453 dest, resp->code, ruser, replymsg(resp));
456 break; /* we have a proper reply, no need to ask again */