From mboxrd@z Thu Jan 1 00:00:00 1970 From: Mike Haertel Message-Id: <200111020543.fA25hkG02351@ducky.net> To: 9fans@cse.psu.edu Subject: [9fans] auth protocol questions + proposed kfs improvement Date: Thu, 1 Nov 2001 21:43:46 -0800 Topicbox-Message-UUID: 1246c3b6-eaca-11e9-9e20-41e7f4b1d025 What is the reason for the distinction between AuthTs and AuthTc tickets, and AuthAs and AuthAc authenticators? This caused me some grief in a recent project... I wanted to modify kfs so that "listen il!*!17008" would work on terminals as well as cpu servers, so that I could use the local file system on my laptop as the boot file system for the bitsy. The problem, I found, is that the code in kfs relies on being able to read /dev/key, and uses the key so obtained to verify authentication protocol tickets by hand. But reading /dev/key is only allowed on cpu servers (presumably it's disallowed on terminals to prevent trojans from having access to your password). So, I wanted to modify kfs to use /dev/authcheck to verify the authentication protocol, rather than relying on being able to read /dev/key. Here's the new version of authorize() for /sys/src/cmd/disk/kfs/auth.c. (The text of this email continues beyond the code...) int authorize(Chan *cp, Fcall *in, Fcall *ou) { Ticket t; Authenticator a; char authbuf[TICKETLEN + AUTHENTLEN + CHALLEN + 4]; int fd, try; ulong bit; if (cp == cons.srvchan) /* local channel already safe */ return 1; if(noauth || wstatallow) /* set to allow entry during boot */ return 1; if(strcmp(in->uname, "none") == 0) return allownone || cp->auth; if(in->type == Toattach) return 0; fd = open("#c/authcheck", ORDWR); if (fd < 0) { print("can't open #c/authcheck\n"); return 0; } memcpy(authbuf, in->ticket, TICKETLEN); memcpy(authbuf + TICKETLEN, in->auth, AUTHENTLEN); memcpy(authbuf + TICKETLEN + AUTHENTLEN, cp->chal, CHALLEN); /* * the id must be in a valid range. the range is specified by a * lower bount (idoffset) and a bit vector (idvec) where a * bit set to 1 means unusable */ lock(&cp->idlock); for (try = cp->idoffset; try < cp->idoffset + 16; ++try) { authbuf[TICKETLEN + AUTHENTLEN + CHALLEN + 0] = try; authbuf[TICKETLEN + AUTHENTLEN + CHALLEN + 1] = try >> 8; authbuf[TICKETLEN + AUTHENTLEN + CHALLEN + 2] = try >> 16; authbuf[TICKETLEN + AUTHENTLEN + CHALLEN + 3] = try >> 24; if (write(fd, authbuf, sizeof authbuf) == sizeof authbuf) break; } if (try == cp->idoffset + 16) { unlock(&cp->idlock); close(fd); print("authcheck failed\n"); return 0; } bit = 1UL << (try - cp->idoffset); if (cp->idvec & bit) { unlock(&cp->idlock); print("id %d reused (replay attack?)\n", try); close(fd); return 0; } cp->idvec |= bit; /* normalize the vector */ while(cp->idvec&0xffff0001){ cp->idvec >>= 1; cp->idoffset++; } unlock(&cp->idlock); /* ticket name and attach name must match */ if (read(fd, authbuf, TICKETLEN) != TICKETLEN) { close(fd); print("can't read decrypted ticket\n"); return 0; } close(fd); convM2T(authbuf, &t, nil); if(memcmp(in->uname, t.cuid, sizeof(in->uname)) != 0){ print("names don't match\n"); return 0; } /* copy translated name into input record */ memmove(in->uname, t.suid, sizeof(in->uname)); /* craft a reply */ a.num = AuthAs; memmove(a.chal, cp->rchal, CHALLEN); a.id = try; convA2M(&a, ou->rauth, t.key); cp->auth = 1; return 1; } Unfortunately, what I discovered is this doesn't work. The reason it doesn't work is that /dev/authcheck only works with AuthTc flavored tickets, and AuthAs flavored authenticators. So I replaced authcheck() in /sys/src/9/port/auth.c with the following code. (email *still* continues after this code...) long authcheck(Chan *c, char *a, int n) { Crypt *cp; char *chal; ulong id; if(n != TICKETLEN+AUTHENTLEN && n != TICKETLEN+AUTHENTLEN+CHALLEN+4) error(Ebadarg); if(c->aux == 0) c->aux = newcrypt(); cp = c->aux; memmove(cp->tbuf, a, TICKETLEN); convM2T(cp->tbuf, &cp->t, evekey); if (cp->t.num == AuthTc) { if(strcmp(up->user, cp->t.cuid)) error(cp->t.cuid); } else if (cp->t.num != AuthTs) { error(Ebadarg); } memmove(cp->tbuf, a+TICKETLEN, AUTHENTLEN); convM2A(cp->tbuf, &cp->a, cp->t.key); if(n == TICKETLEN+AUTHENTLEN+CHALLEN+4){ uchar *p = (uchar *)&a[TICKETLEN+AUTHENTLEN+CHALLEN]; id = p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24); chal = &a[TICKETLEN+AUTHENTLEN]; }else{ id = 0; chal = cp->t.chal; } if(cp->t.num == AuthTc && cp->a.num != AuthAs || cp->t.num == AuthTs && cp->a.num != AuthAc || memcmp(chal, cp->a.chal, CHALLEN) || cp->a.id != id) error(Eperm); return n; } Now, /dev/authcheck works either with (AuthTc,AuthAs) or (AuthTs,AuthAc) flavored ticket+authenticator pairs. So, a couple of questions: 1. Is there any security reason why /dev/authcheck didn't already work with AuthTs flavored tickets and AuthAc flavored authenticators? 2. What is the reason for strcmp(up->user, cp->t.cuid)? 3. Repeating my original question, is there a security reason for the distinction between AuthTs and AuthTc flavored tickets, and the distinction between AuthAc and AuthAs flavored authenticators?