plan9port

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

p9sk1.c (7389B)


      1 /*
      2  * p9sk1, p9sk2 - Plan 9 secret (private) key authentication.
      3  * p9sk2 is an incomplete flawed variant of p9sk1.
      4  *
      5  * Client protocol:
      6  *	write challenge[challen]	(p9sk1 only)
      7  *	read tickreq[tickreqlen]
      8  *	write ticket[ticketlen]
      9  *	read authenticator[authentlen]
     10  *
     11  * Server protocol:
     12  * 	read challenge[challen]	(p9sk1 only)
     13  *	write tickreq[tickreqlen]
     14  *	read ticket[ticketlen]
     15  *	write authenticator[authentlen]
     16  */
     17 
     18 #include "std.h"
     19 #include "dat.h"
     20 
     21 extern Proto p9sk1, p9sk2;
     22 static int gettickets(Ticketreq*, char*, Key*);
     23 
     24 #define max(a, b) ((a) > (b) ? (a) : (b))
     25 enum
     26 {
     27 	MAXAUTH = max(TICKREQLEN, TICKETLEN+max(TICKETLEN, AUTHENTLEN))
     28 };
     29 
     30 static int
     31 p9skclient(Conv *c)
     32 {
     33 	char *user;
     34 	char cchal[CHALLEN];
     35 	uchar secret[8];
     36 	char buf[MAXAUTH];
     37 	int speakfor, ret;
     38 	Attr *a;
     39 	Authenticator au;
     40 	Key *k;
     41 	Ticket t;
     42 	Ticketreq tr;
     43 
     44 	ret = -1;
     45 	a = nil;
     46 	k = nil;
     47 
     48 	/* p9sk1: send client challenge */
     49 	if(c->proto == &p9sk1){
     50 		c->state = "write challenge";
     51 		memrandom(cchal, CHALLEN);
     52 		if(convwrite(c, cchal, CHALLEN) < 0)
     53 			goto out;
     54 	}
     55 
     56 	/* read ticket request */
     57 	c->state = "read tickreq";
     58 	if(convread(c, buf, TICKREQLEN) < 0)
     59 		goto out;
     60 	convM2TR(buf, &tr);
     61 
     62 	/* p9sk2: use server challenge as client challenge */
     63 	if(c->proto == &p9sk2)
     64 		memmove(cchal, tr.chal, CHALLEN);
     65 
     66 	/*
     67 	 * find a key.
     68 	 *
     69 	 * if the user is the factotum owner, any key will do.
     70 	 * if not, then if we have a speakfor key,
     71 	 * we will only vouch for the user's local identity.
     72 	 *
     73 	 * this logic is duplicated in p9any.c
     74 	 */
     75 	user = strfindattr(c->attr, "user");
     76 	a = delattr(copyattr(c->attr), "role");
     77 	a = addattr(a, "proto=p9sk1");
     78 
     79 	if(strcmp(c->sysuser, owner) == 0){
     80 		speakfor = 0;
     81 		a = addattr(a, "proto=p9sk1 user? dom=%q", tr.authdom);
     82 	}else if(user==nil || strcmp(c->sysuser, user)==0){
     83 		speakfor = 1;
     84 		a = delattr(a, "user");
     85 		a = addattr(a, "proto=p9sk1 user? dom=%q role=speakfor", tr.authdom);
     86 	}else{
     87 		werrstr("will not authenticate for %q as %q", c->sysuser, user);
     88 		goto out;
     89 	}
     90 
     91 	for(;;){
     92 		c->state = "find key";
     93 		k = keyfetch(c, "%A", a);
     94 		if(k == nil)
     95 			goto out;
     96 
     97 		/* relay ticket request to auth server, get tickets */
     98 		strcpy(tr.hostid, strfindattr(k->attr, "user"));
     99 		if(speakfor)
    100 			strcpy(tr.uid, c->sysuser);
    101 		else
    102 			strcpy(tr.uid, tr.hostid);
    103 
    104 		c->state = "get tickets";
    105 		if(gettickets(&tr, buf, k) < 0)
    106 			goto out;
    107 
    108 		convM2T(buf, &t, k->priv);
    109 		if(t.num == AuthTc)
    110 			break;
    111 
    112 		/* we don't agree with the auth server about the key; try again */
    113 		c->state = "replace key";
    114 		if((k = keyreplace(c, k, "key mismatch with auth server")) == nil){
    115 			werrstr("key mismatch with auth server");
    116 			goto out;
    117 		}
    118 	}
    119 
    120 	/* send second ticket and authenticator to server */
    121 	c->state = "write ticket+auth";
    122 	memmove(buf, buf+TICKETLEN, TICKETLEN);
    123 	au.num = AuthAc;
    124 	memmove(au.chal, tr.chal, CHALLEN);
    125 	au.id = 0;
    126 	convA2M(&au, buf+TICKETLEN, t.key);
    127 	if(convwrite(c, buf, TICKETLEN+AUTHENTLEN) < 0)
    128 		goto out;
    129 
    130 	/* read authenticator from server */
    131 	c->state = "read auth";
    132 	if(convread(c, buf, AUTHENTLEN) < 0)
    133 		goto out;
    134 	convM2A(buf, &au, t.key);
    135 	if(au.num != AuthAs || memcmp(au.chal, cchal, CHALLEN) != 0 || au.id != 0){
    136 		werrstr("server lies through his teeth");
    137 		goto out;
    138 	}
    139 
    140 	/* success */
    141 	c->attr = addcap(c->attr, c->sysuser, &t);
    142 	flog("p9skclient success %A", c->attr);	/* before adding secret! */
    143 	des56to64((uchar*)t.key, secret);
    144 	c->attr = addattr(c->attr, "secret=%.8H", secret);
    145 	ret = 0;
    146 
    147 out:
    148 	if(ret < 0)
    149 		flog("p9skclient: %r");
    150 	freeattr(a);
    151 	keyclose(k);
    152 	return ret;
    153 }
    154 
    155 static int
    156 p9skserver(Conv *c)
    157 {
    158 	char cchal[CHALLEN], buf[MAXAUTH];
    159 	uchar secret[8];
    160 	int ret;
    161 	Attr *a;
    162 	Authenticator au;
    163 	Key *k;
    164 	Ticketreq tr;
    165 	Ticket t;
    166 
    167 	ret = -1;
    168 
    169 	a = addattr(copyattr(c->attr), "user? dom?");
    170 	a = addattr(a, "user? dom? proto=p9sk1");
    171 	if((k = keyfetch(c, "%A", a)) == nil)
    172 		goto out;
    173 
    174 	/* p9sk1: read client challenge */
    175 	if(c->proto == &p9sk1){
    176 		if(convread(c, cchal, CHALLEN) < 0)
    177 			goto out;
    178 	}
    179 
    180 	/* send ticket request */
    181 	memset(&tr, 0, sizeof tr);
    182 	tr.type = AuthTreq;
    183 	strcpy(tr.authid, strfindattr(k->attr, "user"));
    184 	strcpy(tr.authdom, strfindattr(k->attr, "dom"));
    185 	memrandom(tr.chal, sizeof tr.chal);
    186 	convTR2M(&tr, buf);
    187 	if(convwrite(c, buf, TICKREQLEN) < 0)
    188 		goto out;
    189 
    190 	/* p9sk2: use server challenge as client challenge */
    191 	if(c->proto == &p9sk2)
    192 		memmove(cchal, tr.chal, sizeof tr.chal);
    193 
    194 	/* read ticket+authenticator */
    195 	if(convread(c, buf, TICKETLEN+AUTHENTLEN) < 0)
    196 		goto out;
    197 
    198 	convM2T(buf, &t, k->priv);
    199 	if(t.num != AuthTs || memcmp(t.chal, tr.chal, CHALLEN) != 0){
    200 		/* BUG badkey */
    201 		werrstr("key mismatch with auth server");
    202 		goto out;
    203 	}
    204 
    205 	convM2A(buf+TICKETLEN, &au, t.key);
    206 	if(au.num != AuthAc || memcmp(au.chal, tr.chal, CHALLEN) != 0 || au.id != 0){
    207 		werrstr("client lies through his teeth");
    208 		goto out;
    209 	}
    210 
    211 	/* send authenticator */
    212 	au.num = AuthAs;
    213 	memmove(au.chal, cchal, CHALLEN);
    214 	convA2M(&au, buf, t.key);
    215 	if(convwrite(c, buf, AUTHENTLEN) < 0)
    216 		goto out;
    217 
    218 	/* success */
    219 	c->attr = addcap(c->attr, c->sysuser, &t);
    220 	flog("p9skserver success %A", c->attr);	/* before adding secret! */
    221 	des56to64((uchar*)t.key, secret);
    222 	c->attr = addattr(c->attr, "secret=%.8H", secret);
    223 	ret = 0;
    224 
    225 out:
    226 	if(ret < 0)
    227 		flog("p9skserver: %r");
    228 	freeattr(a);
    229 	keyclose(k);
    230 	return ret;
    231 }
    232 
    233 int
    234 _asgetticket(int fd, char *trbuf, char *tbuf)
    235 {
    236 	if(write(fd, trbuf, TICKREQLEN) < 0){
    237 		close(fd);
    238 		return -1;
    239 	}
    240 	return _asrdresp(fd, tbuf, 2*TICKETLEN);
    241 }
    242 static int
    243 getastickets(Ticketreq *tr, char *buf)
    244 {
    245 	int asfd;
    246 	int ret;
    247 
    248 	if((asfd = xioauthdial(nil, tr->authdom)) < 0)
    249 		return -1;
    250 	convTR2M(tr, buf);
    251 	ret = xioasgetticket(asfd, buf, buf);
    252 	xioclose(asfd);
    253 	return ret;
    254 }
    255 
    256 static int
    257 mktickets(Ticketreq *tr, char *buf, Key *k)
    258 {
    259 	Ticket t;
    260 
    261 	if(strcmp(tr->authid, tr->hostid) != 0)
    262 		return -1;
    263 
    264 	memset(&t, 0, sizeof t);
    265 	memmove(t.chal, tr->chal, CHALLEN);
    266 	strcpy(t.cuid, tr->uid);
    267 	strcpy(t.suid, tr->uid);
    268 	memrandom(t.key, DESKEYLEN);
    269 	t.num = AuthTc;
    270 	convT2M(&t, buf, k->priv);
    271 	t.num = AuthTs;
    272 	convT2M(&t, buf+TICKETLEN, k->priv);
    273 	return 0;
    274 }
    275 
    276 static int
    277 gettickets(Ticketreq *tr, char *buf, Key *k)
    278 {
    279 	if(getastickets(tr, buf) == 0)
    280 		return 0;
    281 	if(mktickets(tr, buf, k) == 0)
    282 		return 0;
    283 	werrstr("gettickets: %r");
    284 	return -1;
    285 }
    286 
    287 static int
    288 p9sk1check(Key *k)
    289 {
    290 	char *user, *dom, *pass;
    291 	Ticketreq tr;
    292 
    293 	user = strfindattr(k->attr, "user");
    294 	dom = strfindattr(k->attr, "dom");
    295 	if(user==nil || dom==nil){
    296 		werrstr("need user and dom attributes");
    297 		return -1;
    298 	}
    299 	if(strlen(user) >= sizeof tr.authid){
    300 		werrstr("user name too long");
    301 		return -1;
    302 	}
    303 	if(strlen(dom) >= sizeof tr.authdom){
    304 		werrstr("auth dom name too long");
    305 		return -1;
    306 	}
    307 
    308 	k->priv = emalloc(DESKEYLEN);
    309 	if(pass = strfindattr(k->privattr, "!password"))
    310 		passtokey(k->priv, pass);
    311 	else if(pass = strfindattr(k->privattr, "!hex")){
    312 		if(hexparse(pass, k->priv, 7) < 0){
    313 			werrstr("malformed !hex key data");
    314 			return -1;
    315 		}
    316 	}else{
    317 		werrstr("need !password or !hex attribute");
    318 		return -1;
    319 	}
    320 
    321 	return 0;
    322 }
    323 
    324 static void
    325 p9sk1close(Key *k)
    326 {
    327 	free(k->priv);
    328 	k->priv = nil;
    329 }
    330 
    331 static Role
    332 p9sk1roles[] =
    333 {
    334 	"client",	p9skclient,
    335 	"server",	p9skserver,
    336 	0
    337 };
    338 
    339 static Role
    340 p9sk2roles[] =
    341 {
    342 	"client",	p9skclient,
    343 	"server",	p9skserver,
    344 	0
    345 };
    346 
    347 Proto p9sk1 = {
    348 	"p9sk1",
    349 	p9sk1roles,
    350 	"user? dom? !password?",
    351 	p9sk1check,
    352 	p9sk1close
    353 };
    354 
    355 Proto p9sk2 = {
    356 	"p9sk2",
    357 	p9sk2roles
    358 };