1 /*
2 * p9cr, vnc - textual challenge/response authentication
3 *
4 * Client protocol: [currently unimplemented]
5 * write challenge
6 * read response
7 *
8 * Server protocol:
9 * write user
10 * read challenge
11 * write response
12 */
13
14 #include "dat.h"
15
16 enum
17 {
18 Maxchal= 64,
19 };
20
21 typedef struct State State;
22 struct State
23 {
24 Key *key;
25 int astype;
26 int asfd;
27 Ticket t;
28 Ticketreq tr;
29 char chal[Maxchal];
30 int challen;
31 char resp[Maxchal];
32 int resplen;
33 };
34
35 enum
36 {
37 CNeedChal,
38 CHaveResp,
39
40 SHaveChal,
41 SNeedResp,
42
43 Maxphase,
44 };
45
46 static char *phasenames[Maxphase] =
47 {
48 [CNeedChal] "CNeedChal",
49 [CHaveResp] "CHaveResp",
50
51 [SHaveChal] "SHaveChal",
52 [SNeedResp] "SNeedResp",
53 };
54
55 static void
p9crclose(Fsstate * fss)56 p9crclose(Fsstate *fss)
57 {
58 State *s;
59
60 s = fss->ps;
61 if(s->asfd >= 0){
62 close(s->asfd);
63 s->asfd = -1;
64 }
65 free(s);
66 }
67
68 static int getchal(State*, Fsstate*);
69
70 static int
p9crinit(Proto * p,Fsstate * fss)71 p9crinit(Proto *p, Fsstate *fss)
72 {
73 int iscli, ret;
74 char *user;
75 State *s;
76 Attr *attr;
77 Keyinfo ki;
78
79 if((iscli = isclient(_strfindattr(fss->attr, "role"))) < 0)
80 return failure(fss, nil);
81
82 s = emalloc(sizeof(*s));
83 s->asfd = -1;
84 if(p == &p9cr){
85 s->astype = AuthChal;
86 s->challen = NETCHLEN;
87 }else if(p == &vnc){
88 s->astype = AuthVNC;
89 s->challen = Maxchal;
90 }else
91 abort();
92
93 if(iscli){
94 fss->phase = CNeedChal;
95 if(p == &p9cr)
96 attr = setattr(_copyattr(fss->attr), "proto=p9sk1");
97 else
98 attr = nil;
99 ret = findkey(&s->key, mkkeyinfo(&ki, fss, attr),
100 "role=client %s", p->keyprompt);
101 _freeattr(attr);
102 if(ret != RpcOk){
103 free(s);
104 return ret;
105 }
106 fss->ps = s;
107 }else{
108 if((ret = findp9authkey(&s->key, fss)) != RpcOk){
109 free(s);
110 return ret;
111 }
112 if((user = _strfindattr(fss->attr, "user")) == nil){
113 free(s);
114 return failure(fss, "no user name specified in start msg");
115 }
116 if(strlen(user) >= sizeof s->tr.uid){
117 free(s);
118 return failure(fss, "user name too long");
119 }
120 fss->ps = s;
121 strcpy(s->tr.uid, user);
122 ret = getchal(s, fss);
123 if(ret != RpcOk){
124 p9crclose(fss); /* frees s */
125 fss->ps = nil;
126 }
127 }
128 fss->phasename = phasenames;
129 fss->maxphase = Maxphase;
130 return ret;
131 }
132
133 static int
p9crread(Fsstate * fss,void * va,uint * n)134 p9crread(Fsstate *fss, void *va, uint *n)
135 {
136 int m;
137 State *s;
138
139 s = fss->ps;
140 switch(fss->phase){
141 default:
142 return phaseerror(fss, "read");
143
144 case CHaveResp:
145 if(s->resplen < *n)
146 *n = s->resplen;
147 memmove(va, s->resp, *n);
148 fss->phase = Established;
149 return RpcOk;
150
151 case SHaveChal:
152 if(s->astype == AuthChal)
153 m = strlen(s->chal); /* ascii string */
154 else
155 m = s->challen; /* fixed length binary */
156 if(m > *n)
157 return toosmall(fss, m);
158 *n = m;
159 memmove(va, s->chal, m);
160 fss->phase = SNeedResp;
161 return RpcOk;
162 }
163 }
164
165 static int
p9response(Fsstate * fss,State * s)166 p9response(Fsstate *fss, State *s)
167 {
168 char key[DESKEYLEN];
169 uchar buf[8];
170 ulong chal;
171 char *pw;
172
173 pw = _strfindattr(s->key->privattr, "!password");
174 if(pw == nil)
175 return failure(fss, "vncresponse cannot happen");
176 passtokey(key, pw);
177 memset(buf, 0, 8);
178 snprint((char*)buf, sizeof buf, "%d", atoi(s->chal));
179 if(encrypt(key, buf, 8) < 0)
180 return failure(fss, "can't encrypt response");
181 chal = (buf[0]<<24)+(buf[1]<<16)+(buf[2]<<8)+buf[3];
182 s->resplen = snprint(s->resp, sizeof s->resp, "%.8lux", chal);
183 return RpcOk;
184 }
185
186 static uchar tab[256];
187
188 /* VNC reverses the bits of each byte before using as a des key */
189 static void
mktab(void)190 mktab(void)
191 {
192 int i, j, k;
193 static int once;
194
195 if(once)
196 return;
197 once = 1;
198
199 for(i=0; i<256; i++) {
200 j=i;
201 tab[i] = 0;
202 for(k=0; k<8; k++) {
203 tab[i] = (tab[i]<<1) | (j&1);
204 j >>= 1;
205 }
206 }
207 }
208
209 static int
vncaddkey(Key * k,int before)210 vncaddkey(Key *k, int before)
211 {
212 uchar *p;
213 char *s;
214
215 k->priv = emalloc(8+1);
216 if(s = _strfindattr(k->privattr, "!password")){
217 mktab();
218 memset(k->priv, 0, 8+1);
219 strncpy((char*)k->priv, s, 8);
220 for(p=k->priv; *p; p++)
221 *p = tab[*p];
222 }else{
223 werrstr("no key data");
224 return -1;
225 }
226 return replacekey(k, before);
227 }
228
229 static void
vncclosekey(Key * k)230 vncclosekey(Key *k)
231 {
232 free(k->priv);
233 }
234
235 static int
vncresponse(Fsstate *,State * s)236 vncresponse(Fsstate*, State *s)
237 {
238 DESstate des;
239
240 memmove(s->resp, s->chal, sizeof s->chal);
241 setupDESstate(&des, s->key->priv, nil);
242 desECBencrypt((uchar*)s->resp, s->challen, &des);
243 s->resplen = s->challen;
244 return RpcOk;
245 }
246
247 static int
p9crwrite(Fsstate * fss,void * va,uint n)248 p9crwrite(Fsstate *fss, void *va, uint n)
249 {
250 char tbuf[TICKETLEN+AUTHENTLEN];
251 State *s;
252 char *data = va;
253 Authenticator a;
254 char resp[Maxchal];
255 int ret;
256
257 s = fss->ps;
258 switch(fss->phase){
259 default:
260 return phaseerror(fss, "write");
261
262 case CNeedChal:
263 if(n >= sizeof(s->chal))
264 return failure(fss, Ebadarg);
265 memset(s->chal, 0, sizeof s->chal);
266 memmove(s->chal, data, n);
267 s->challen = n;
268
269 if(s->astype == AuthChal)
270 ret = p9response(fss, s);
271 else
272 ret = vncresponse(fss, s);
273 if(ret != RpcOk)
274 return ret;
275 fss->phase = CHaveResp;
276 return RpcOk;
277
278 case SNeedResp:
279 /* send response to auth server and get ticket */
280 if(n > sizeof(resp))
281 return failure(fss, Ebadarg);
282 memset(resp, 0, sizeof resp);
283 memmove(resp, data, n);
284 if(write(s->asfd, resp, s->challen) != s->challen)
285 return failure(fss, Easproto);
286
287 /* get ticket plus authenticator from auth server */
288 if(_asrdresp(s->asfd, tbuf, TICKETLEN+AUTHENTLEN) < 0)
289 return failure(fss, nil);
290
291 /* check ticket */
292 convM2T(tbuf, &s->t, s->key->priv);
293 if(s->t.num != AuthTs
294 || memcmp(s->t.chal, s->tr.chal, sizeof(s->t.chal)) != 0){
295 if (s->key->successes == 0)
296 disablekey(s->key);
297 return failure(fss, Easproto);
298 }
299 s->key->successes++;
300 convM2A(tbuf+TICKETLEN, &a, s->t.key);
301 if(a.num != AuthAc
302 || memcmp(a.chal, s->tr.chal, sizeof(a.chal)) != 0
303 || a.id != 0)
304 return failure(fss, Easproto);
305
306 fss->haveai = 1;
307 fss->ai.cuid = s->t.cuid;
308 fss->ai.suid = s->t.suid;
309 fss->ai.nsecret = 0;
310 fss->ai.secret = nil;
311 fss->phase = Established;
312 return RpcOk;
313 }
314 }
315
316 static int
getchal(State * s,Fsstate * fss)317 getchal(State *s, Fsstate *fss)
318 {
319 char trbuf[TICKREQLEN];
320 int n;
321
322 safecpy(s->tr.hostid, _strfindattr(s->key->attr, "user"), sizeof(s->tr.hostid));
323 safecpy(s->tr.authdom, _strfindattr(s->key->attr, "dom"), sizeof(s->tr.authdom));
324 s->tr.type = s->astype;
325 convTR2M(&s->tr, trbuf);
326
327 /* get challenge from auth server */
328 s->asfd = _authdial(nil, _strfindattr(s->key->attr, "dom"));
329 if(s->asfd < 0)
330 return failure(fss, Easproto);
331 if(write(s->asfd, trbuf, TICKREQLEN) != TICKREQLEN)
332 return failure(fss, Easproto);
333 n = _asrdresp(s->asfd, s->chal, s->challen);
334 if(n <= 0){
335 if(n == 0)
336 werrstr("_asrdresp short read");
337 return failure(fss, nil);
338 }
339 s->challen = n;
340 fss->phase = SHaveChal;
341 return RpcOk;
342 }
343
344 Proto p9cr =
345 {
346 .name= "p9cr",
347 .init= p9crinit,
348 .write= p9crwrite,
349 .read= p9crread,
350 .close= p9crclose,
351 .keyprompt= "user? !password?",
352 };
353
354 Proto vnc =
355 {
356 .name= "vnc",
357 .init= p9crinit,
358 .write= p9crwrite,
359 .read= p9crread,
360 .close= p9crclose,
361 .keyprompt= "!password?",
362 .addkey= vncaddkey,
363 };
364