📄 secureidcheck.c
字号:
/* This code uses RADIUS as a portable way to validate tokens such as SecurID. It is relatively simple to send a UDP packet and get a response, but various things can go wrong. Speaking the proprietary ACE protocol would allow handling "next token code" and other error messages. More importantly, the timeout threshold is inherently hard to pick. We observe responses taking longer than 10 seconds in normal times. That is a long time to wait before retrying on a second server. Moreover, if the UDP response is lost, retrying on a second server will also fail because the valid token code may be presented only once. This whole approach is flawed, but best we can do.*//* RFC2138 */#include <u.h>#include <libc.h>#include <ip.h>#include <ctype.h>#include <mp.h>#include <libsec.h>#include <bio.h>#include <ndb.h>#define AUTHLOG "auth"enum{ R_AccessRequest=1, /* Packet code */ R_AccessAccept=2, R_AccessReject=3, R_AccessChallenge=11, R_UserName=1, R_UserPassword=2, R_NASIPAddress=4, R_ReplyMessage=18, R_State=24, R_NASIdentifier=32};typedef struct Secret{ uchar *s; int len;} Secret;typedef struct Attribute{ struct Attribute *next; uchar type; uchar len; // number of bytes in value uchar val[256];} Attribute;typedef struct Packet{ uchar code, ID; uchar authenticator[16]; Attribute first;} Packet;// assumes pass is at most 16 charsvoidhide(Secret *shared, uchar *auth, Secret *pass, uchar *x){ DigestState *M; int i, n = pass->len; M = md5(shared->s, shared->len, nil, nil); md5(auth, 16, x, M); if(n > 16) n = 16; for(i = 0; i < n; i++) x[i] ^= (pass->s)[i];}intauthcmp(Secret *shared, uchar *buf, int m, uchar *auth){ DigestState *M; uchar x[16]; M = md5(buf, 4, nil, nil); // Code+ID+Length M = md5(auth, 16, nil, M); // RequestAuth M = md5(buf+20, m-20, nil, M); // Attributes md5(shared->s, shared->len, x, M); return memcmp(x, buf+4, 16);}Packet*newRequest(uchar *auth){ static uchar ID = 0; Packet *p; p = (Packet*)malloc(sizeof(*p)); if(p == nil) return nil; p->code = R_AccessRequest; p->ID = ++ID; memmove(p->authenticator, auth, 16); p->first.next = nil; p->first.type = 0; return p;}voidfreePacket(Packet *p){ Attribute *a, *x; if(!p) return; a = p->first.next; while(a){ x = a; a = a->next; free(x); } free(p);}intding(void*, char *msg){ syslog(0, AUTHLOG, "ding %s", msg); if(strstr(msg, "alarm")) return 1; return 0;}Packet *rpc(char *dest, Secret *shared, Packet *req){ uchar buf[4096], buf2[4096], *b, *e; Packet *resp; Attribute *a; int m, n, fd, try; // marshal request e = buf + sizeof buf; buf[0] = req->code; buf[1] = req->ID; memmove(buf+4, req->authenticator, 16); b = buf+20; for(a = &req->first; a; a = a->next){ if(b + 2 + a->len > e) return nil; *b++ = a->type; *b++ = 2 + a->len; memmove(b, a->val, a->len); b += a->len; } n = b-buf; buf[2] = n>>8; buf[3] = n; // send request, wait for reply fd = dial(dest, 0, 0, 0); if(fd < 0){ syslog(0, AUTHLOG, "%s: rpc can't get udp channel", dest); return nil; } atnotify(ding, 1); m = -1; for(try = 0; try < 2; try++){ /* increased timeout from 4sec to 15sec because corporate server really takes that long */ alarm(15000); m = write(fd, buf, n); if(m != n){ syslog(0, AUTHLOG, "%s: rpc write err %d %d: %r", dest, m, n); m = -1; break; } m = read(fd, buf2, sizeof buf2); alarm(0); if(m < 0){ syslog(0, AUTHLOG, "%s rpc read err %d: %r", dest, m); break; // failure } if(m == 0 || buf2[1] != buf[1]){ // need matching ID syslog(0, AUTHLOG, "%s unmatched reply %d", dest, m); continue; } if(authcmp(shared, buf2, m, buf+4) == 0) break; syslog(0, AUTHLOG, "%s bad rpc chksum", dest); } close(fd); if(m <= 0) return nil; // unmarshal reply b = buf2; e = buf2+m; resp = (Packet*)malloc(sizeof(*resp)); if(resp == nil) return nil; resp->code = *b++; resp->ID = *b++; n = *b++; n = (n<<8) | *b++; if(m != n){ syslog(0, AUTHLOG, "rpc got %d bytes, length said %d", m, n); if(m > n) e = buf2+n; } memmove(resp->authenticator, b, 16); b += 16; a = &resp->first; a->type = 0; for(;;){ if(b >= e){ a->next = nil; break; // exit loop } a->type = *b++; a->len = (*b++) - 2; if(b + a->len > e){ // corrupt packet a->next = nil; freePacket(resp); return nil; } memmove(a->val, b, a->len); b += a->len; if(b < e){ // any more attributes? a->next = (Attribute*)malloc(sizeof(*a)); if(a->next == nil){ free(req); return nil; } a = a->next; } } return resp;}intsetAttribute(Packet *p, uchar type, uchar *s, int n){ Attribute *a; a = &p->first; if(a->type != 0){ a = (Attribute*)malloc(sizeof(*a)); if(a == nil) return -1; a->next = p->first.next; p->first.next = a; } a->type = type; a->len = n; if(a->len > 253 ) // RFC2138, section 5 a->len = 253; memmove(a->val, s, a->len); return 0;}/* return a reply message attribute string */char*replymsg(Packet *p){ Attribute *a; static char buf[255]; for(a = &p->first; a; a = a->next){ if(a->type == R_ReplyMessage){ if(a->len >= sizeof buf) a->len = sizeof(buf)-1; memmove(buf, a->val, a->len); buf[a->len] = 0; } } return buf;}/* for convenience while debugging */char *replymess;Attribute *stateattr;voidlogPacket(Packet *p){ Attribute *a; char buf[255]; char pbuf[4*1024]; uchar *au = p->authenticator; int i; char *np, *e; e = pbuf + sizeof(pbuf); np = seprint(pbuf, e, "Packet ID=%d auth=%x %x %x... ", p->ID, au[0], au[1], au[2]); switch(p->code){ case R_AccessRequest: np = seprint(np, e, "request\n"); break; case R_AccessAccept: np = seprint(np, e, "accept\n"); break; case R_AccessReject: np = seprint(np, e, "reject\n"); break; case R_AccessChallenge: np = seprint(np, e, "challenge\n"); break; default: np = seprint(np, e, "code=%d\n", p->code); break; } replymess = "0000000"; for(a = &p->first; a; a = a->next){ if(a->len > 253 ) a->len = 253; memmove(buf, a->val, a->len); np = seprint(np, e, " [%d]", a->type); for(i = 0; i<a->len; i++) if(isprint(a->val[i])) np = seprint(np, e, "%c", a->val[i]); else np = seprint(np, e, "\\%o", a->val[i]); np = seprint(np, e, "\n"); buf[a->len] = 0; if(a->type == R_ReplyMessage) replymess = strdup(buf); else if(a->type == R_State) stateattr = a; } syslog(0, AUTHLOG, "%s", pbuf);}static uchar*getipv4addr(void){ Ipifc *nifc; Iplifc *lifc; static Ipifc *ifc; ifc = readipifc("/net", ifc, -1); for(nifc = ifc; nifc; nifc = nifc->next) for(lifc = nifc->lifc; lifc; lifc = lifc->next) if(ipcmp(lifc->ip, IPnoaddr) != 0 && ipcmp(lifc->ip, v4prefix) != 0) return lifc->ip; return nil;}extern Ndb *db;/* returns 0 on success, error message on failure */char*secureidcheck(char *user, char *response){ Packet *req = nil, *resp = nil; ulong u[4]; uchar x[16]; char *radiussecret; char ruser[ 64]; char dest[3*IPaddrlen+20]; Secret shared, pass; char *rv = "authentication failed"; Ndbs s; Ndbtuple *t, *nt, *tt; uchar *ip; static Ndb *netdb; if(netdb == nil) netdb = ndbopen(0); /* bad responses make them disable the fob, avoid silly checks */ if(strlen(response) < 4 || strpbrk(response,"abcdefABCDEF") != nil) goto out; /* get radius secret */ radiussecret = ndbgetvalue(db, &s, "radius", "lra-radius", "secret", &t); if(radiussecret == nil){ syslog(0, AUTHLOG, "secureidcheck: nil radius secret: %r"); goto out; } /* translate user name if we have to */ strcpy(ruser, user); for(nt = t; nt; nt = nt->entry){ if(strcmp(nt->attr, "uid") == 0 && strcmp(nt->val, user) == 0) for(tt = nt->line; tt != nt; tt = tt->line) if(strcmp(tt->attr, "rid") == 0){ strcpy(ruser, tt->val); break; } } ndbfree(t); u[0] = fastrand(); u[1] = fastrand(); u[2] = fastrand(); u[3] = fastrand(); req = newRequest((uchar*)u); if(req == nil) goto out; shared.s = (uchar*)radiussecret; shared.len = strlen(radiussecret); ip = getipv4addr(); if(ip == nil){ syslog(0, AUTHLOG, "no interfaces: %r\n"); goto out; } if(setAttribute(req, R_NASIPAddress, ip + IPv4off, 4) < 0) goto out; if(setAttribute(req, R_UserName, (uchar*)ruser, strlen(ruser)) < 0) goto out; pass.s = (uchar*)response; pass.len = strlen(response); hide(&shared, req->authenticator, &pass, x); if(setAttribute(req, R_UserPassword, x, 16) < 0) goto out; t = ndbsearch(netdb, &s, "sys", "lra-radius"); if(t == nil){ syslog(0, AUTHLOG, "secureidcheck: nil radius sys search: %r\n"); goto out; } for(nt = t; nt; nt = nt->entry){ if(strcmp(nt->attr, "ip") != 0) continue; snprint(dest,sizeof dest,"udp!%s!oradius", nt->val); resp = rpc(dest, &shared, req); if(resp == nil){ syslog(0, AUTHLOG, "%s nil response", dest); continue; } if(resp->ID != req->ID){ syslog(0, AUTHLOG, "%s mismatched ID req=%d resp=%d", dest, req->ID, resp->ID); freePacket(resp); resp = nil; continue; } switch(resp->code){ case R_AccessAccept: syslog(0, AUTHLOG, "%s accepted ruser=%s", dest, ruser); rv = nil; break; case R_AccessReject: syslog(0, AUTHLOG, "%s rejected ruser=%s %s", dest, ruser, replymsg(resp)); rv = "secureid failed"; break; case R_AccessChallenge: syslog(0, AUTHLOG, "%s challenge ruser=%s %s", dest, ruser, replymsg(resp)); rv = "secureid out of sync"; break; default: syslog(0, AUTHLOG, "%s code=%d ruser=%s %s", dest, resp->code, ruser, replymsg(resp)); break; } break; // we have a proper reply, no need to ask again } ndbfree(t); free(radiussecret);out: freePacket(req); freePacket(resp); return rv;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -