Blob


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"
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 };
24 typedef struct Secret{
25 uchar *s;
26 int len;
27 } Secret;
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;
36 typedef struct Packet{
37 uchar code, ID;
38 uchar authenticator[16];
39 Attribute first;
40 } Packet;
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;
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 }
57 int
58 authcmp(Secret *shared, uchar *buf, int m, uchar *auth)
59 {
60 DigestState *M;
61 uchar x[16];
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 }
70 Packet*
71 newRequest(uchar *auth)
72 {
73 static uchar ID = 0;
74 Packet *p;
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 }
87 void
88 freePacket(Packet *p)
89 {
90 Attribute *a, *x;
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);
103 int
104 ding(void *v, char *msg)
106 USED(v);
107 /* syslog(0, AUTHLOG, "ding %s", msg); */
108 if(strstr(msg, "alarm"))
109 return 1;
110 return 0;
113 Packet *
114 rpc(char *dest, Secret *shared, Packet *req)
116 uchar buf[4096], buf2[4096], *b, *e;
117 Packet *resp;
118 Attribute *a;
119 int m, n, fd, try;
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;
135 n = b-buf;
136 buf[2] = n>>8;
137 buf[3] = n;
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;
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;
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
161 if(m == 0 || buf2[1] != buf[1]){ // need matching ID
162 syslog(0, AUTHLOG, "%s unmatched reply %d", dest, m);
163 continue;
165 if(authcmp(shared, buf2, m, buf+4) == 0)
166 break;
167 syslog(0, AUTHLOG, "%s bad rpc chksum", dest);
169 close(fd);
170 if(m <= 0)
171 return nil;
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;
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
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;
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;
212 a = a->next;
215 return resp;
218 int
219 setAttribute(Packet *p, uchar type, uchar *s, int n)
221 Attribute *a;
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;
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;
239 /* return a reply message attribute string */
240 char*
241 replymsg(Packet *p)
243 Attribute *a;
244 static char buf[255];
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;
254 return buf;
257 /* for convenience while debugging */
258 char *replymess;
259 Attribute *stateattr;
261 void
262 logPacket(Packet *p)
264 Attribute *a;
265 char buf[255];
266 char pbuf[4*1024];
267 uchar *au = p->authenticator;
268 int i;
269 char *np, *e;
271 e = pbuf + sizeof(pbuf);
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;
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;
310 syslog(0, AUTHLOG, "%s", pbuf);
313 static uchar*
314 getipv4addr(void)
316 Ipifc *nifc;
317 Iplifc *lifc;
318 static Ipifc *ifc;
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;
328 extern Ndb *db;
330 /* returns 0 on success, error message on failure */
331 char*
332 secureidcheck(char *user, char *response)
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;
347 if(netdb == nil)
348 netdb = ndbopen(0);
350 /* bad responses make them disable the fob, avoid silly checks */
351 if(strlen(response) < 4 || strpbrk(response,"abcdefABCDEF") != nil)
352 goto out;
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;
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;
371 ndbfree(t);
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;
387 if(setAttribute(req, R_NASIPAddress, ip + IPv4off, 4) < 0)
388 goto out;
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;
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;
403 for(nt = t; nt; nt = nt->entry){
404 if(strcmp(nt->attr, "ip") != 0)
405 continue;
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;
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;
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;
438 break; // we have a proper reply, no need to ask again
440 ndbfree(t);
441 free(radiussecret);
442 out:
443 freePacket(req);
444 freePacket(resp);
445 return rv;