URI:
       tsecstore.c - plan9port - [fork] Plan 9 from user space
  HTML git clone git://src.adamsgaard.dk/plan9port
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       tsecstore.c (12665B)
       ---
            1 /* network login client */
            2 #include <u.h>
            3 #include <libc.h>
            4 #include <mp.h>
            5 #include <libsec.h>
            6 #include <authsrv.h>
            7 #include "SConn.h"
            8 #include "secstore.h"
            9 enum{ CHK = 16, MAXFILES = 100 };
           10 
           11 typedef struct AuthConn{
           12         SConn *conn;
           13         char pass[64];
           14         int passlen;
           15 } AuthConn;
           16 
           17 int verbose;
           18 Nvrsafe nvr;
           19 char *SECSTORE_DIR;
           20 
           21 void
           22 usage(void)
           23 {
           24         fprint(2, "usage: secstore [-cin] [-g getfile] [-p putfile] [-r rmfile] [-s tcp!server!5356] [-u user] [-v]\n");
           25         exits("usage");
           26 }
           27 
           28 static int
           29 getfile(SConn *conn, char *gf, uchar **buf, ulong *buflen, uchar *key, int nkey)
           30 {
           31         int fd = -1;
           32         int i, n, nr, nw, len;
           33         char s[Maxmsg+1];
           34         uchar skey[SHA1dlen], ib[Maxmsg+CHK], *ibr, *ibw, *bufw, *bufe;
           35         AESstate aes;
           36         DigestState *sha;
           37 
           38         if(strchr(gf, '/')){
           39                 fprint(2, "simple filenames, not paths like %s\n", gf);
           40                 return -1;
           41         }
           42         memset(&aes, 0, sizeof aes);
           43 
           44         snprint(s, Maxmsg, "GET %s\n", gf);
           45         conn->write(conn, (uchar*)s, strlen(s));
           46 
           47         /* get file size */
           48         s[0] = '\0';
           49         bufw = bufe = nil;
           50         if(readstr(conn, s) < 0){
           51                 fprint(2, "remote: %s\n", s);
           52                 return -1;
           53         }
           54         len = atoi(s);
           55         if(len == -1){
           56                 fprint(2, "remote file %s does not exist\n", gf);
           57                 return -1;
           58         }else if(len == -3){
           59                 fprint(2, "implausible filesize for %s\n", gf);
           60                 return -1;
           61         }else if(len < 0){
           62                 fprint(2, "GET refused for %s\n", gf);
           63                 return -1;
           64         }
           65         if(buf != nil){
           66                 *buflen = len - AESbsize - CHK;
           67                 *buf = bufw = emalloc(len);
           68                 bufe = bufw + len;
           69         }
           70 
           71         /* directory listing */
           72         if(strcmp(gf,".")==0){
           73                 if(buf != nil)
           74                         *buflen = len;
           75                 for(i=0; i < len; i += n){
           76                         if((n = conn->read(conn, (uchar*)s, Maxmsg)) <= 0){
           77                                 fprint(2, "empty file chunk\n");
           78                                 return -1;
           79                         }
           80                         if(buf == nil)
           81                                 write(1, s, n);
           82                         else
           83                                 memmove((*buf)+i, s, n);
           84                 }
           85                 return 0;
           86         }
           87 
           88         /* conn is already encrypted against wiretappers,
           89                 but gf is also encrypted against server breakin. */
           90         if(buf == nil && (fd =create(gf, OWRITE, 0600)) < 0){
           91                 fprint(2, "can't open %s: %r\n", gf);
           92                 return -1;
           93         }
           94 
           95         ibr = ibw = ib;
           96         for(nr=0; nr < len;){
           97                 if((n = conn->read(conn, ibw, Maxmsg)) <= 0){
           98                         fprint(2, "empty file chunk n=%d nr=%d len=%d: %r\n", n, nr, len);
           99                         return -1;
          100                 }
          101                 nr += n;
          102                 ibw += n;
          103                 if(!aes.setup){ /* first time, read 16 byte IV */
          104                         if(n < AESbsize){
          105                                 fprint(2, "no IV in file\n");
          106                                 return -1;
          107                         }
          108                         sha = sha1((uchar*)"aescbc file", 11, nil, nil);
          109                         sha1(key, nkey, skey, sha);
          110                         setupAESstate(&aes, skey, AESbsize, ibr);
          111                         memset(skey, 0, sizeof skey);
          112                         ibr += AESbsize;
          113                         n -= AESbsize;
          114                 }
          115                 aesCBCdecrypt(ibw-n, n, &aes);
          116                 n = ibw-ibr-CHK;
          117                 if(n > 0){
          118                         if(buf == nil){
          119                                 nw = write(fd, ibr, n);
          120                                 if(nw != n){
          121                                         fprint(2, "write error on %s", gf);
          122                                         return -1;
          123                                 }
          124                         }else{
          125                                 assert(bufw+n <= bufe);
          126                                 memmove(bufw, ibr, n);
          127                                 bufw += n;
          128                         }
          129                         ibr += n;
          130                 }
          131                 memmove(ib, ibr, ibw-ibr);
          132                 ibw = ib + (ibw-ibr);
          133                 ibr = ib;
          134         }
          135         if(buf == nil)
          136                 close(fd);
          137         n = ibw-ibr;
          138         if((n != CHK) || (memcmp(ib, "XXXXXXXXXXXXXXXX", CHK) != 0)){
          139                         fprint(2,"decrypted file failed to authenticate!\n");
          140                         return -1;
          141         }
          142         return 0;
          143 }
          144 
          145 /* This sends a file to the secstore disk that can, in an emergency, be */
          146 /* decrypted by the program aescbc.c. */
          147 static int
          148 putfile(SConn *conn, char *pf, uchar *buf, ulong len, uchar *key, int nkey)
          149 {
          150         int i, n, fd, ivo, bufi, done;
          151         char s[Maxmsg];
          152         uchar  skey[SHA1dlen], b[CHK+Maxmsg], IV[AESbsize];
          153         AESstate aes;
          154         DigestState *sha;
          155 
          156         /* create initialization vector */
          157         srand(time(0));  /* doesn't need to be unpredictable */
          158         for(i=0; i<AESbsize; i++)
          159                 IV[i] = 0xff & rand();
          160         sha = sha1((uchar*)"aescbc file", 11, nil, nil);
          161         sha1(key, nkey, skey, sha);
          162         setupAESstate(&aes, skey, AESbsize, IV);
          163         memset(skey, 0, sizeof skey);
          164 
          165         snprint(s, Maxmsg, "PUT %s\n", pf);
          166         conn->write(conn, (uchar*)s, strlen(s));
          167 
          168         if(buf == nil){
          169                 /* get file size */
          170                 if((fd = open(pf, OREAD)) < 0){
          171                         fprint(2, "can't open %s: %r\n", pf);
          172                         return -1;
          173                 }
          174                 len = seek(fd, 0, 2);
          175                 seek(fd, 0, 0);
          176         } else {
          177                 fd = -1;
          178         }
          179         if(len > MAXFILESIZE){
          180                 fprint(2, "implausible filesize %ld for %s\n", len, pf);
          181                 return -1;
          182         }
          183 
          184         /* send file size */
          185         snprint(s, Maxmsg, "%ld", len+AESbsize+CHK);
          186         conn->write(conn, (uchar*)s, strlen(s));
          187 
          188         /* send IV and file+XXXXX in Maxmsg chunks */
          189         ivo = AESbsize;
          190         bufi = 0;
          191         memcpy(b, IV, ivo);
          192         for(done = 0; !done; ){
          193                 if(buf == nil){
          194                         n = read(fd, b+ivo, Maxmsg-ivo);
          195                         if(n < 0){
          196                                 fprint(2, "read error on %s: %r\n", pf);
          197                                 return -1;
          198                         }
          199                 }else{
          200                         if((n = len - bufi) > Maxmsg-ivo)
          201                                 n = Maxmsg-ivo;
          202                         memcpy(b+ivo, buf+bufi, n);
          203                         bufi += n;
          204                 }
          205                 n += ivo;
          206                 ivo = 0;
          207                 if(n < Maxmsg){ /* EOF on input; append XX... */
          208                         memset(b+n, 'X', CHK);
          209                         n += CHK; /* might push n>Maxmsg */
          210                         done = 1;
          211                 }
          212                 aesCBCencrypt(b, n, &aes);
          213                 if(n > Maxmsg){
          214                         assert(done==1);
          215                         conn->write(conn, b, Maxmsg);
          216                         n -= Maxmsg;
          217                         memmove(b, b+Maxmsg, n);
          218                 }
          219                 conn->write(conn, b, n);
          220         }
          221 
          222         if(buf == nil)
          223                 close(fd);
          224         fprint(2, "saved %ld bytes\n", len);
          225 
          226         return 0;
          227 }
          228 
          229 static int
          230 removefile(SConn *conn, char *rf)
          231 {
          232         char buf[Maxmsg];
          233 
          234         if(strchr(rf, '/')){
          235                 fprint(2, "simple filenames, not paths like %s\n", rf);
          236                 return -1;
          237         }
          238 
          239         snprint(buf, Maxmsg, "RM %s\n", rf);
          240         conn->write(conn, (uchar*)buf, strlen(buf));
          241 
          242         return 0;
          243 }
          244 
          245 static int
          246 cmd(AuthConn *c, char **gf, int *Gflag, char **pf, char **rf)
          247 {
          248         ulong len;
          249         int rv = -1;
          250         uchar *memfile, *memcur, *memnext;
          251 
          252         while(*gf != nil){
          253                 if(verbose)
          254                         fprint(2, "get %s\n", *gf);
          255                 if(getfile(c->conn, *gf, *Gflag ? &memfile : nil, &len, (uchar*)c->pass, c->passlen) < 0)
          256                         goto Out;
          257                 if(*Gflag){
          258                         /* write one line at a time, as required by /mnt/factotum/ctl */
          259                         memcur = memfile;
          260                         while(len>0){
          261                                 memnext = (uchar*)strchr((char*)memcur, '\n');
          262                                 if(memnext){
          263                                         write(1, memcur, memnext-memcur+1);
          264                                         len -= memnext-memcur+1;
          265                                         memcur = memnext+1;
          266                                 }else{
          267                                         write(1, memcur, len);
          268                                         break;
          269                                 }
          270                         }
          271                         free(memfile);
          272                 }
          273                 gf++;
          274                 Gflag++;
          275         }
          276         while(*pf != nil){
          277                 if(verbose)
          278                         fprint(2, "put %s\n", *pf);
          279                 if(putfile(c->conn, *pf, nil, 0, (uchar*)c->pass, c->passlen) < 0)
          280                         goto Out;
          281                 pf++;
          282         }
          283         while(*rf != nil){
          284                 if(verbose)
          285                         fprint(2, "rm  %s\n", *rf);
          286                 if(removefile(c->conn, *rf) < 0)
          287                         goto Out;
          288                 rf++;
          289         }
          290 
          291         c->conn->write(c->conn, (uchar*)"BYE", 3);
          292         rv = 0;
          293 
          294 Out:
          295         c->conn->free(c->conn);
          296         return rv;
          297 }
          298 
          299 static int
          300 chpasswd(AuthConn *c, char *id)
          301 {
          302         ulong len;
          303         int rv = -1, newpasslen = 0;
          304         mpint *H, *Hi;
          305         uchar *memfile;
          306         char *newpass, *passck;
          307         char *list, *cur, *next, *hexHi;
          308         char *f[8], prompt[128];
          309 
          310         H = mpnew(0);
          311         Hi = mpnew(0);
          312         /* changing our password is vulnerable to connection failure */
          313         for(;;){
          314                 snprint(prompt, sizeof(prompt), "new password for %s: ", id);
          315                 newpass = readcons(prompt, nil, 1);
          316                 if(newpass == nil)
          317                         goto Out;
          318                 if(strlen(newpass) >= 7)
          319                         break;
          320                 else if(strlen(newpass) == 0){
          321                         fprint(2, "!password change aborted\n");
          322                         goto Out;
          323                 }
          324                 print("!password must be at least 7 characters\n");
          325         }
          326         newpasslen = strlen(newpass);
          327         snprint(prompt, sizeof(prompt), "retype password: ");
          328         passck = readcons(prompt, nil, 1);
          329         if(passck == nil){
          330                 fprint(2, "readcons failed\n");
          331                 goto Out;
          332         }
          333         if(strcmp(passck, newpass) != 0){
          334                 fprint(2, "passwords didn't match\n");
          335                 goto Out;
          336         }
          337 
          338         c->conn->write(c->conn, (uchar*)"CHPASS", strlen("CHPASS"));
          339         hexHi = PAK_Hi(id, newpass, H, Hi);
          340         c->conn->write(c->conn, (uchar*)hexHi, strlen(hexHi));
          341         free(hexHi);
          342         mpfree(H);
          343         mpfree(Hi);
          344 
          345         if(getfile(c->conn, ".", (uchar **)(void*)&list, &len, nil, 0) < 0){
          346                 fprint(2, "directory listing failed.\n");
          347                 goto Out;
          348         }
          349 
          350         /* Loop over files and reencrypt them; try to keep going after error */
          351         for(cur=list; (next=strchr(cur, '\n')) != nil; cur=next+1){
          352                 *next = '\0';
          353                 if(tokenize(cur, f, nelem(f))< 1)
          354                         break;
          355                 fprint(2, "reencrypting '%s'\n", f[0]);
          356                 if(getfile(c->conn, f[0], &memfile, &len, (uchar*)c->pass, c->passlen) < 0){
          357                         fprint(2, "getfile of '%s' failed\n", f[0]);
          358                         continue;
          359                 }
          360                 if(putfile(c->conn, f[0], memfile, len, (uchar*)newpass, newpasslen) < 0)
          361                         fprint(2, "putfile of '%s' failed\n", f[0]);
          362                 free(memfile);
          363         }
          364         free(list);
          365         c->conn->write(c->conn, (uchar*)"BYE", 3);
          366         rv = 0;
          367 
          368 Out:
          369         if(newpass != nil){
          370                 memset(newpass, 0, newpasslen);
          371                 free(newpass);
          372         }
          373         c->conn->free(c->conn);
          374         return rv;
          375 }
          376 
          377 static AuthConn*
          378 login(char *id, char *dest, int pass_stdin, int pass_nvram)
          379 {
          380         AuthConn *c;
          381         int fd, n, ntry = 0;
          382         char *S, *PINSTA = nil, *nl, s[Maxmsg+1], *pass;
          383 
          384         if(dest == nil){
          385                 fprint(2, "tried to login with nil dest\n");
          386                 exits("nil dest");
          387         }
          388         c = emalloc(sizeof(*c));
          389         if(pass_nvram){
          390                 /* if(readnvram(&nvr, 0) < 0) */
          391                         exits("readnvram: %r");
          392                 strecpy(c->pass, c->pass+sizeof c->pass, nvr.config);
          393         }
          394         if(pass_stdin){
          395                 n = readn(0, s, Maxmsg-2);  /* so len(PINSTA)<Maxmsg-3 */
          396                 if(n < 1)
          397                         exits("no password on standard input");
          398                 s[n] = 0;
          399                 nl = strchr(s, '\n');
          400                 if(nl){
          401                         *nl++ = 0;
          402                         PINSTA = estrdup(nl);
          403                         nl = strchr(PINSTA, '\n');
          404                         if(nl)
          405                                 *nl = 0;
          406                 }
          407                 strecpy(c->pass, c->pass+sizeof c->pass, s);
          408         }
          409         while(1){
          410                 if(verbose)
          411                         fprint(2, "dialing %s\n", dest);
          412                 if((fd = dial(dest, nil, nil, nil)) < 0){
          413                         fprint(2, "can't dial %s\n", dest);
          414                         free(c);
          415                         return nil;
          416                 }
          417                 if((c->conn = newSConn(fd)) == nil){
          418                         free(c);
          419                         return nil;
          420                 }
          421                 ntry++;
          422                 if(!pass_stdin && !pass_nvram){
          423                         pass = readcons("secstore password", nil, 1);
          424                         if(pass == nil)
          425                                 pass = estrdup("");
          426                         if(strlen(pass) >= sizeof c->pass){
          427                                 fprint(2, "password too long, skipping secstore login\n");
          428                                 exits("password too long");
          429                         }
          430                         strcpy(c->pass, pass);
          431                         memset(pass, 0, strlen(pass));
          432                         free(pass);
          433                 }
          434                 if(c->pass[0]==0){
          435                         fprint(2, "null password, skipping secstore login\n");
          436                         exits("no password");
          437                 }
          438                 if(PAKclient(c->conn, id, c->pass, &S) >= 0)
          439                         break;
          440                 c->conn->free(c->conn);
          441                 if(pass_stdin)
          442                         exits("invalid password on standard input");
          443                 if(pass_nvram)
          444                         exits("invalid password in nvram");
          445                 /* and let user try retyping the password */
          446                 if(ntry==3)
          447                         fprint(2, "Enter an empty password to quit.\n");
          448         }
          449         c->passlen = strlen(c->pass);
          450         fprint(2, "server: %s\n", S);
          451         free(S);
          452         if(readstr(c->conn, s) < 0){
          453                 c->conn->free(c->conn);
          454                 free(c);
          455                 return nil;
          456         }
          457         if(strcmp(s, "STA") == 0){
          458                 long sn;
          459                 if(pass_stdin){
          460                         if(PINSTA)
          461                                 strncpy(s+3, PINSTA, (sizeof s)-3);
          462                         else
          463                                 exits("missing PIN+SecureID on standard input");
          464                         free(PINSTA);
          465                 }else{
          466                         pass = readcons("STA PIN+SecureID", nil, 1);
          467                         if(pass == nil)
          468                                 pass = estrdup("");
          469                         strncpy(s+3, pass, (sizeof s)-4);
          470                         memset(pass, 0, strlen(pass));
          471                         free(pass);
          472                 }
          473                 sn = strlen(s+3);
          474                 if(verbose)
          475                         fprint(2, "%ld\n", sn);
          476                 c->conn->write(c->conn, (uchar*)s, sn+3);
          477                 readstr(c->conn, s);
          478         }
          479         if(strcmp(s, "OK") != 0){
          480                 fprint(2, "%s\n", s);
          481                 c->conn->free(c->conn);
          482                 free(c);
          483                 return nil;
          484         }
          485         return c;
          486 }
          487 
          488 int
          489 main(int argc, char **argv)
          490 {
          491         int chpass = 0, pass_stdin = 0, pass_nvram = 0, rc;
          492         int ngfile = 0, npfile = 0, nrfile = 0, Gflag[MAXFILES+1];
          493         char *gfile[MAXFILES], *pfile[MAXFILES], *rfile[MAXFILES];
          494         char *serve, *tcpserve, *user;
          495         AuthConn *c;
          496 
          497         serve = getenv("secstore");
          498         if(serve == nil)
          499                 serve = "secstore";
          500         user = getuser();
          501         memset(Gflag, 0, sizeof Gflag);
          502         fmtinstall('B', mpfmt);
          503         fmtinstall('H', encodefmt);
          504 
          505         ARGBEGIN{
          506         case 'c':
          507                 chpass = 1;
          508                 break;
          509         case 'G':
          510                 Gflag[ngfile]++;
          511                 /* fall through */
          512         case 'g':
          513                 if(ngfile >= MAXFILES)
          514                         exits("too many gfiles");
          515                 gfile[ngfile++] = ARGF();
          516                 if(gfile[ngfile-1] == nil)
          517                         usage();
          518                 break;
          519         case 'i':
          520                 pass_stdin = 1;
          521                 break;
          522         case 'n':
          523                 pass_nvram = 1;
          524                 break;
          525         case 'p':
          526                 if(npfile >= MAXFILES)
          527                         exits("too many pfiles");
          528                 pfile[npfile++] = ARGF();
          529                 if(pfile[npfile-1] == nil)
          530                         usage();
          531                 break;
          532         case 'r':
          533                 if(nrfile >= MAXFILES)
          534                         exits("too many rfiles");
          535                 rfile[nrfile++] = ARGF();
          536                 if(rfile[nrfile-1] == nil)
          537                         usage();
          538                 break;
          539         case 's':
          540                 serve = EARGF(usage());
          541                 break;
          542         case 'u':
          543                 user = EARGF(usage());
          544                 break;
          545         case 'v':
          546                 verbose++;
          547                 break;
          548         default:
          549                 usage();
          550                 break;
          551         }ARGEND;
          552         gfile[ngfile] = nil;
          553         pfile[npfile] = nil;
          554         rfile[nrfile] = nil;
          555 
          556         if(argc!=0 || user==nil)
          557                 usage();
          558 
          559         if(chpass && (ngfile || npfile || nrfile)){
          560                 fprint(2, "Get, put, and remove invalid with password change.\n");
          561                 exits("usage");
          562         }
          563 
          564         tcpserve = netmkaddr(serve, "tcp", "secstore");
          565         c = login(user, tcpserve, pass_stdin, pass_nvram);
          566         if(c == nil){
          567                 fprint(2, "secstore authentication failed\n");
          568                 exits("secstore authentication failed");
          569         }
          570         if(chpass)
          571                 rc = chpasswd(c, user);
          572         else
          573                 rc = cmd(c, gfile, Gflag, pfile, rfile);
          574         if(rc < 0){
          575                 fprint(2, "secstore cmd failed\n");
          576                 exits("secstore cmd failed");
          577         }
          578         exits("");
          579         return 0;
          580 }