19a747e4fSDavid du Colombier /*
29a747e4fSDavid du Colombier * p9cr, vnc - textual challenge/response authentication
39a747e4fSDavid du Colombier *
49a747e4fSDavid du Colombier * Client protocol: [currently unimplemented]
59a747e4fSDavid du Colombier * write challenge
69a747e4fSDavid du Colombier * read response
79a747e4fSDavid du Colombier *
89a747e4fSDavid du Colombier * Server protocol:
99a747e4fSDavid du Colombier * write user
109a747e4fSDavid du Colombier * read challenge
119a747e4fSDavid du Colombier * write response
129a747e4fSDavid du Colombier */
139a747e4fSDavid du Colombier
149a747e4fSDavid du Colombier #include "dat.h"
159a747e4fSDavid du Colombier
169a747e4fSDavid du Colombier enum
179a747e4fSDavid du Colombier {
189a747e4fSDavid du Colombier Maxchal= 64,
199a747e4fSDavid du Colombier };
209a747e4fSDavid du Colombier
219a747e4fSDavid du Colombier typedef struct State State;
229a747e4fSDavid du Colombier struct State
239a747e4fSDavid du Colombier {
249a747e4fSDavid du Colombier Key *key;
259a747e4fSDavid du Colombier int astype;
269a747e4fSDavid du Colombier int asfd;
279a747e4fSDavid du Colombier Ticket t;
289a747e4fSDavid du Colombier Ticketreq tr;
299a747e4fSDavid du Colombier char chal[Maxchal];
309a747e4fSDavid du Colombier int challen;
31b7b24591SDavid du Colombier char resp[Maxchal];
32b7b24591SDavid du Colombier int resplen;
339a747e4fSDavid du Colombier };
349a747e4fSDavid du Colombier
359a747e4fSDavid du Colombier enum
369a747e4fSDavid du Colombier {
379a747e4fSDavid du Colombier CNeedChal,
389a747e4fSDavid du Colombier CHaveResp,
399a747e4fSDavid du Colombier
409a747e4fSDavid du Colombier SHaveChal,
419a747e4fSDavid du Colombier SNeedResp,
429a747e4fSDavid du Colombier
439a747e4fSDavid du Colombier Maxphase,
449a747e4fSDavid du Colombier };
459a747e4fSDavid du Colombier
469a747e4fSDavid du Colombier static char *phasenames[Maxphase] =
479a747e4fSDavid du Colombier {
489a747e4fSDavid du Colombier [CNeedChal] "CNeedChal",
499a747e4fSDavid du Colombier [CHaveResp] "CHaveResp",
509a747e4fSDavid du Colombier
519a747e4fSDavid du Colombier [SHaveChal] "SHaveChal",
529a747e4fSDavid du Colombier [SNeedResp] "SNeedResp",
539a747e4fSDavid du Colombier };
549a747e4fSDavid du Colombier
559a747e4fSDavid du Colombier static void
p9crclose(Fsstate * fss)569a747e4fSDavid du Colombier p9crclose(Fsstate *fss)
579a747e4fSDavid du Colombier {
589a747e4fSDavid du Colombier State *s;
599a747e4fSDavid du Colombier
609a747e4fSDavid du Colombier s = fss->ps;
619a747e4fSDavid du Colombier if(s->asfd >= 0){
629a747e4fSDavid du Colombier close(s->asfd);
639a747e4fSDavid du Colombier s->asfd = -1;
649a747e4fSDavid du Colombier }
659a747e4fSDavid du Colombier free(s);
669a747e4fSDavid du Colombier }
679a747e4fSDavid du Colombier
689a747e4fSDavid du Colombier static int getchal(State*, Fsstate*);
699a747e4fSDavid du Colombier
709a747e4fSDavid du Colombier static int
p9crinit(Proto * p,Fsstate * fss)719a747e4fSDavid du Colombier p9crinit(Proto *p, Fsstate *fss)
729a747e4fSDavid du Colombier {
739a747e4fSDavid du Colombier int iscli, ret;
749a747e4fSDavid du Colombier char *user;
759a747e4fSDavid du Colombier State *s;
76b7b24591SDavid du Colombier Attr *attr;
77260f7b65SDavid du Colombier Keyinfo ki;
789a747e4fSDavid du Colombier
792ebbfa15SDavid du Colombier if((iscli = isclient(_strfindattr(fss->attr, "role"))) < 0)
809a747e4fSDavid du Colombier return failure(fss, nil);
819a747e4fSDavid du Colombier
829a747e4fSDavid du Colombier s = emalloc(sizeof(*s));
839a747e4fSDavid du Colombier s->asfd = -1;
849a747e4fSDavid du Colombier if(p == &p9cr){
859a747e4fSDavid du Colombier s->astype = AuthChal;
869a747e4fSDavid du Colombier s->challen = NETCHLEN;
879a747e4fSDavid du Colombier }else if(p == &vnc){
889a747e4fSDavid du Colombier s->astype = AuthVNC;
899a747e4fSDavid du Colombier s->challen = Maxchal;
909a747e4fSDavid du Colombier }else
919a747e4fSDavid du Colombier abort();
929a747e4fSDavid du Colombier
93b7b24591SDavid du Colombier if(iscli){
94b7b24591SDavid du Colombier fss->phase = CNeedChal;
95b7b24591SDavid du Colombier if(p == &p9cr)
96b7b24591SDavid du Colombier attr = setattr(_copyattr(fss->attr), "proto=p9sk1");
97b7b24591SDavid du Colombier else
98b7b24591SDavid du Colombier attr = nil;
99260f7b65SDavid du Colombier ret = findkey(&s->key, mkkeyinfo(&ki, fss, attr),
100b7b24591SDavid du Colombier "role=client %s", p->keyprompt);
101b7b24591SDavid du Colombier _freeattr(attr);
102b7b24591SDavid du Colombier if(ret != RpcOk){
103b7b24591SDavid du Colombier free(s);
104b7b24591SDavid du Colombier return ret;
105b7b24591SDavid du Colombier }
106b7b24591SDavid du Colombier fss->ps = s;
107b7b24591SDavid du Colombier }else{
1089a747e4fSDavid du Colombier if((ret = findp9authkey(&s->key, fss)) != RpcOk){
1099a747e4fSDavid du Colombier free(s);
1109a747e4fSDavid du Colombier return ret;
1119a747e4fSDavid du Colombier }
1122ebbfa15SDavid du Colombier if((user = _strfindattr(fss->attr, "user")) == nil){
1139a747e4fSDavid du Colombier free(s);
1149a747e4fSDavid du Colombier return failure(fss, "no user name specified in start msg");
1159a747e4fSDavid du Colombier }
1169a747e4fSDavid du Colombier if(strlen(user) >= sizeof s->tr.uid){
1179a747e4fSDavid du Colombier free(s);
1189a747e4fSDavid du Colombier return failure(fss, "user name too long");
1199a747e4fSDavid du Colombier }
1209a747e4fSDavid du Colombier fss->ps = s;
1219a747e4fSDavid du Colombier strcpy(s->tr.uid, user);
1229a747e4fSDavid du Colombier ret = getchal(s, fss);
1239a747e4fSDavid du Colombier if(ret != RpcOk){
1249a747e4fSDavid du Colombier p9crclose(fss); /* frees s */
1259a747e4fSDavid du Colombier fss->ps = nil;
1269a747e4fSDavid du Colombier }
127b7b24591SDavid du Colombier }
128b7b24591SDavid du Colombier fss->phasename = phasenames;
129b7b24591SDavid du Colombier fss->maxphase = Maxphase;
1309a747e4fSDavid du Colombier return ret;
1319a747e4fSDavid du Colombier }
1329a747e4fSDavid du Colombier
1339a747e4fSDavid du Colombier static int
p9crread(Fsstate * fss,void * va,uint * n)1349a747e4fSDavid du Colombier p9crread(Fsstate *fss, void *va, uint *n)
1359a747e4fSDavid du Colombier {
1369a747e4fSDavid du Colombier int m;
1379a747e4fSDavid du Colombier State *s;
1389a747e4fSDavid du Colombier
1399a747e4fSDavid du Colombier s = fss->ps;
1409a747e4fSDavid du Colombier switch(fss->phase){
1419a747e4fSDavid du Colombier default:
1429a747e4fSDavid du Colombier return phaseerror(fss, "read");
1439a747e4fSDavid du Colombier
144b7b24591SDavid du Colombier case CHaveResp:
145b7b24591SDavid du Colombier if(s->resplen < *n)
146b7b24591SDavid du Colombier *n = s->resplen;
147b7b24591SDavid du Colombier memmove(va, s->resp, *n);
148b7b24591SDavid du Colombier fss->phase = Established;
149b7b24591SDavid du Colombier return RpcOk;
150b7b24591SDavid du Colombier
1519a747e4fSDavid du Colombier case SHaveChal:
1529a747e4fSDavid du Colombier if(s->astype == AuthChal)
1539a747e4fSDavid du Colombier m = strlen(s->chal); /* ascii string */
1549a747e4fSDavid du Colombier else
1559a747e4fSDavid du Colombier m = s->challen; /* fixed length binary */
1569a747e4fSDavid du Colombier if(m > *n)
1579a747e4fSDavid du Colombier return toosmall(fss, m);
1589a747e4fSDavid du Colombier *n = m;
1599a747e4fSDavid du Colombier memmove(va, s->chal, m);
1609a747e4fSDavid du Colombier fss->phase = SNeedResp;
1619a747e4fSDavid du Colombier return RpcOk;
1629a747e4fSDavid du Colombier }
1639a747e4fSDavid du Colombier }
1649a747e4fSDavid du Colombier
1659a747e4fSDavid du Colombier static int
p9response(Fsstate * fss,State * s)166b7b24591SDavid du Colombier p9response(Fsstate *fss, State *s)
167b7b24591SDavid du Colombier {
168b7b24591SDavid du Colombier char key[DESKEYLEN];
169b7b24591SDavid du Colombier uchar buf[8];
170b7b24591SDavid du Colombier ulong chal;
171b7b24591SDavid du Colombier char *pw;
172b7b24591SDavid du Colombier
1732ebbfa15SDavid du Colombier pw = _strfindattr(s->key->privattr, "!password");
174b7b24591SDavid du Colombier if(pw == nil)
175b7b24591SDavid du Colombier return failure(fss, "vncresponse cannot happen");
176b7b24591SDavid du Colombier passtokey(key, pw);
177b7b24591SDavid du Colombier memset(buf, 0, 8);
178*f54edc78SDavid du Colombier snprint((char*)buf, sizeof buf, "%d", atoi(s->chal));
179b7b24591SDavid du Colombier if(encrypt(key, buf, 8) < 0)
180b7b24591SDavid du Colombier return failure(fss, "can't encrypt response");
181b7b24591SDavid du Colombier chal = (buf[0]<<24)+(buf[1]<<16)+(buf[2]<<8)+buf[3];
182b7b24591SDavid du Colombier s->resplen = snprint(s->resp, sizeof s->resp, "%.8lux", chal);
183b7b24591SDavid du Colombier return RpcOk;
184b7b24591SDavid du Colombier }
185b7b24591SDavid du Colombier
186b7b24591SDavid du Colombier static uchar tab[256];
187b7b24591SDavid du Colombier
188b7b24591SDavid du Colombier /* VNC reverses the bits of each byte before using as a des key */
189b7b24591SDavid du Colombier static void
mktab(void)190b7b24591SDavid du Colombier mktab(void)
191b7b24591SDavid du Colombier {
192b7b24591SDavid du Colombier int i, j, k;
193b7b24591SDavid du Colombier static int once;
194b7b24591SDavid du Colombier
195b7b24591SDavid du Colombier if(once)
196b7b24591SDavid du Colombier return;
197b7b24591SDavid du Colombier once = 1;
198b7b24591SDavid du Colombier
199b7b24591SDavid du Colombier for(i=0; i<256; i++) {
200b7b24591SDavid du Colombier j=i;
201b7b24591SDavid du Colombier tab[i] = 0;
202b7b24591SDavid du Colombier for(k=0; k<8; k++) {
203b7b24591SDavid du Colombier tab[i] = (tab[i]<<1) | (j&1);
204b7b24591SDavid du Colombier j >>= 1;
205b7b24591SDavid du Colombier }
206b7b24591SDavid du Colombier }
207b7b24591SDavid du Colombier }
208b7b24591SDavid du Colombier
209b7b24591SDavid du Colombier static int
vncaddkey(Key * k,int before)21070b8e010SDavid du Colombier vncaddkey(Key *k, int before)
211b7b24591SDavid du Colombier {
212b7b24591SDavid du Colombier uchar *p;
213b7b24591SDavid du Colombier char *s;
214b7b24591SDavid du Colombier
215b7b24591SDavid du Colombier k->priv = emalloc(8+1);
2162ebbfa15SDavid du Colombier if(s = _strfindattr(k->privattr, "!password")){
217b7b24591SDavid du Colombier mktab();
218b7b24591SDavid du Colombier memset(k->priv, 0, 8+1);
219b7b24591SDavid du Colombier strncpy((char*)k->priv, s, 8);
220b7b24591SDavid du Colombier for(p=k->priv; *p; p++)
221b7b24591SDavid du Colombier *p = tab[*p];
222b7b24591SDavid du Colombier }else{
223b7b24591SDavid du Colombier werrstr("no key data");
224b7b24591SDavid du Colombier return -1;
225b7b24591SDavid du Colombier }
22670b8e010SDavid du Colombier return replacekey(k, before);
227b7b24591SDavid du Colombier }
228b7b24591SDavid du Colombier
229b7b24591SDavid du Colombier static void
vncclosekey(Key * k)230b7b24591SDavid du Colombier vncclosekey(Key *k)
231b7b24591SDavid du Colombier {
232b7b24591SDavid du Colombier free(k->priv);
233b7b24591SDavid du Colombier }
234b7b24591SDavid du Colombier
235b7b24591SDavid du Colombier static int
vncresponse(Fsstate *,State * s)236b7b24591SDavid du Colombier vncresponse(Fsstate*, State *s)
237b7b24591SDavid du Colombier {
238b7b24591SDavid du Colombier DESstate des;
239b7b24591SDavid du Colombier
240b7b24591SDavid du Colombier memmove(s->resp, s->chal, sizeof s->chal);
241b7b24591SDavid du Colombier setupDESstate(&des, s->key->priv, nil);
242b7b24591SDavid du Colombier desECBencrypt((uchar*)s->resp, s->challen, &des);
243b7b24591SDavid du Colombier s->resplen = s->challen;
244b7b24591SDavid du Colombier return RpcOk;
245b7b24591SDavid du Colombier }
246b7b24591SDavid du Colombier
247b7b24591SDavid du Colombier static int
p9crwrite(Fsstate * fss,void * va,uint n)2489a747e4fSDavid du Colombier p9crwrite(Fsstate *fss, void *va, uint n)
2499a747e4fSDavid du Colombier {
2509a747e4fSDavid du Colombier char tbuf[TICKETLEN+AUTHENTLEN];
2519a747e4fSDavid du Colombier State *s;
2529a747e4fSDavid du Colombier char *data = va;
2539a747e4fSDavid du Colombier Authenticator a;
2549a747e4fSDavid du Colombier char resp[Maxchal];
255b7b24591SDavid du Colombier int ret;
2569a747e4fSDavid du Colombier
2579a747e4fSDavid du Colombier s = fss->ps;
2589a747e4fSDavid du Colombier switch(fss->phase){
2599a747e4fSDavid du Colombier default:
2609a747e4fSDavid du Colombier return phaseerror(fss, "write");
2619a747e4fSDavid du Colombier
262b7b24591SDavid du Colombier case CNeedChal:
263b7b24591SDavid du Colombier if(n >= sizeof(s->chal))
264b7b24591SDavid du Colombier return failure(fss, Ebadarg);
265b7b24591SDavid du Colombier memset(s->chal, 0, sizeof s->chal);
266b7b24591SDavid du Colombier memmove(s->chal, data, n);
267b7b24591SDavid du Colombier s->challen = n;
268b7b24591SDavid du Colombier
269b7b24591SDavid du Colombier if(s->astype == AuthChal)
270b7b24591SDavid du Colombier ret = p9response(fss, s);
271b7b24591SDavid du Colombier else
272b7b24591SDavid du Colombier ret = vncresponse(fss, s);
273b7b24591SDavid du Colombier if(ret != RpcOk)
274b7b24591SDavid du Colombier return ret;
275b7b24591SDavid du Colombier fss->phase = CHaveResp;
276b7b24591SDavid du Colombier return RpcOk;
277b7b24591SDavid du Colombier
2789a747e4fSDavid du Colombier case SNeedResp:
2799a747e4fSDavid du Colombier /* send response to auth server and get ticket */
2809a747e4fSDavid du Colombier if(n > sizeof(resp))
2819a747e4fSDavid du Colombier return failure(fss, Ebadarg);
2829a747e4fSDavid du Colombier memset(resp, 0, sizeof resp);
2839a747e4fSDavid du Colombier memmove(resp, data, n);
2849a747e4fSDavid du Colombier if(write(s->asfd, resp, s->challen) != s->challen)
2859a747e4fSDavid du Colombier return failure(fss, Easproto);
2869a747e4fSDavid du Colombier
2879a747e4fSDavid du Colombier /* get ticket plus authenticator from auth server */
2889a747e4fSDavid du Colombier if(_asrdresp(s->asfd, tbuf, TICKETLEN+AUTHENTLEN) < 0)
2899a747e4fSDavid du Colombier return failure(fss, nil);
2909a747e4fSDavid du Colombier
2919a747e4fSDavid du Colombier /* check ticket */
2929a747e4fSDavid du Colombier convM2T(tbuf, &s->t, s->key->priv);
2939a747e4fSDavid du Colombier if(s->t.num != AuthTs
294260f7b65SDavid du Colombier || memcmp(s->t.chal, s->tr.chal, sizeof(s->t.chal)) != 0){
2956822557bSDavid du Colombier if (s->key->successes == 0)
296260f7b65SDavid du Colombier disablekey(s->key);
2979a747e4fSDavid du Colombier return failure(fss, Easproto);
298260f7b65SDavid du Colombier }
2996822557bSDavid du Colombier s->key->successes++;
3009a747e4fSDavid du Colombier convM2A(tbuf+TICKETLEN, &a, s->t.key);
3019a747e4fSDavid du Colombier if(a.num != AuthAc
3029a747e4fSDavid du Colombier || memcmp(a.chal, s->tr.chal, sizeof(a.chal)) != 0
3039a747e4fSDavid du Colombier || a.id != 0)
3049a747e4fSDavid du Colombier return failure(fss, Easproto);
3059a747e4fSDavid du Colombier
3069a747e4fSDavid du Colombier fss->haveai = 1;
3079a747e4fSDavid du Colombier fss->ai.cuid = s->t.cuid;
3089a747e4fSDavid du Colombier fss->ai.suid = s->t.suid;
3099a747e4fSDavid du Colombier fss->ai.nsecret = 0;
3109a747e4fSDavid du Colombier fss->ai.secret = nil;
3119a747e4fSDavid du Colombier fss->phase = Established;
3129a747e4fSDavid du Colombier return RpcOk;
3139a747e4fSDavid du Colombier }
3149a747e4fSDavid du Colombier }
3159a747e4fSDavid du Colombier
3169a747e4fSDavid du Colombier static int
getchal(State * s,Fsstate * fss)3179a747e4fSDavid du Colombier getchal(State *s, Fsstate *fss)
3189a747e4fSDavid du Colombier {
3199a747e4fSDavid du Colombier char trbuf[TICKREQLEN];
3209a747e4fSDavid du Colombier int n;
321d9306527SDavid du Colombier
3222ebbfa15SDavid du Colombier safecpy(s->tr.hostid, _strfindattr(s->key->attr, "user"), sizeof(s->tr.hostid));
3232ebbfa15SDavid du Colombier safecpy(s->tr.authdom, _strfindattr(s->key->attr, "dom"), sizeof(s->tr.authdom));
3249a747e4fSDavid du Colombier s->tr.type = s->astype;
3259a747e4fSDavid du Colombier convTR2M(&s->tr, trbuf);
3269a747e4fSDavid du Colombier
3279a747e4fSDavid du Colombier /* get challenge from auth server */
3282ebbfa15SDavid du Colombier s->asfd = _authdial(nil, _strfindattr(s->key->attr, "dom"));
3299a747e4fSDavid du Colombier if(s->asfd < 0)
3309a747e4fSDavid du Colombier return failure(fss, Easproto);
3319a747e4fSDavid du Colombier if(write(s->asfd, trbuf, TICKREQLEN) != TICKREQLEN)
3329a747e4fSDavid du Colombier return failure(fss, Easproto);
3339a747e4fSDavid du Colombier n = _asrdresp(s->asfd, s->chal, s->challen);
3349a747e4fSDavid du Colombier if(n <= 0){
3359a747e4fSDavid du Colombier if(n == 0)
3369a747e4fSDavid du Colombier werrstr("_asrdresp short read");
3379a747e4fSDavid du Colombier return failure(fss, nil);
3389a747e4fSDavid du Colombier }
3399a747e4fSDavid du Colombier s->challen = n;
3409a747e4fSDavid du Colombier fss->phase = SHaveChal;
3419a747e4fSDavid du Colombier return RpcOk;
3429a747e4fSDavid du Colombier }
3439a747e4fSDavid du Colombier
3449a747e4fSDavid du Colombier Proto p9cr =
3459a747e4fSDavid du Colombier {
3469a747e4fSDavid du Colombier .name= "p9cr",
3479a747e4fSDavid du Colombier .init= p9crinit,
3489a747e4fSDavid du Colombier .write= p9crwrite,
3499a747e4fSDavid du Colombier .read= p9crread,
3509a747e4fSDavid du Colombier .close= p9crclose,
351b7b24591SDavid du Colombier .keyprompt= "user? !password?",
3529a747e4fSDavid du Colombier };
3539a747e4fSDavid du Colombier
3549a747e4fSDavid du Colombier Proto vnc =
3559a747e4fSDavid du Colombier {
3569a747e4fSDavid du Colombier .name= "vnc",
3579a747e4fSDavid du Colombier .init= p9crinit,
3589a747e4fSDavid du Colombier .write= p9crwrite,
3599a747e4fSDavid du Colombier .read= p9crread,
3609a747e4fSDavid du Colombier .close= p9crclose,
361b7b24591SDavid du Colombier .keyprompt= "!password?",
362b7b24591SDavid du Colombier .addkey= vncaddkey,
3639a747e4fSDavid du Colombier };
364