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 };