plan9port

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

p9cr.c (6981B)


      1 /*
      2  * p9cr - one-sided challenge/response authentication
      3  *
      4  * Protocol:
      5  *
      6  *	C -> S: user
      7  *	S -> C: challenge
      8  *	C -> S: response
      9  *	S -> C: ok or bad
     10  *
     11  * Note that this is the protocol between factotum and the local
     12  * program, not between the two factotums.  The information
     13  * exchanged here is wrapped in other protocols by the local
     14  * programs.
     15  */
     16 
     17 #include "std.h"
     18 #include "dat.h"
     19 
     20 /* shared with auth dialing routines */
     21 typedef struct ServerState ServerState;
     22 struct ServerState
     23 {
     24 	int asfd;
     25 	Key *k;
     26 	Ticketreq tr;
     27 	Ticket t;
     28 	char *dom;
     29 	char *hostid;
     30 };
     31 
     32 enum
     33 {
     34 	MAXCHAL = 64,
     35 	MAXRESP = 64,
     36 };
     37 
     38 extern Proto p9cr, vnc;
     39 static int p9response(char*, uchar*, uchar*);
     40 // static int vncresponse(char*, uchar*, uchar*);
     41 static int p9crchal(ServerState *s, int, char*, uchar*, int);
     42 static int p9crresp(ServerState*, uchar*, int);
     43 
     44 static int
     45 p9crcheck(Key *k)
     46 {
     47 	if(!strfindattr(k->attr, "user") || !strfindattr(k->privattr, "!password")){
     48 		werrstr("need user and !password attributes");
     49 		return -1;
     50 	}
     51 	return 0;
     52 }
     53 
     54 static int
     55 p9crclient(Conv *c)
     56 {
     57 	char *pw, *res, *user;
     58 	int astype, challen, resplen, ntry, ret;
     59 	Attr *attr;
     60 	Key *k;
     61 	uchar chal[MAXCHAL+1], resp[MAXRESP];
     62 	int (*response)(char*, uchar*, uchar*);
     63 
     64 	k = nil;
     65 	res = nil;
     66 	ret = -1;
     67 	attr = c->attr;
     68 	astype = -1;
     69 
     70 	if(c->proto == &p9cr){
     71 		astype = AuthChal;
     72 		challen = NETCHLEN;
     73 		response = p9response;
     74 		attr = _mkattr(AttrNameval, "proto", "p9sk1", _delattr(_copyattr(attr), "proto"));
     75 	}else if(c->proto == &vnc){
     76 		astype = AuthVNC;
     77 		challen = MAXCHAL;
     78 	//	response = vncresponse;
     79 		werrstr("no vnc");
     80 		goto out;
     81 	}else{
     82 		werrstr("bad proto");
     83 		goto out;
     84 	}
     85 
     86 	c->state = "find key";
     87 	k = keyfetch(c, "%A %s", attr, c->proto->keyprompt);
     88 	if(k == nil)
     89 		goto out;
     90 
     91 	for(ntry=1;; ntry++){
     92 		if(c->attr != attr)
     93 			freeattr(c->attr);
     94 		c->attr = addattrs(copyattr(attr), k->attr);
     95 
     96 		if((pw = strfindattr(k->privattr, "!password")) == nil){
     97 			werrstr("key has no !password (cannot happen)");
     98 			goto out;
     99 		}
    100 		if((user = strfindattr(k->attr, "user")) == nil){
    101 			werrstr("key has no user (cannot happen)");
    102 			goto out;
    103 		}
    104 
    105 		if(convprint(c, "%s", user) < 0)
    106 			goto out;
    107 
    108 		if(convread(c, chal, challen) < 0)
    109 			goto out;
    110 		chal[challen] = 0;
    111 
    112 		if((resplen = (*response)(pw, chal, resp)) < 0)
    113 			goto out;
    114 
    115 		if(convwrite(c, resp, resplen) < 0)
    116 			goto out;
    117 
    118 		if(convreadm(c, &res) < 0)
    119 			goto out;
    120 
    121 		if(strcmp(res, "ok") == 0)
    122 			break;
    123 
    124 		if((k = keyreplace(c, k, "%s", res)) == nil){
    125 			c->state = "auth failed";
    126 			werrstr("%s", res);
    127 			goto out;
    128 		}
    129 	}
    130 
    131 	werrstr("succeeded");
    132 	ret = 0;
    133 
    134 out:
    135 	USED(astype);
    136 	keyclose(k);
    137 	if(c->attr != attr)
    138 		freeattr(attr);
    139 	return ret;
    140 }
    141 
    142 static int
    143 p9crserver(Conv *c)
    144 {
    145 	uchar chal[MAXCHAL], *resp, *resp1;
    146 	char *user;
    147 	ServerState s;
    148 	int astype, ret, challen, resplen;
    149 	Attr *a;
    150 
    151 	ret = -1;
    152 	user = nil;
    153 	resp = nil;
    154 	memset(&s, 0, sizeof s);
    155 	s.asfd = -1;
    156 
    157 	if(c->proto == &p9cr){
    158 		astype = AuthChal;
    159 		challen = NETCHLEN;
    160 	}else if(c->proto == &vnc){
    161 		challen = MAXCHAL;
    162 	}else{
    163 		werrstr("bad proto");
    164 		goto out;
    165 	}
    166 
    167 	c->state = "find key";
    168 	if((s.k = plan9authkey(c->attr)) == nil)
    169 		goto out;
    170 
    171 	a = copyattr(s.k->attr);
    172 	a = delattr(a, "proto");
    173 	c->attr = addattrs(c->attr, a);
    174 	freeattr(a);
    175 
    176 	c->state = "authdial";
    177 	s.hostid = strfindattr(s.k->attr, "user");
    178 	s.dom = strfindattr(s.k->attr, "dom");
    179 	if((s.asfd = xioauthdial(nil, s.dom)) < 0){
    180 		werrstr("authdial %s: %r", s.dom);
    181 		goto out;
    182 	}
    183 
    184 	for(;;){
    185 		c->state = "read user";
    186 		if(convreadm(c, &user) < 0)
    187 			goto out;
    188 
    189 		c->state = "authchal";
    190 		if(p9crchal(&s, astype, user, chal, challen) < 0)
    191 			goto out;
    192 
    193 		c->state = "write challenge";
    194 		if(convwrite(c, chal, challen) < 0)
    195 			goto out;
    196 
    197 		c->state = "read response";
    198 		if((resplen = convreadm(c, (char**)(void*)&resp)) < 0)
    199 			goto out;
    200 		if(c->proto == &p9cr){
    201 			if(resplen > NETCHLEN){
    202 				convprint(c, "bad response too long");
    203 				goto out;
    204 			}
    205 			resp1 = emalloc(NETCHLEN);
    206 			memset(resp1, 0, NETCHLEN);
    207 			memmove(resp1, resp, resplen);
    208 			free(resp);
    209 			resp = resp1;
    210 			resplen = NETCHLEN;
    211 		}
    212 
    213 		c->state = "authwrite";
    214 		switch(p9crresp(&s, resp, resplen)){
    215 		case -1:
    216 			fprint(2, "p9crresp: %r\n");
    217 			goto out;
    218 		case 0:
    219 			c->state = "write status";
    220 			if(convprint(c, "bad authentication failed") < 0)
    221 				goto out;
    222 			break;
    223 		case 1:
    224 			c->state = "write status";
    225 			if(convprint(c, "ok") < 0)
    226 				goto out;
    227 			goto ok;
    228 		}
    229 		free(user);
    230 		free(resp);
    231 		user = nil;
    232 		resp = nil;
    233 	}
    234 
    235 ok:
    236 	ret = 0;
    237 	c->attr = addcap(c->attr, c->sysuser, &s.t);
    238 
    239 out:
    240 	keyclose(s.k);
    241 	free(user);
    242 	free(resp);
    243 	xioclose(s.asfd);
    244 	return ret;
    245 }
    246 
    247 static int
    248 p9crchal(ServerState *s, int astype, char *user, uchar *chal, int challen)
    249 {
    250 	char trbuf[TICKREQLEN];
    251 	Ticketreq tr;
    252 	int n;
    253 
    254 	memset(&tr, 0, sizeof tr);
    255 
    256 	tr.type = astype;
    257 
    258 	if(strlen(s->hostid) >= sizeof tr.hostid){
    259 		werrstr("hostid too long");
    260 		return -1;
    261 	}
    262 	strcpy(tr.hostid, s->hostid);
    263 
    264 	if(strlen(s->dom) >= sizeof tr.authdom){
    265 		werrstr("domain too long");
    266 		return -1;
    267 	}
    268 	strcpy(tr.authdom, s->dom);
    269 
    270 	if(strlen(user) >= sizeof tr.uid){
    271 		werrstr("user name too long");
    272 		return -1;
    273 	}
    274 	strcpy(tr.uid, user);
    275 	convTR2M(&tr, trbuf);
    276 
    277 	if(xiowrite(s->asfd, trbuf, TICKREQLEN) != TICKREQLEN)
    278 		return -1;
    279 
    280 	if((n=xioasrdresp(s->asfd, chal, challen)) <= 0)
    281 		return -1;
    282 	return n;
    283 }
    284 
    285 static int
    286 p9crresp(ServerState *s, uchar *resp, int resplen)
    287 {
    288 	char tabuf[TICKETLEN+AUTHENTLEN];
    289 	Authenticator a;
    290 	Ticket t;
    291 	Ticketreq tr;
    292 
    293 	memset(&tr, 0, sizeof tr);	// TODO: what should tr be initialized to?
    294 
    295 	if(xiowrite(s->asfd, resp, resplen) != resplen)
    296 		return -1;
    297 
    298 	if(xioasrdresp(s->asfd, tabuf, TICKETLEN+AUTHENTLEN) != TICKETLEN+AUTHENTLEN)
    299 		return 0;
    300 
    301 	convM2T(tabuf, &t, s->k->priv);
    302 	if(t.num != AuthTs
    303 	|| memcmp(t.chal, tr.chal, sizeof tr.chal) != 0){
    304 		werrstr("key mismatch with auth server");
    305 		return -1;
    306 	}
    307 
    308 	convM2A(tabuf+TICKETLEN, &a, t.key);
    309 	if(a.num != AuthAc
    310 	|| memcmp(a.chal, tr.chal, sizeof a.chal) != 0
    311 	|| a.id != 0){
    312 		werrstr("key2 mismatch with auth server");
    313 		return -1;
    314 	}
    315 
    316 	s->t = t;
    317 	return 1;
    318 }
    319 
    320 static int
    321 p9response(char *pw, uchar *chal, uchar *resp)
    322 {
    323 	char key[DESKEYLEN];
    324 	uchar buf[8];
    325 	ulong x;
    326 
    327 	passtokey(key, pw);
    328 	memset(buf, 0, 8);
    329 	snprint((char*)buf, sizeof buf, "%d", atoi((char*)chal));
    330 	if(encrypt(key, buf, 8) < 0){
    331 		werrstr("can't encrypt response");
    332 		return -1;
    333 	}
    334 	x = (buf[0]<<24)+(buf[1]<<16)+(buf[2]<<8)+buf[3];
    335 	return snprint((char*)resp, MAXRESP, "%.8lux", x);
    336 }
    337 
    338 /*
    339 static int
    340 vncresponse(char *pw, uchar *chal, uchar *resp)
    341 {
    342 	DESstate des;
    343 
    344 	memmove(resp, chal, MAXCHAL);
    345 	setupDESstate(&des, 0, nil);  // XXX put key in for 0
    346 	desECBencrypt(resp, MAXCHAL, &des);
    347 	return MAXCHAL;
    348 }
    349 */
    350 
    351 static Role
    352 p9crroles[] =
    353 {
    354 	"client", p9crclient,
    355 	"server", p9crserver,
    356 	0
    357 };
    358 
    359 Proto p9cr = {
    360 	"p9cr",
    361 	p9crroles,
    362 	"user? !password?",
    363 	p9crcheck,
    364 	nil
    365 };
    366 
    367 /* still need to implement vnc key generator */
    368 Proto vnc = {
    369 	"vnc",
    370 	p9crroles,
    371 	"user? !password?",
    372 	p9crcheck,
    373 	nil
    374 };