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 extern int chatty9p; 88 89 static int convT2M(Ticket*, char*, char*); 90 static void convM2T(char*, Ticket*, char*); 91 static void convM2Tnoenc(char*, Ticket*); 92 static int convA2M(Authenticator*, char*, char*); 93 static void convM2A(char*, Authenticator*, char*); 94 static int convTR2M(Ticketreq*, char*); 95 static void convM2TR(char*, Ticketreq*); 96 static int passtokey(char*, char*); 97 98 /* 99 * destructively encrypt the buffer, which 100 * must be at least 8 characters long. 101 */ 102 static int 103 encrypt9p(void *key, void *vbuf, int n) 104 { 105 char ekey[128], *buf; 106 int i, r; 107 108 if(n < 8) 109 return 0; 110 key_setup(key, ekey); 111 buf = vbuf; 112 n--; 113 r = n % 7; 114 n /= 7; 115 for(i = 0; i < n; i++){ 116 block_cipher(ekey, buf, 0); 117 buf += 7; 118 } 119 if(r) 120 block_cipher(ekey, buf - 7 + r, 0); 121 return 1; 122 } 123 124 /* 125 * destructively decrypt the buffer, which 126 * must be at least 8 characters long. 127 */ 128 static int 129 decrypt9p(void *key, void *vbuf, int n) 130 { 131 char ekey[128], *buf; 132 int i, r; 133 134 if(n < 8) 135 return 0; 136 key_setup(key, ekey); 137 buf = vbuf; 138 n--; 139 r = n % 7; 140 n /= 7; 141 buf += n * 7; 142 if(r) 143 block_cipher(ekey, buf - 7 + r, 1); 144 for(i = 0; i < n; i++){ 145 buf -= 7; 146 block_cipher(ekey, buf, 1); 147 } 148 return 1; 149 } 150 151 #define CHAR(x) *p++ = f->x 152 #define SHORT(x) p[0] = f->x; p[1] = f->x>>8; p += 2 153 #define VLONG(q) p[0] = (q); p[1] = (q)>>8; p[2] = (q)>>16; p[3] = (q)>>24; p += 4 154 #define LONG(x) VLONG(f->x) 155 #define STRING(x,n) memmove(p, f->x, n); p += n 156 157 static int 158 convTR2M(Ticketreq *f, char *ap) 159 { 160 int n; 161 uchar *p; 162 163 p = (uchar*)ap; 164 CHAR(type); 165 STRING(authid, NAMELEN); 166 STRING(authdom, DOMLEN); 167 STRING(chal, CHALLEN); 168 STRING(hostid, NAMELEN); 169 STRING(uid, NAMELEN); 170 n = p - (uchar*)ap; 171 return n; 172 } 173 174 static int 175 convT2M(Ticket *f, char *ap, char *key) 176 { 177 int n; 178 uchar *p; 179 180 p = (uchar*)ap; 181 CHAR(num); 182 STRING(chal, CHALLEN); 183 STRING(cuid, NAMELEN); 184 STRING(suid, NAMELEN); 185 STRING(key, DESKEYLEN); 186 n = p - (uchar*)ap; 187 if(key) 188 encrypt9p(key, ap, n); 189 return n; 190 } 191 192 int 193 convA2M(Authenticator *f, char *ap, char *key) 194 { 195 int n; 196 uchar *p; 197 198 p = (uchar*)ap; 199 CHAR(num); 200 STRING(chal, CHALLEN); 201 LONG(id); 202 n = p - (uchar*)ap; 203 if(key) 204 encrypt9p(key, ap, n); 205 return n; 206 } 207 208 #undef CHAR 209 #undef SHORT 210 #undef VLONG 211 #undef LONG 212 #undef STRING 213 214 #define CHAR(x) f->x = *p++ 215 #define SHORT(x) f->x = (p[0] | (p[1]<<8)); p += 2 216 #define VLONG(q) q = (p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24)); p += 4 217 #define LONG(x) VLONG(f->x) 218 #define STRING(x,n) memmove(f->x, p, n); p += n 219 220 void 221 convM2A(char *ap, Authenticator *f, char *key) 222 { 223 uchar *p; 224 225 if(key) 226 decrypt9p(key, ap, AUTHENTLEN); 227 p = (uchar*)ap; 228 CHAR(num); 229 STRING(chal, CHALLEN); 230 LONG(id); 231 USED(p); 232 } 233 234 void 235 convM2T(char *ap, Ticket *f, char *key) 236 { 237 uchar *p; 238 239 if(key) 240 decrypt9p(key, ap, TICKETLEN); 241 p = (uchar*)ap; 242 CHAR(num); 243 STRING(chal, CHALLEN); 244 STRING(cuid, NAMELEN); 245 f->cuid[NAMELEN-1] = 0; 246 STRING(suid, NAMELEN); 247 f->suid[NAMELEN-1] = 0; 248 STRING(key, DESKEYLEN); 249 USED(p); 250 } 251 252 #undef CHAR 253 #undef SHORT 254 #undef LONG 255 #undef VLONG 256 #undef STRING 257 258 static int 259 passtokey(char *key, char *p) 260 { 261 uchar buf[NAMELEN], *t; 262 int i, n; 263 264 n = strlen(p); 265 if(n >= NAMELEN) 266 n = NAMELEN-1; 267 memset(buf, ' ', 8); 268 t = buf; 269 strncpy((char*)t, p, n); 270 t[n] = 0; 271 memset(key, 0, DESKEYLEN); 272 for(;;){ 273 for(i = 0; i < DESKEYLEN; i++) 274 key[i] = (t[i] >> i) + (t[i+1] << (8 - (i+1))); 275 if(n <= 8) 276 return 1; 277 n -= 8; 278 t += 8; 279 if(n < 8){ 280 t -= 8 - n; 281 n = 8; 282 } 283 encrypt9p(key, t, 8); 284 } 285 return 1; /* not reached */ 286 } 287 288 static char authkey[DESKEYLEN]; 289 static char *authid; 290 static char *authdom; 291 static char *haveprotosmsg; 292 static char *needprotomsg; 293 294 static void 295 p9anyinit(void) 296 { 297 int n, fd; 298 char abuf[200]; 299 char *af, *f[4]; 300 301 af = autharg; 302 if(af == nil) 303 af = "/etc/u9fs.key"; 304 305 if((fd = open(af, OREAD)) < 0) 306 sysfatal("can't open key file '%s'", af); 307 308 if((n = readn(fd, abuf, sizeof(abuf)-1)) < 0) 309 sysfatal("can't read key file '%s'", af); 310 if (n > 0 && abuf[n - 1] == '\n') 311 n--; 312 abuf[n] = '\0'; 313 314 if(getfields(abuf, f, nelem(f), 0, "\n") != 3) 315 sysfatal("key file '%s' not exactly 3 lines", af); 316 317 passtokey(authkey, f[0]); 318 authid = strdup(f[1]); 319 authdom = strdup(f[2]); 320 haveprotosmsg = malloc(strlen("p9sk1") + 1 + strlen(authdom) + 1); 321 sprint(haveprotosmsg, "p9sk1@%s", authdom); 322 needprotomsg = malloc(strlen("p9sk1") + 1 + strlen(authdom) + 1); 323 sprint(needprotomsg, "p9sk1 %s", authdom); 324 } 325 326 typedef struct AuthSession { 327 int state; 328 char *uname; 329 char *aname; 330 char cchal[CHALLEN]; 331 Ticketreq tr; 332 Ticket t; 333 } AuthSession; 334 335 static char* 336 p9anyauth(Fcall *rx, Fcall *tx) 337 { 338 AuthSession *sp; 339 int result; 340 Fid *f; 341 char *ep; 342 343 sp = malloc(sizeof(AuthSession)); 344 f = newauthfid(rx->afid, sp, &ep); 345 if (f == nil) { 346 free(sp); 347 return ep; 348 } 349 if (chatty9p) 350 fprint(2, "p9anyauth: afid %d\n", rx->afid); 351 sp->state = HaveProtos; 352 sp->uname = strdup(rx->uname); 353 sp->aname = strdup(rx->aname); 354 tx->aqid.type = QTAUTH; 355 tx->aqid.path = 1; 356 tx->aqid.vers = 0; 357 return nil; 358 } 359 360 static char * 361 p9anyattach(Fcall *rx, Fcall *tx) 362 { 363 AuthSession *sp; 364 Fid *f; 365 char *ep; 366 367 f = oldauthfid(rx->afid, (void **)&sp, &ep); 368 if (f == nil) 369 return ep; 370 if (chatty9p) 371 fprint(2, "p9anyattach: afid %d state %d\n", rx->afid, sp->state); 372 if (sp->state == Established && strcmp(rx->uname, sp->uname) == 0 373 && strcmp(rx->aname, sp->aname) == 0) 374 return nil; 375 return "authentication failed"; 376 } 377 378 static int 379 readstr(Fcall *rx, Fcall *tx, char *s, int len) 380 { 381 if (rx->offset >= len) 382 return 0; 383 tx->count = len - rx->offset; 384 if (tx->count > rx->count) 385 tx->count = rx->count; 386 memcpy(tx->data, s + rx->offset, tx->count); 387 return tx->count; 388 } 389 390 static char * 391 p9anyread(Fcall *rx, Fcall *tx) 392 { 393 AuthSession *sp; 394 char *ep; 395 char buf[100]; 396 397 Fid *f; 398 f = oldauthfid(rx->afid, (void **)&sp, &ep); 399 if (f == nil) 400 return ep; 401 if (chatty9p) 402 fprint(2, "p9anyread: afid %d state %d\n", rx->fid, sp->state); 403 switch (sp->state) { 404 case HaveProtos: 405 readstr(rx, tx, haveprotosmsg, strlen(haveprotosmsg) + 1); 406 if (rx->offset + tx->count == strlen(haveprotosmsg) + 1) 407 sp->state = NeedProto; 408 return nil; 409 case HaveTreq: 410 if (rx->count != TICKREQLEN) 411 goto botch; 412 convTR2M(&sp->tr, tx->data); 413 tx->count = TICKREQLEN; 414 sp->state = NeedTicket; 415 return nil; 416 case HaveAuth: { 417 Authenticator a; 418 if (rx->count != AUTHENTLEN) 419 goto botch; 420 a.num = AuthAs; 421 memmove(a.chal, sp->cchal, CHALLEN); 422 a.id = 0; 423 convA2M(&a, (char*)tx->data, sp->t.key); 424 memset(sp->t.key, 0, sizeof(sp->t.key)); 425 tx->count = rx->count; 426 sp->state = Established; 427 return nil; 428 } 429 default: 430 botch: 431 return "protocol botch"; 432 } 433 } 434 435 static char * 436 p9anywrite(Fcall *rx, Fcall *tx) 437 { 438 AuthSession *sp; 439 char *ep; 440 441 Fid *f; 442 443 f = oldauthfid(rx->afid, (void **)&sp, &ep); 444 if (f == nil) 445 return ep; 446 if (chatty9p) 447 fprint(2, "p9anywrite: afid %d state %d\n", rx->fid, sp->state); 448 switch (sp->state) { 449 case NeedProto: 450 if (rx->count != strlen(needprotomsg) + 1) 451 return "protocol response wrong length"; 452 if (memcmp(rx->data, needprotomsg, rx->count) != 0) 453 return "unacceptable protocol"; 454 sp->state = NeedChal; 455 tx->count = rx->count; 456 return nil; 457 case NeedChal: 458 if (rx->count != CHALLEN) 459 goto botch; 460 memmove(sp->cchal, rx->data, CHALLEN); 461 sp->tr.type = AuthTreq; 462 safecpy(sp->tr.authid, authid, sizeof(sp->tr.authid)); 463 safecpy(sp->tr.authdom, authdom, sizeof(sp->tr.authdom)); 464 randombytes((uchar *)sp->tr.chal, CHALLEN); 465 safecpy(sp->tr.hostid, "", sizeof(sp->tr.hostid)); 466 safecpy(sp->tr.uid, "", sizeof(sp->tr.uid)); 467 tx->count = rx->count; 468 sp->state = HaveTreq; 469 return nil; 470 case NeedTicket: { 471 Authenticator a; 472 473 if (rx->count != TICKETLEN + AUTHENTLEN) { 474 fprint(2, "bad length in attach"); 475 goto botch; 476 } 477 convM2T((char*)rx->data, &sp->t, authkey); 478 if (sp->t.num != AuthTs) { 479 fprint(2, "bad AuthTs in attach\n"); 480 goto botch; 481 } 482 if (memcmp(sp->t.chal, sp->tr.chal, CHALLEN) != 0) { 483 fprint(2, "bad challenge in attach\n"); 484 goto botch; 485 } 486 convM2A((char*)rx->data + TICKETLEN, &a, sp->t.key); 487 if (a.num != AuthAc) { 488 fprint(2, "bad AuthAs in attach\n"); 489 goto botch; 490 } 491 if(memcmp(a.chal, sp->tr.chal, CHALLEN) != 0) { 492 fprint(2, "bad challenge in attach 2\n"); 493 goto botch; 494 } 495 sp->state = HaveAuth; 496 tx->count = rx->count; 497 return nil; 498 } 499 default: 500 botch: 501 return "protocol botch"; 502 } 503 } 504 505 static void 506 safefree(char *p) 507 { 508 if (p) { 509 memset(p, 0, strlen(p)); 510 free(p); 511 } 512 } 513 514 static char * 515 p9anyclunk(Fcall *rx, Fcall *tx) 516 { 517 Fid *f; 518 AuthSession *sp; 519 char *ep; 520 521 f = oldauthfid(rx->afid, (void **)&sp, &ep); 522 if (f == nil) 523 return ep; 524 if (chatty9p) 525 fprint(2, "p9anyclunk: afid %d\n", rx->fid); 526 safefree(sp->uname); 527 safefree(sp->aname); 528 memset(sp, 0, sizeof(sp)); 529 free(sp); 530 return nil; 531 } 532 533 Auth authp9any = { 534 "p9any", 535 p9anyauth, 536 p9anyattach, 537 p9anyinit, 538 p9anyread, 539 p9anywrite, 540 p9anyclunk, 541 }; 542