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