ref: b181d62f485e617f54518a8e875b1a30bef02e54
dir: /cpu.c/
/* * cpu.c - Make a connection to a cpu server */ #include <stdio.h> #include <sys/socket.h> #include <sys/types.h> #include <arpa/inet.h> #include <netdb.h> #include <unistd.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <signal.h> #include <netinet/in.h> #include <openssl/ssl.h> #include <openssl/err.h> #include <u.h> #include <args.h> #include <libc.h> #include <auth.h> #include <authsrv.h> #include <libsec.h> static AuthInfo *p9any(int); static int getkey(Authkey*, char*, char*, char*, char*); static int p9authtls(int); char *argv0; static char *host; char *authserver; char *user, *pass; SSL_CTX *ssl_ctx; SSL *ssl_conn; //callback needs access to ai returned from p9any static AuthInfo *ai; static uint psk_client_cb(SSL *ssl, const char *hint, char *identity, uint max_iden_len, uchar *psk, uint max_psk_len) { uint nsecret = ai->nsecret; char i[] = "p9secret"; if(max_iden_len < sizeof i || max_psk_len < ai->nsecret) sysfatal("psk buffers are too small"); memcpy(identity, i, sizeof i); memcpy(psk, ai->secret, ai->nsecret); memset(ai, 0, sizeof *ai); return nsecret; } void errstr(char *s){} int unix_dial(char *host, char *port) { int fd; struct sockaddr_in server; struct hostent *he; struct in_addr **addr_list; he = gethostbyname(host); if(he == nil){ sysfatal("could not resolve %s", host); } fd = socket(AF_INET, SOCK_STREAM, 0); addr_list = (struct in_addr **) he->h_addr_list; server.sin_addr.s_addr = inet_addr(inet_ntoa(*addr_list[0])); server.sin_family = AF_INET; server.sin_port = htons(atoi(port)); if(connect(fd, (struct sockaddr*)&server, sizeof(server)) < 0) return -1; return fd; } char* estrdup(char *s) { s = strdup(s); if(s == nil) sysfatal("out of memory"); return s; } typedef size_t (*iofunc)(int, void*, size_t); size_t tls_send(int f, void *b, size_t n) { return SSL_write(ssl_conn, b, n); } size_t tls_recv(int f, void *b, size_t n) { return SSL_read(ssl_conn, b, n); } size_t s_send(int f, void *b, size_t n) { return write(f, b, n); } size_t s_recv(int f, void *b, size_t n) { return read(f, b, n); } void xfer(int from, int to, iofunc recvf, iofunc sendf) { char buf[12*1024]; size_t n; while((n = recvf(from, buf, sizeof buf)) > 0){ if(sendf(to, buf, n) < 0) break; } } void usage(void) { fprint(2, "Usage: %s [ -R ] [ -u user ] [ -h host ] [ -a authserver ] -p port cmd...\n", argv0); exits("usage"); } int main(int argc, char **argv) { int Rflag; int fd, res; char buf2[1024]; char buf[1024]; size_t n; char *port; int pin[2]; int pout[2]; int infd, outfd; int i; pid_t execc, xferc; execc = xferc = 0; Rflag = 0; infd = 0; outfd = 1; user = getenv("USER"); host = getenv("CPU"); authserver = getenv("AUTH"); pass = getenv("PASS"); port = nil; ARGBEGIN { case 'u': user = EARGF(usage()); break; case 'h': host = EARGF(usage()); break; case 'a': authserver = EARGF(usage()); break; case 'p': port = EARGF(usage()); break; case 'R': Rflag++; break; } ARGEND if(Rflag) port = "17019"; if(user == nil || host == nil || authserver == nil || port == nil) usage(); if(pass == nil) pass = getpass("password:"); SSL_library_init(); OpenSSL_add_all_algorithms(); SSL_load_error_strings(); ssl_ctx = SSL_CTX_new(TLSv1_2_client_method()); SSL_CTX_set_psk_client_callback(ssl_ctx, psk_client_cb); if(ssl_ctx == nil) sysfatal("could not init openssl"); ssl_conn = SSL_new(ssl_ctx); if(*argv && !Rflag){ pipe(pin); pipe(pout); switch((execc = fork())){ case -1: sysfatal("fork"); case 0: close(pin[1]); close(pout[0]); dup2(pin[0], 0); dup2(pout[1], 1); execvp(argv[0], argv); sysfatal("exec"); } close(pout[1]); close(pin[0]); infd = pout[0]; outfd = pin[1]; } fd = unix_dial(host, port); if(fd < 0){ sysfatal("Failed to connect to the client"); } p9authtls(fd); if(*argv && Rflag) { for(i=0,n=0; i<argc; i++) n += snprint(buf+n, sizeof buf - n - 1, "%s ", argv[i]); if(n <= 0) usage(); buf[n-1] = '\n'; buf[n] = '\0'; i = strlen(buf); snprint(buf2, sizeof buf2, "%7d\n", i); tls_send(-1, buf2, strlen(buf2)); tls_send(-1, buf, i); } switch((xferc = fork())){ case -1: sysfatal("fork"); case 0: xfer(infd, -1, s_recv, tls_send); break; default: xfer(-1, outfd, tls_recv, s_send); break; } if(xferc) kill(xferc, SIGTERM); if(execc) kill(execc, SIGTERM); } int readstr(int fd, char *str, int len) { int n; while(len) { n = read(fd, str, 1); if(n < 0) return -1; if(*str == '\0') return 0; str++; len--; } return -1; } /* * p9any authentication followed by tls-psk encryption */ static int p9authtls(int fd) { ai = p9any(fd); if(ai == nil) sysfatal("can't authenticate: %r"); SSL_set_fd(ssl_conn, fd); if(SSL_connect(ssl_conn) < 0) sysfatal("ssl could not connect"); return fd; } int authdial(char *net, char *dom) { return unix_dial(authserver, "567"); } static int getastickets(Authkey *key, Ticketreq *tr, uchar *y, char *tbuf, int tbuflen) { int asfd, rv; char *dom; dom = tr->authdom; asfd = authdial(nil, dom); if(asfd < 0) return -1; if(y != nil){ PAKpriv p; rv = -1; tr->type = AuthPAK; if(_asrequest(asfd, tr) != 0 || write(asfd, y, PAKYLEN) != PAKYLEN) goto Out; authpak_new(&p, key, (uchar*)tbuf, 1); if(write(asfd, tbuf, PAKYLEN) != PAKYLEN) goto Out; if(_asrdresp(asfd, tbuf, 2*PAKYLEN) != 2*PAKYLEN) goto Out; memmove(y, tbuf, PAKYLEN); if(authpak_finish(&p, key, (uchar*)tbuf+PAKYLEN)) goto Out; } tr->type = AuthTreq; rv = _asgetticket(asfd, tr, tbuf, tbuflen); Out: close(asfd); return rv; } static int mkservertickets(Authkey *key, Ticketreq *tr, uchar *y, char *tbuf, int tbuflen) { Ticket t; int ret; if(strcmp(tr->authid, tr->hostid) != 0) return -1; memset(&t, 0, sizeof(t)); ret = 0; if(y != nil){ PAKpriv p; t.form = 1; memmove(tbuf, y, PAKYLEN); authpak_new(&p, key, y, 0); authpak_finish(&p, key, (uchar*)tbuf); } memmove(t.chal, tr->chal, CHALLEN); strcpy(t.cuid, tr->uid); strcpy(t.suid, tr->uid); genrandom((uchar*)t.key, sizeof(t.key)); t.num = AuthTc; ret += convT2M(&t, tbuf+ret, tbuflen-ret, key); t.num = AuthTs; ret += convT2M(&t, tbuf+ret, tbuflen-ret, key); memset(&t, 0, sizeof(t)); return ret; } static int gettickets(Authkey *key, Ticketreq *tr, uchar *y, char *tbuf, int tbuflen) { int ret; ret = getastickets(key, tr, y, tbuf, tbuflen); if(ret > 0) return ret; return mkservertickets(key, tr, y, tbuf, tbuflen); } AuthInfo* p9any(int fd) { char buf[1024], buf2[1024], *bbuf, *p, *proto, *dom; uchar crand[2*NONCELEN], cchal[CHALLEN], y[PAKYLEN]; char tbuf[2*MAXTICKETLEN+MAXAUTHENTLEN+PAKYLEN], trbuf[TICKREQLEN+PAKYLEN]; Authkey authkey; Authenticator auth; int i, n, m, v2, dp9ik; Ticketreq tr; Ticket t; AuthInfo *ai; if(readstr(fd, buf, sizeof buf) < 0) sysfatal("cannot read p9any negotiation: %r"); bbuf = buf; v2 = 0; if(strncmp(buf, "v.2 ", 4) == 0){ v2 = 1; bbuf += 4; } dp9ik = 0; proto = nil; while(bbuf != nil){ if((p = strchr(bbuf, ' '))) *p++ = 0; if((dom = strchr(bbuf, '@')) == nil) sysfatal("bad p9any domain"); *dom++ = 0; if(strcmp(bbuf, "p9sk1") == 0 || strcmp(bbuf, "dp9ik") == 0){ proto = bbuf; if(strcmp(proto, "dp9ik") == 0){ dp9ik = 1; break; } } bbuf = p; } if(proto == nil) sysfatal("server did not offer p9sk1 or dp9ik"); proto = estrdup(proto); sprint(buf2, "%s %s", proto, dom); if(write(fd, buf2, strlen(buf2)+1) != strlen(buf2)+1) sysfatal("cannot write user/domain choice in p9any"); if(v2){ if(readstr(fd, buf, sizeof buf) < 0) sysfatal("cannot read OK in p9any: %r"); if(memcmp(buf, "OK\0", 3) != 0) sysfatal("did not get OK in p9any: got %s", buf); } genrandom(crand, 2*NONCELEN); genrandom(cchal, CHALLEN); if(write(fd, cchal, CHALLEN) != CHALLEN) sysfatal("cannot write p9sk1 challenge: %r"); n = TICKREQLEN; if(dp9ik) n += PAKYLEN; if(readn(fd, trbuf, n) != n || convM2TR(trbuf, TICKREQLEN, &tr) <= 0) sysfatal("cannot read ticket request in p9sk1: %r"); again: if(!getkey(&authkey, user, tr.authdom, proto, pass)) sysfatal("no password"); strecpy(tr.hostid, tr.hostid+sizeof tr.hostid, user); strecpy(tr.uid, tr.uid+sizeof tr.uid, user); if(dp9ik){ memmove(y, trbuf+TICKREQLEN, PAKYLEN); n = gettickets(&authkey, &tr, y, tbuf, sizeof(tbuf)); } else { n = gettickets(&authkey, &tr, nil, tbuf, sizeof(tbuf)); } if(n <= 0) sysfatal("cannot get auth tickets in p9sk1: %r"); m = convM2T(tbuf, n, &t, &authkey); if(m <= 0 || t.num != AuthTc){ print("?password mismatch with auth server\n"); if(pass != nil && *pass) sysfatal("wrong password"); goto again; } n -= m; memmove(tbuf, tbuf+m, n); if(dp9ik && write(fd, y, PAKYLEN) != PAKYLEN) sysfatal("cannot send authpak public key back: %r"); auth.num = AuthAc; memmove(auth.rand, crand, NONCELEN); memmove(auth.chal, tr.chal, CHALLEN); m = convA2M(&auth, tbuf+n, sizeof(tbuf)-n, &t); n += m; if(write(fd, tbuf, n) != n) sysfatal("cannot send ticket and authenticator back: %r"); if((n=read(fd, tbuf, m)) != m || memcmp(tbuf, "cpu:", 4) == 0){ if(n <= 4) sysfatal("cannot read authenticator"); /* * didn't send back authenticator: * sent back fatal error message. */ memmove(buf, tbuf, n); i = readn(fd, buf+n, sizeof buf-n-1); if(i > 0) n += i; buf[n] = 0; sysfatal("server says: %s", buf); } if(convM2A(tbuf, n, &auth, &t) <= 0 || auth.num != AuthAs || tsmemcmp(auth.chal, cchal, CHALLEN) != 0){ print("?you and auth server agree about password.\n"); print("?server is confused.\n"); sysfatal("server lies"); } memmove(crand+NONCELEN, auth.rand, NONCELEN); // print("i am %s there.\n", t.suid); ai = mallocz(sizeof(AuthInfo), 1); ai->suid = estrdup(t.suid); ai->cuid = estrdup(t.cuid); if(dp9ik){ static char info[] = "Plan 9 session secret"; ai->nsecret = 256; ai->secret = mallocz(ai->nsecret, 1); hkdf_x( crand, 2*NONCELEN, (uchar*)info, sizeof(info)-1, (uchar*)t.key, NONCELEN, ai->secret, ai->nsecret, hmac_sha2_256, SHA2_256dlen); } else { ai->nsecret = 8; ai->secret = mallocz(ai->nsecret, 1); des56to64((uchar*)t.key, ai->secret); } memset(&t, 0, sizeof(t)); memset(&auth, 0, sizeof(auth)); memset(&authkey, 0, sizeof(authkey)); memset(cchal, 0, sizeof(cchal)); memset(crand, 0, sizeof(crand)); free(proto); return ai; } static int getkey(Authkey *key, char *user, char *dom, char *proto, char *pass) { if(pass != nil && *pass) pass = estrdup(pass); else { sysfatal("getkey: no password"); } if(pass != nil){ memset(key, 0, sizeof(*key)); passtokey(key, pass); if(strcmp(proto, "dp9ik") == 0) { authpak_hash(key, user); } return 1; } return 0; }