1 #include "u.h" 2 #include "../port/lib.h" 3 #include "mem.h" 4 #include "dat.h" 5 #include "fns.h" 6 #include "io.h" 7 #include "../port/error.h" 8 9 typedef struct Crypt Crypt; 10 struct Crypt 11 { 12 Crypt *next; 13 Ticket t; 14 Authenticator a; 15 char tbuf[TICKETLEN]; /* remote ticket */ 16 }; 17 18 typedef struct Session Session; 19 struct Session 20 { 21 Lock; 22 QLock send; 23 Crypt *cache; /* cache of tickets */ 24 char cchal[CHALLEN]; /* client challenge */ 25 char schal[CHALLEN]; /* server challenge */ 26 char authid[NAMELEN]; /* server encryption uid */ 27 char authdom[DOMLEN]; /* server encryption domain */ 28 ulong cid; /* challenge id */ 29 int valid; 30 }; 31 32 struct 33 { 34 Lock; 35 Crypt *free; 36 } cryptalloc; 37 38 char eve[NAMELEN] = "bootes"; 39 char evekey[DESKEYLEN]; 40 char hostdomain[DOMLEN]; 41 42 /* 43 * return true if current user is eve 44 */ 45 int 46 iseve(void) 47 { 48 return strcmp(eve, u->p->user) == 0; 49 } 50 51 /* 52 * crypt entries are allocated from a pool rather than allocated using malloc so 53 * the memory can be protected from reading by devproc. The base and top of the 54 * crypt arena is stored in palloc for devproc. 55 */ 56 Crypt* 57 newcrypt(void) 58 { 59 Crypt *c; 60 61 lock(&cryptalloc); 62 if(cryptalloc.free) { 63 c = cryptalloc.free; 64 cryptalloc.free = c->next; 65 unlock(&cryptalloc); 66 memset(c, 0, sizeof(Crypt)); 67 return c; 68 } 69 70 cryptalloc.free = xalloc(sizeof(Crypt)*conf.nproc); 71 if(cryptalloc.free == 0) 72 panic("newcrypt"); 73 74 for(c = cryptalloc.free; c < cryptalloc.free+conf.nproc-1; c++) 75 c->next = c+1; 76 77 palloc.cmembase = (ulong)cryptalloc.free; 78 palloc.cmemtop = palloc.cmembase+(sizeof(Crypt)*conf.nproc); 79 unlock(&cryptalloc); 80 return newcrypt(); 81 } 82 83 void 84 freecrypt(Crypt *c) 85 { 86 lock(&cryptalloc); 87 c->next = cryptalloc.free; 88 cryptalloc.free = c; 89 unlock(&cryptalloc); 90 } 91 92 /* 93 * return the info received in the session message on this channel. 94 * if no session message has been exchanged, do it. 95 */ 96 long 97 sysfsession(ulong *arg) 98 { 99 int i, n; 100 Chan *c; 101 Crypt *cp; 102 Session *s; 103 Ticketreq tr; 104 Fcall f; 105 char buf[MAXMSG]; 106 107 validaddr(arg[1], TICKREQLEN, 1); 108 c = fdtochan(arg[0], OWRITE, 0, 1); 109 if(waserror()){ 110 close(c); 111 nexterror(); 112 } 113 114 /* add a session structure to the channel if it has none */ 115 lock(c); 116 s = c->session; 117 if(s == 0){ 118 s = malloc(sizeof(Session)); 119 if(s == 0){ 120 unlock(c); 121 error(Enomem); 122 } 123 c->session = s; 124 } 125 unlock(c); 126 127 qlock(&s->send); 128 if(s->valid == 0){ 129 /* 130 * Exchange a session message with the server. 131 */ 132 for(i = 0; i < CHALLEN; i++) 133 s->cchal[i] = nrand(256); 134 135 f.tag = NOTAG; 136 f.type = Tsession; 137 memmove(f.chal, s->cchal, CHALLEN); 138 n = convS2M(&f, buf); 139 140 /* 141 * If an error occurs reading or writing, 142 * this probably is a mount of a mount so turn off 143 * authentication. 144 */ 145 if(waserror()) 146 goto noauth; 147 148 if((*devtab[c->type].write)(c, buf, n, 0) != n) 149 error(Esession); 150 n = (*devtab[c->type].read)(c, buf, sizeof buf, 0); 151 /* OK is sometimes sent as a Datakit sign-on */ 152 if(n == 2 && buf[0] == 'O' && buf[1] == 'K') 153 n = (*devtab[c->type].read)(c, buf, sizeof buf, 0); 154 155 poperror(); 156 157 if(convM2S(buf, &f, n) == 0){ 158 qunlock(&s->send); 159 error(Esession); 160 } 161 switch(f.type){ 162 case Rsession: 163 memmove(s->schal, f.chal, CHALLEN); 164 memmove(s->authid, f.authid, NAMELEN); 165 memmove(s->authdom, f.authdom, DOMLEN); 166 break; 167 case Rerror: 168 qunlock(&s->send); 169 error(f.ename); 170 default: 171 qunlock(&s->send); 172 error(Esession); 173 } 174 noauth: 175 s->valid = 1; 176 } 177 qunlock(&s->send); 178 179 /* 180 * If server requires no ticket, or user is "none", or a ticket 181 * is already cached, zero the request type 182 */ 183 tr.type = AuthTreq; 184 if(strcmp(u->p->user, "none") == 0 || s->authid[0] == 0) 185 tr.type = 0; 186 else{ 187 lock(s); 188 for(cp = s->cache; cp; cp = cp->next) 189 if(strcmp(cp->t.cuid, u->p->user) == 0){ 190 tr.type = 0; 191 break; 192 } 193 unlock(s); 194 } 195 196 /* create ticket request */ 197 memmove(tr.chal, s->schal, CHALLEN); 198 memmove(tr.authid, s->authid, NAMELEN); 199 memmove(tr.authdom, s->authdom, DOMLEN); 200 memmove(tr.uid, u->p->user, NAMELEN); 201 memmove(tr.hostid, eve, NAMELEN); 202 convTR2M(&tr, (char*)arg[1]); 203 204 close(c); 205 poperror(); 206 return 0; 207 } 208 209 /* 210 * attach tickets to a session 211 */ 212 long 213 sysfauth(ulong *arg) 214 { 215 Chan *c; 216 char *ta; 217 Session *s; 218 Crypt *cp, *ncp, **l; 219 char tbuf[2*TICKETLEN]; 220 221 validaddr(arg[1], 2*TICKETLEN, 0); 222 c = fdtochan(arg[0], OWRITE, 0, 1); 223 s = c->session; 224 if(s == 0) 225 error("fauth must follow fsession"); 226 cp = newcrypt(); 227 if(waserror()){ 228 freecrypt(cp); 229 nexterror(); 230 } 231 232 /* ticket supplied, use it */ 233 ta = (char*)arg[1]; 234 memmove(tbuf, ta, 2*TICKETLEN); 235 convM2T(tbuf, &cp->t, evekey); 236 if(cp->t.num != AuthTc) 237 error("bad AuthTc in ticket"); 238 if(strncmp(u->p->user, cp->t.cuid, NAMELEN) != 0) 239 error("bad uid in ticket"); 240 if(memcmp(cp->t.chal, s->schal, CHALLEN) != 0) 241 error("bad chal in ticket"); 242 memmove(cp->tbuf, tbuf+TICKETLEN, TICKETLEN); 243 244 /* string onto local list, replace old version */ 245 lock(s); 246 l = &s->cache; 247 for(ncp = s->cache; ncp; ncp = *l){ 248 if(strcmp(ncp->t.cuid, u->p->user) == 0){ 249 *l = ncp->next; 250 freecrypt(ncp); 251 break; 252 } 253 l = &ncp->next; 254 } 255 cp->next = s->cache; 256 s->cache = cp; 257 unlock(s); 258 poperror(); 259 return 0; 260 } 261 262 /* 263 * free a session created by fsession 264 */ 265 void 266 freesession(Session *s) 267 { 268 Crypt *cp, *next; 269 270 for(cp = s->cache; cp; cp = next) { 271 next = cp->next; 272 freecrypt(cp); 273 } 274 free(s); 275 } 276 277 /* 278 * called by mattach() to fill in the Tattach message 279 */ 280 ulong 281 authrequest(Session *s, Fcall *f) 282 { 283 Crypt *cp; 284 ulong id, dofree; 285 286 /* no authentication if user is "none" or if no ticket required by remote */ 287 if(s == 0 || s->authid[0] == 0 || strcmp(u->p->user, "none") == 0){ 288 memset(f->ticket, 0, TICKETLEN); 289 memset(f->auth, 0, AUTHENTLEN); 290 return 0; 291 } 292 293 /* look for ticket in cache */ 294 dofree = 0; 295 lock(s); 296 for(cp = s->cache; cp; cp = cp->next) 297 if(strcmp(cp->t.cuid, u->p->user) == 0) 298 break; 299 300 id = s->cid++; 301 unlock(s); 302 303 if(cp == 0){ 304 /* 305 * create a ticket using hostkey, this solves the 306 * chicken and egg problem 307 */ 308 cp = newcrypt(); 309 cp->t.num = AuthTs; 310 memmove(cp->t.chal, s->schal, CHALLEN); 311 memmove(cp->t.cuid, u->p->user, NAMELEN); 312 memmove(cp->t.suid, u->p->user, NAMELEN); 313 memmove(cp->t.key, evekey, DESKEYLEN); 314 convT2M(&cp->t, f->ticket, evekey); 315 dofree = 1; 316 } else 317 memmove(f->ticket, cp->tbuf, TICKETLEN); 318 319 /* create an authenticator */ 320 memmove(cp->a.chal, s->schal, CHALLEN); 321 cp->a.num = AuthAc; 322 cp->a.id = id; 323 convA2M(&cp->a, f->auth, cp->t.key); 324 if(dofree) 325 freecrypt(cp); 326 return id; 327 } 328 329 /* 330 * called by mattach() to check the Rattach message 331 */ 332 void 333 authreply(Session *s, ulong id, Fcall *f) 334 { 335 Crypt *cp; 336 337 if(s == 0) 338 return; 339 340 lock(s); 341 for(cp = s->cache; cp; cp = cp->next) 342 if(strcmp(cp->t.cuid, u->p->user) == 0) 343 break; 344 unlock(s); 345 346 /* we're getting around authentication */ 347 if(s == 0 || cp == 0 || s->authid[0] == 0 || strcmp(u->p->user, "none") == 0) 348 return; 349 350 convM2A(f->rauth, &cp->a, cp->t.key); 351 if(cp->a.num != AuthAs){ 352 print("bad encryption type\n"); 353 error("server lies"); 354 } 355 if(memcmp(cp->a.chal, s->cchal, sizeof(cp->a.chal))){ 356 print("bad returned challenge\n"); 357 error("server lies"); 358 } 359 if(cp->a.id != id){ 360 print("bad returned id\n"); 361 error("server lies"); 362 } 363 } 364 365 /* 366 * called by devcons() for #c/authenticate 367 * 368 * The protocol is 369 * 1) read ticket request from #c/authenticate 370 * 2) write ticket+authenticator to #c/authenticate. if it matches 371 * the challenge the user is changed to the suid field of the ticket 372 * 3) read authenticator (to confirm this is the server advertised) 373 */ 374 long 375 authread(Chan *c, char *a, int n) 376 { 377 Crypt *cp; 378 int i; 379 Ticketreq tr; 380 381 if(c->aux == 0){ 382 /* 383 * first read returns a ticket request 384 */ 385 if(n != TICKREQLEN) 386 error(Ebadarg); 387 c->aux = newcrypt(); 388 cp = c->aux; 389 390 memset(&tr, 0, sizeof(tr)); 391 tr.type = AuthTreq; 392 strcpy(tr.hostid, eve); 393 strcpy(tr.authid, eve); 394 strcpy(tr.authdom, hostdomain); 395 strcpy(tr.uid, u->p->user); 396 for(i = 0; i < CHALLEN; i++) 397 tr.chal[i] = nrand(256); 398 memmove(cp->a.chal, tr.chal, CHALLEN); 399 convTR2M(&tr, a); 400 } else { 401 /* 402 * subsequent read returns an authenticator 403 */ 404 if(n != AUTHENTLEN) 405 error(Ebadarg); 406 cp = c->aux; 407 408 cp->a.num = AuthAs; 409 memmove(cp->a.chal, cp->t.chal, CHALLEN); 410 cp->a.id = 0; 411 convA2M(&cp->a, cp->tbuf, cp->t.key); 412 memmove(a, cp->tbuf, AUTHENTLEN); 413 414 freecrypt(cp); 415 c->aux = 0; 416 } 417 return n; 418 } 419 420 long 421 authwrite(Chan *c, char *a, int n) 422 { 423 Crypt *cp; 424 425 if(n != TICKETLEN+AUTHENTLEN) 426 error(Ebadarg); 427 if(c->aux == 0) 428 error(Ebadarg); 429 cp = c->aux; 430 431 memmove(cp->tbuf, a, TICKETLEN); 432 convM2T(cp->tbuf, &cp->t, evekey); 433 if(cp->t.num != AuthTs || memcmp(cp->a.chal, cp->t.chal, CHALLEN)) 434 error(Eperm); 435 436 memmove(cp->tbuf, a+TICKETLEN, AUTHENTLEN); 437 convM2A(cp->tbuf, &cp->a, cp->t.key); 438 if(cp->a.num != AuthAc || memcmp(cp->a.chal, cp->t.chal, CHALLEN)) 439 error(Eperm); 440 441 memmove(u->p->user, cp->t.suid, NAMELEN); 442 return n; 443 } 444 445 /* 446 * called by devcons() for #c/authcheck 447 * 448 * a write of a ticket+authenticator [+challenge+id] succeeds if they match 449 */ 450 long 451 authcheck(Chan *c, char *a, int n) 452 { 453 Crypt *cp; 454 char *chal; 455 ulong id; 456 457 if(n != TICKETLEN+AUTHENTLEN && n != TICKETLEN+AUTHENTLEN+CHALLEN+4) 458 error(Ebadarg); 459 if(c->aux == 0) 460 c->aux = newcrypt(); 461 cp = c->aux; 462 463 memmove(cp->tbuf, a, TICKETLEN); 464 convM2T(cp->tbuf, &cp->t, evekey); 465 if(cp->t.num != AuthTc) 466 error(Ebadarg); 467 if(strcmp(u->p->user, cp->t.cuid)) 468 error(cp->t.cuid); 469 470 memmove(cp->tbuf, a+TICKETLEN, AUTHENTLEN); 471 convM2A(cp->tbuf, &cp->a, cp->t.key); 472 if(n == TICKETLEN+AUTHENTLEN+CHALLEN+4){ 473 uchar *p = (uchar *)&a[TICKETLEN+AUTHENTLEN+CHALLEN]; 474 id = p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24); 475 chal = &a[TICKETLEN+AUTHENTLEN]; 476 }else{ 477 id = 0; 478 chal = cp->t.chal; 479 } 480 if(cp->a.num != AuthAs || memcmp(chal, cp->a.chal, CHALLEN) || cp->a.id != id) 481 error(Eperm); 482 483 return n; 484 } 485 486 /* 487 * called by devcons() for #c/authenticator 488 * 489 * a read after a write of a ticket (or ticket+id) returns an authenticator 490 * for that ticket. 491 */ 492 long 493 authentwrite(Chan *c, char *a, int n) 494 { 495 Crypt *cp; 496 497 if(n != TICKETLEN && n != TICKETLEN+4) 498 error(Ebadarg); 499 if(c->aux == 0) 500 c->aux = newcrypt(); 501 cp = c->aux; 502 503 memmove(cp->tbuf, a, TICKETLEN); 504 convM2T(cp->tbuf, &cp->t, evekey); 505 if(cp->t.num != AuthTc || strcmp(cp->t.cuid, u->p->user)){ 506 freecrypt(cp); 507 c->aux = 0; 508 error(Ebadarg); 509 } 510 if(n == TICKETLEN+4){ 511 uchar *p = (uchar *)&a[TICKETLEN]; 512 cp->a.id = p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24); 513 }else 514 cp->a.id = 0; 515 516 return n; 517 } 518 long 519 authentread(Chan *c, char *a, int n) 520 { 521 Crypt *cp; 522 523 cp = c->aux; 524 if(cp == 0) 525 error("authenticator read must follow a write"); 526 527 cp->a.num = AuthAc; 528 memmove(cp->a.chal, cp->t.chal, CHALLEN); 529 convA2M(&cp->a, cp->tbuf, cp->t.key); 530 memmove(a, cp->tbuf, AUTHENTLEN); 531 532 return n; 533 } 534 535 void 536 authclose(Chan *c) 537 { 538 if(c->aux) 539 freecrypt(c->aux); 540 c->aux = 0; 541 } 542 543 /* 544 * called by devcons() for key device 545 */ 546 long 547 keyread(char *a, int n, long offset) 548 { 549 if(n<DESKEYLEN || offset != 0) 550 error(Ebadarg); 551 if(!cpuserver || !iseve()) 552 error(Eperm); 553 memmove(a, evekey, DESKEYLEN); 554 return DESKEYLEN; 555 } 556 557 long 558 keywrite(char *a, int n) 559 { 560 if(n != DESKEYLEN) 561 error(Ebadarg); 562 if(!iseve()) 563 error(Eperm); 564 memmove(evekey, a, DESKEYLEN); 565 return DESKEYLEN; 566 } 567 568 /* 569 * called by devcons() for user device 570 * 571 * anyone can become none 572 */ 573 long 574 userwrite(char *a, int n) 575 { 576 if(n >= NAMELEN) 577 error(Ebadarg); 578 if(strcmp(a, "none") != 0) 579 error(Eperm); 580 memset(u->p->user, 0, NAMELEN); 581 strcpy(u->p->user, "none"); 582 return n; 583 } 584 585 /* 586 * called by devcons() for host owner/domain 587 * 588 * writing hostowner also sets user 589 */ 590 long 591 hostownerwrite(char *a, int n) 592 { 593 char buf[NAMELEN]; 594 595 if(!iseve()) 596 error(Eperm); 597 if(n >= NAMELEN) 598 error(Ebadarg); 599 memset(buf, 0, NAMELEN); 600 strncpy(buf, a, n); 601 if(buf[0] == 0) 602 error(Ebadarg); 603 memmove(eve, buf, NAMELEN); 604 memmove(u->p->user, buf, NAMELEN); 605 return n; 606 } 607 608 long 609 hostdomainwrite(char *a, int n) 610 { 611 char buf[DOMLEN]; 612 613 if(!iseve()) 614 error(Eperm); 615 if(n >= DOMLEN) 616 error(Ebadarg); 617 memset(buf, 0, DOMLEN); 618 strncpy(buf, a, n); 619 if(buf[0] == 0) 620 error(Ebadarg); 621 memmove(hostdomain, buf, DOMLEN); 622 return n; 623 } 624 625 void 626 wipekeys(void) 627 { 628 memset(evekey, 0, sizeof(evekey)); 629 memset((void*)palloc.cmembase, 0, palloc.cmemtop - palloc.cmembase); 630 } 631