plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

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 }