Warning: Attempt to read property "date" on null in /usr/local/www/websvn.planix.org/blame.php on line 247

Warning: Attempt to read property "msg" on null in /usr/local/www/websvn.planix.org/blame.php on line 247
WebSVN – planix.SVN – Blame – /os/branches/feature-vt/sys/src/cmd/auth/secureidcheck.c – Rev 2

Subversion Repositories planix.SVN

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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
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
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*
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
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
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 *
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
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*
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
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*
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*
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
}