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