1 /* 2 * 4th Edition p9any/p9sk1 authentication based on auth9p1.c 3 * Nigel Roles (nigel@9fs.org) 2003 4 */ 5 6 #include <plan9.h> 7 #include <fcall.h> 8 #include <u9fs.h> 9 #include <stdlib.h> /* for random stuff */ 10 11 typedef struct Ticket Ticket; 12 typedef struct Ticketreq Ticketreq; 13 typedef struct Authenticator Authenticator; 14 15 enum 16 { 17 DOMLEN= 48, /* length of an authentication domain name */ 18 DESKEYLEN= 7, /* length of a des key for encrypt/decrypt */ 19 CHALLEN= 8 /* length of a challenge */ 20 }; 21 22 enum { 23 HaveProtos, 24 NeedProto, 25 NeedChal, 26 HaveTreq, 27 NeedTicket, 28 HaveAuth, 29 Established, 30 }; 31 32 /* encryption numberings (anti-replay) */ 33 enum 34 { 35 AuthTreq=1, /* ticket request */ 36 AuthChal=2, /* challenge box request */ 37 AuthPass=3, /* change password */ 38 AuthOK=4, /* fixed length reply follows */ 39 AuthErr=5, /* error follows */ 40 AuthMod=6, /* modify user */ 41 AuthApop=7, /* apop authentication for pop3 */ 42 AuthOKvar=9, /* variable length reply follows */ 43 AuthChap=10, /* chap authentication for ppp */ 44 AuthMSchap=11, /* MS chap authentication for ppp */ 45 AuthCram=12, /* CRAM verification for IMAP (RFC2195 & rfc2104) */ 46 AuthHttp=13, /* http domain login */ 47 AuthVNC=14, /* http domain login */ 48 49 50 AuthTs=64, /* ticket encrypted with server's key */ 51 AuthTc, /* ticket encrypted with client's key */ 52 AuthAs, /* server generated authenticator */ 53 AuthAc, /* client generated authenticator */ 54 AuthTp, /* ticket encrypted with client's key for password change */ 55 AuthHr /* http reply */ 56 }; 57 58 struct Ticketreq 59 { 60 char type; 61 char authid[NAMELEN]; /* server's encryption id */ 62 char authdom[DOMLEN]; /* server's authentication domain */ 63 char chal[CHALLEN]; /* challenge from server */ 64 char hostid[NAMELEN]; /* host's encryption id */ 65 char uid[NAMELEN]; /* uid of requesting user on host */ 66 }; 67 #define TICKREQLEN (3*NAMELEN+CHALLEN+DOMLEN+1) 68 69 struct Ticket 70 { 71 char num; /* replay protection */ 72 char chal[CHALLEN]; /* server challenge */ 73 char cuid[NAMELEN]; /* uid on client */ 74 char suid[NAMELEN]; /* uid on server */ 75 char key[DESKEYLEN]; /* nonce DES key */ 76 }; 77 #define TICKETLEN (CHALLEN+2*NAMELEN+DESKEYLEN+1) 78 79 struct Authenticator 80 { 81 char num; /* replay protection */ 82 char chal[CHALLEN]; 83 ulong id; /* authenticator id, ++'d with each auth */ 84 }; 85 #define AUTHENTLEN (CHALLEN+4+1) 86 87 static int convT2M(Ticket*, char*, char*); 88 static void convM2T(char*, Ticket*, char*); 89 static void convM2Tnoenc(char*, Ticket*); 90 static int convA2M(Authenticator*, char*, char*); 91 static void convM2A(char*, Authenticator*, char*); 92 static int convTR2M(Ticketreq*, char*); 93 static void convM2TR(char*, Ticketreq*); 94 static int passtokey(char*, char*); 95 96 /* 97 * destructively encrypt the buffer, which 98 * must be at least 8 characters long. 99 */ 100 static int 101 encrypt9p(void *key, void *vbuf, int n) 102 { 103 char ekey[128], *buf; 104 int i, r; 105 106 if(n < 8) 107 return 0; 108 key_setup(key, ekey); 109 buf = vbuf; 110 n--; 111 r = n % 7; 112 n /= 7; 113 for(i = 0; i < n; i++){ 114 block_cipher(ekey, buf, 0); 115 buf += 7; 116 } 117 if(r) 118 block_cipher(ekey, buf - 7 + r, 0); 119 return 1; 120 } 121 122 /* 123 * destructively decrypt the buffer, which 124 * must be at least 8 characters long. 125 */ 126 static int 127 decrypt9p(void *key, void *vbuf, int n) 128 { 129 char ekey[128], *buf; 130 int i, r; 131 132 if(n < 8) 133 return 0; 134 key_setup(key, ekey); 135 buf = vbuf; 136 n--; 137 r = n % 7; 138 n /= 7; 139 buf += n * 7; 140 if(r) 141 block_cipher(ekey, buf - 7 + r, 1); 142 for(i = 0; i < n; i++){ 143 buf -= 7; 144 block_cipher(ekey, buf, 1); 145 } 146 return 1; 147 } 148 149 #define CHAR(x) *p++ = f->x 150 #define SHORT(x) p[0] = f->x; p[1] = f->x>>8; p += 2 151 #define VLONG(q) p[0] = (q); p[1] = (q)>>8; p[2] = (q)>>16; p[3] = (q)>>24; p += 4 152 #define LONG(x) VLONG(f->x) 153 #define STRING(x,n) memmove(p, f->x, n); p += n 154 155 static int 156 convTR2M(Ticketreq *f, char *ap) 157 { 158 int n; 159 uchar *p; 160 161 p = (uchar*)ap; 162 CHAR(type); 163 STRING(authid, NAMELEN); 164 STRING(authdom, DOMLEN); 165 STRING(chal, CHALLEN); 166 STRING(hostid, NAMELEN); 167 STRING(uid, NAMELEN); 168 n = p - (uchar*)ap; 169 return n; 170 } 171 172 static int 173 convT2M(Ticket *f, char *ap, char *key) 174 { 175 int n; 176 uchar *p; 177 178 p = (uchar*)ap; 179 CHAR(num); 180 STRING(chal, CHALLEN); 181 STRING(cuid, NAMELEN); 182 STRING(suid, NAMELEN); 183 STRING(key, DESKEYLEN); 184 n = p - (uchar*)ap; 185 if(key) 186 encrypt9p(key, ap, n); 187 return n; 188 } 189 190 int 191 convA2M(Authenticator *f, char *ap, char *key) 192 { 193 int n; 194 uchar *p; 195 196 p = (uchar*)ap; 197 CHAR(num); 198 STRING(chal, CHALLEN); 199 LONG(id); 200 n = p - (uchar*)ap; 201 if(key) 202 encrypt9p(key, ap, n); 203 return n; 204 } 205 206 #undef CHAR 207 #undef SHORT 208 #undef VLONG 209 #undef LONG 210 #undef STRING 211 212 #define CHAR(x) f->x = *p++ 213 #define SHORT(x) f->x = (p[0] | (p[1]<<8)); p += 2 214 #define VLONG(q) q = (p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24)); p += 4 215 #define LONG(x) VLONG(f->x) 216 #define STRING(x,n) memmove(f->x, p, n); p += n 217 218 void 219 convM2A(char *ap, Authenticator *f, char *key) 220 { 221 uchar *p; 222 223 if(key) 224 decrypt9p(key, ap, AUTHENTLEN); 225 p = (uchar*)ap; 226 CHAR(num); 227 STRING(chal, CHALLEN); 228 LONG(id); 229 USED(p); 230 } 231 232 void 233 convM2T(char *ap, Ticket *f, char *key) 234 { 235 uchar *p; 236 237 if(key) 238 decrypt9p(key, ap, TICKETLEN); 239 p = (uchar*)ap; 240 CHAR(num); 241 STRING(chal, CHALLEN); 242 STRING(cuid, NAMELEN); 243 f->cuid[NAMELEN-1] = 0; 244 STRING(suid, NAMELEN); 245 f->suid[NAMELEN-1] = 0; 246 STRING(key, DESKEYLEN); 247 USED(p); 248 } 249 250 #undef CHAR 251 #undef SHORT 252 #undef LONG 253 #undef VLONG 254 #undef STRING 255 256 static int 257 passtokey(char *key, char *p) 258 { 259 uchar buf[NAMELEN], *t; 260 int i, n; 261 262 n = strlen(p); 263 if(n >= NAMELEN) 264 n = NAMELEN-1; 265 memset(buf, ' ', 8); 266 t = buf; 267 strncpy((char*)t, p, n); 268 t[n] = 0; 269 memset(key, 0, DESKEYLEN); 270 for(;;){ 271 for(i = 0; i < DESKEYLEN; i++) 272 key[i] = (t[i] >> i) + (t[i+1] << (8 - (i+1))); 273 if(n <= 8) 274 return 1; 275 n -= 8; 276 t += 8; 277 if(n < 8){ 278 t -= 8 - n; 279 n = 8; 280 } 281 encrypt9p(key, t, 8); 282 } 283 return 1; /* not reached */ 284 } 285 286 static char authkey[DESKEYLEN]; 287 static char *authid; 288 static char *authdom; 289 static char *haveprotosmsg; 290 static char *needprotomsg; 291 292 static void 293 p9anyinit(void) 294 { 295 int n, fd; 296 char abuf[200]; 297 char *af, *f[4]; 298 299 af = autharg; 300 if(af == nil) 301 af = "/etc/u9fs.key"; 302 303 if((fd = open(af, OREAD)) < 0) 304 sysfatal("can't open key file '%s'", af); 305 306 if((n = readn(fd, abuf, sizeof(abuf)-1)) < 0) 307 sysfatal("can't read key file '%s'", af); 308 if (n > 0 && abuf[n - 1] == '\n') 309 n--; 310 abuf[n] = '\0'; 311 312 if(getfields(abuf, f, nelem(f), 0, "\n") != 3) 313 sysfatal("key file '%s' not exactly 3 lines", af); 314 315 passtokey(authkey, f[0]); 316 authid = strdup(f[1]); 317 authdom = strdup(f[2]); 318 haveprotosmsg = malloc(strlen("p9sk1") + 1 + strlen(authdom) + 1); 319 sprint(haveprotosmsg, "p9sk1@%s", authdom); 320 needprotomsg = malloc(strlen("p9sk1") + 1 + strlen(authdom) + 1); 321 sprint(needprotomsg, "p9sk1 %s", authdom); 322 } 323 324 typedef struct AuthSession { 325 int state; 326 char *uname; 327 char *aname; 328 char cchal[CHALLEN]; 329 Ticketreq tr; 330 Ticket t; 331 } AuthSession; 332 333 static char* 334 p9anyauth(Fcall *rx, Fcall *tx) 335 { 336 AuthSession *sp; 337 int result; 338 Fid *f; 339 char *ep; 340 341 sp = malloc(sizeof(AuthSession)); 342 f = newauthfid(rx->afid, sp, &ep); 343 if (f == nil) { 344 free(sp); 345 return ep; 346 } 347 if (chatty9p) 348 fprint(2, "p9anyauth: afid %d\n", rx->afid); 349 sp->state = HaveProtos; 350 sp->uname = strdup(rx->uname); 351 sp->aname = strdup(rx->aname); 352 tx->aqid.type = QTAUTH; 353 tx->aqid.path = 1; 354 tx->aqid.vers = 0; 355 return nil; 356 } 357 358 static char * 359 p9anyattach(Fcall *rx, Fcall *tx) 360 { 361 AuthSession *sp; 362 Fid *f; 363 char *ep; 364 365 f = oldauthfid(rx->afid, (void **)&sp, &ep); 366 if (f == nil) 367 return ep; 368 if (chatty9p) 369 fprint(2, "p9anyattach: afid %d state %d\n", rx->afid, sp->state); 370 if (sp->state == Established && strcmp(rx->uname, sp->uname) == 0 371 && strcmp(rx->aname, sp->aname) == 0) 372 return nil; 373 return "authentication failed"; 374 } 375 376 static int 377 readstr(Fcall *rx, Fcall *tx, char *s, int len) 378 { 379 if (rx->offset >= len) 380 return 0; 381 tx->count = len - rx->offset; 382 if (tx->count > rx->count) 383 tx->count = rx->count; 384 memcpy(tx->data, s + rx->offset, tx->count); 385 return tx->count; 386 } 387 388 static char * 389 p9anyread(Fcall *rx, Fcall *tx) 390 { 391 AuthSession *sp; 392 char *ep; 393 char buf[100]; 394 395 Fid *f; 396 f = oldauthfid(rx->afid, (void **)&sp, &ep); 397 if (f == nil) 398 return ep; 399 if (chatty9p) 400 fprint(2, "p9anyread: afid %d state %d\n", rx->fid, sp->state); 401 switch (sp->state) { 402 case HaveProtos: 403 readstr(rx, tx, haveprotosmsg, strlen(haveprotosmsg) + 1); 404 if (rx->offset + tx->count == strlen(haveprotosmsg) + 1) 405 sp->state = NeedProto; 406 return nil; 407 case HaveTreq: 408 if (rx->count != TICKREQLEN) 409 goto botch; 410 convTR2M(&sp->tr, tx->data); 411 tx->count = TICKREQLEN; 412 sp->state = NeedTicket; 413 return nil; 414 case HaveAuth: { 415 Authenticator a; 416 if (rx->count != AUTHENTLEN) 417 goto botch; 418 a.num = AuthAs; 419 memmove(a.chal, sp->cchal, CHALLEN); 420 a.id = 0; 421 convA2M(&a, (char*)tx->data, sp->t.key); 422 memset(sp->t.key, 0, sizeof(sp->t.key)); 423 tx->count = rx->count; 424 sp->state = Established; 425 return nil; 426 } 427 default: 428 botch: 429 return "protocol botch"; 430 } 431 } 432 433 static char * 434 p9anywrite(Fcall *rx, Fcall *tx) 435 { 436 AuthSession *sp; 437 char *ep; 438 439 Fid *f; 440 441 f = oldauthfid(rx->afid, (void **)&sp, &ep); 442 if (f == nil) 443 return ep; 444 if (chatty9p) 445 fprint(2, "p9anywrite: afid %d state %d\n", rx->fid, sp->state); 446 switch (sp->state) { 447 case NeedProto: 448 if (rx->count != strlen(needprotomsg) + 1) 449 return "protocol response wrong length"; 450 if (memcmp(rx->data, needprotomsg, rx->count) != 0) 451 return "unacceptable protocol"; 452 sp->state = NeedChal; 453 tx->count = rx->count; 454 return nil; 455 case NeedChal: 456 if (rx->count != CHALLEN) 457 goto botch; 458 memmove(sp->cchal, rx->data, CHALLEN); 459 sp->tr.type = AuthTreq; 460 safecpy(sp->tr.authid, authid, sizeof(sp->tr.authid)); 461 safecpy(sp->tr.authdom, authdom, sizeof(sp->tr.authdom)); 462 randombytes((uchar *)sp->tr.chal, CHALLEN); 463 safecpy(sp->tr.hostid, "", sizeof(sp->tr.hostid)); 464 safecpy(sp->tr.uid, "", sizeof(sp->tr.uid)); 465 tx->count = rx->count; 466 sp->state = HaveTreq; 467 return nil; 468 case NeedTicket: { 469 Authenticator a; 470 471 if (rx->count != TICKETLEN + AUTHENTLEN) { 472 fprint(2, "bad length in attach"); 473 goto botch; 474 } 475 convM2T((char*)rx->data, &sp->t, authkey); 476 if (sp->t.num != AuthTs) { 477 fprint(2, "bad AuthTs in attach\n"); 478 goto botch; 479 } 480 if (memcmp(sp->t.chal, sp->tr.chal, CHALLEN) != 0) { 481 fprint(2, "bad challenge in attach\n"); 482 goto botch; 483 } 484 convM2A((char*)rx->data + TICKETLEN, &a, sp->t.key); 485 if (a.num != AuthAc) { 486 fprint(2, "bad AuthAs in attach\n"); 487 goto botch; 488 } 489 if(memcmp(a.chal, sp->tr.chal, CHALLEN) != 0) { 490 fprint(2, "bad challenge in attach 2\n"); 491 goto botch; 492 } 493 sp->state = HaveAuth; 494 tx->count = rx->count; 495 return nil; 496 } 497 default: 498 botch: 499 return "protocol botch"; 500 } 501 } 502 503 static void 504 safefree(char *p) 505 { 506 if (p) { 507 memset(p, 0, strlen(p)); 508 free(p); 509 } 510 } 511 512 static char * 513 p9anyclunk(Fcall *rx, Fcall *tx) 514 { 515 Fid *f; 516 AuthSession *sp; 517 char *ep; 518 519 f = oldauthfid(rx->afid, (void **)&sp, &ep); 520 if (f == nil) 521 return ep; 522 if (chatty9p) 523 fprint(2, "p9anyclunk: afid %d\n", rx->fid); 524 safefree(sp->uname); 525 safefree(sp->aname); 526 memset(sp, 0, sizeof(sp)); 527 free(sp); 528 return nil; 529 } 530 531 Auth authp9any = { 532 "p9any", 533 p9anyauth, 534 p9anyattach, 535 p9anyinit, 536 p9anyread, 537 p9anywrite, 538 p9anyclunk, 539 }; 540