xref: /plan9/sys/src/cmd/auth/factotum/p9cr.c (revision f54edc786b9c49b2c7ab1c0695cdc8c698b11f4d)
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