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