URI:
       tsecstored.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
       ---
       tsecstored.c (8464B)
       ---
            1 #include <u.h>
            2 #include <libc.h>
            3 #include <bio.h>
            4 #include <ndb.h>
            5 #include <mp.h>
            6 #include <libsec.h>
            7 #include "SConn.h"
            8 #include "secstore.h"
            9 
           10 char *SECSTORE_DIR;
           11 char* secureidcheck(char *, char *);   /* from /sys/src/cmd/auth/ */
           12 extern char* dirls(char *path);
           13 
           14 int verbose;
           15 Ndb *db;
           16 
           17 static void
           18 usage(void)
           19 {
           20         fprint(2, "usage: secstored [-R] [-S servername] [-s tcp!*!5356] [-v] [-x netmtpt]\n");
           21         exits("usage");
           22 }
           23 
           24 static int
           25 getdir(SConn *conn, char *id)
           26 {
           27         char *ls, *s;
           28         uchar *msg;
           29         int n, len;
           30 
           31         s = emalloc(Maxmsg);
           32         snprint(s, Maxmsg, "%s/store/%s", SECSTORE_DIR, id);
           33 
           34         if((ls = dirls(s)) == nil)
           35                 len = 0;
           36         else
           37                 len = strlen(ls);
           38 
           39         /* send file size */
           40         snprint(s, Maxmsg, "%d", len);
           41         conn->write(conn, (uchar*)s, strlen(s));
           42 
           43         /* send directory listing in Maxmsg chunks */
           44         n = Maxmsg;
           45         msg = (uchar*)ls;
           46         while(len > 0){
           47                 if(len < Maxmsg)
           48                         n = len;
           49                 conn->write(conn, msg, n);
           50                 msg += n;
           51                 len -= n;
           52         }
           53         free(s);
           54         free(ls);
           55         return 0;
           56 }
           57 
           58 char *
           59 validatefile(char *f)
           60 {
           61         char *nl;
           62 
           63         if(f==nil || *f==0)
           64                 return nil;
           65         if(nl = strchr(f, '\n'))
           66                 *nl = 0;
           67         if(strchr(f,'/') != nil || strcmp(f,"..")==0 || strlen(f) >= 300){
           68                 syslog(0, LOG, "no slashes allowed: %s\n", f);
           69                 return nil;
           70         }
           71         return f;
           72 }
           73 
           74 static int
           75 getfile(SConn *conn, char *id, char *gf)
           76 {
           77         int n, gd, len;
           78         ulong mode;
           79         char *s;
           80         Dir *st;
           81 
           82         if(strcmp(gf,".")==0)
           83                 return getdir(conn, id);
           84 
           85         /* send file size */
           86         s = emalloc(Maxmsg);
           87         snprint(s, Maxmsg, "%s/store/%s/%s", SECSTORE_DIR, id, gf);
           88         gd = open(s, OREAD);
           89         if(gd < 0){
           90                 syslog(0, LOG, "can't open %s: %r\n", s);
           91                 free(s);
           92                 conn->write(conn, (uchar*)"-1", 2);
           93                 return -1;
           94         }
           95         st = dirfstat(gd);
           96         if(st == nil){
           97                 syslog(0, LOG, "can't stat %s: %r\n", s);
           98                 free(s);
           99                 conn->write(conn, (uchar*)"-1", 2);
          100                 return -1;
          101         }
          102         mode = st->mode;
          103         len = st->length;
          104         free(st);
          105         if(mode & DMDIR) {
          106                 syslog(0, LOG, "%s should be a plain file, not a directory\n", s);
          107                 free(s);
          108                 conn->write(conn, (uchar*)"-1", 2);
          109                 return -1;
          110         }
          111         if(len < 0 || len > MAXFILESIZE){
          112                 syslog(0, LOG, "implausible filesize %d for %s\n", len, gf);
          113                 free(s);
          114                 conn->write(conn, (uchar*)"-3", 2);
          115                 return -1;
          116         }
          117         snprint(s, Maxmsg, "%d", len);
          118         conn->write(conn, (uchar*)s, strlen(s));
          119 
          120         /* send file in Maxmsg chunks */
          121         while(len > 0){
          122                 n = read(gd, s, Maxmsg);
          123                 if(n <= 0){
          124                         syslog(0, LOG, "read error on %s: %r\n", gf);
          125                         free(s);
          126                         return -1;
          127                 }
          128                 conn->write(conn, (uchar*)s, n);
          129                 len -= n;
          130         }
          131         close(gd);
          132         free(s);
          133         return 0;
          134 }
          135 
          136 static int
          137 putfile(SConn *conn, char *id, char *pf)
          138 {
          139         int n, nw, pd;
          140         long len;
          141         char s[Maxmsg+1];
          142 
          143         /* get file size */
          144         n = readstr(conn, s);
          145         if(n < 0){
          146                 syslog(0, LOG, "remote: %s: %r\n", s);
          147                 return -1;
          148         }
          149         len = atoi(s);
          150         if(len == -1){
          151                 syslog(0, LOG, "remote file %s does not exist\n", pf);
          152                 return -1;
          153         }else if(len < 0 || len > MAXFILESIZE){
          154                 syslog(0, LOG, "implausible filesize %ld for %s\n", len, pf);
          155                 return -1;
          156         }
          157 
          158         /* get file in Maxmsg chunks */
          159         if(strchr(pf,'/') != nil || strcmp(pf,"..")==0){
          160                 syslog(0, LOG, "no slashes allowed: %s\n", pf);
          161                 return -1;
          162         }
          163         snprint(s, Maxmsg, "%s/store/%s/%s", SECSTORE_DIR, id, pf);
          164         pd = create(s, OWRITE, 0660);
          165         if(pd < 0){
          166                 syslog(0, LOG, "can't open %s: %r\n", s);
          167                 return -1;
          168         }
          169         while(len > 0){
          170                 n = conn->read(conn, (uchar*)s, Maxmsg);
          171                 if(n <= 0){
          172                         syslog(0, LOG, "empty file chunk\n");
          173                         return -1;
          174                 }
          175                 nw = write(pd, s, n);
          176                 if(nw != n){
          177                         syslog(0, LOG, "write error on %s: %r", pf);
          178                         return -1;
          179                 }
          180                 len -= n;
          181         }
          182         close(pd);
          183         return 0;
          184 
          185 }
          186 
          187 static int
          188 removefile(SConn *conn, char *id, char *f)
          189 {
          190         Dir *d;
          191         char buf[Maxmsg];
          192 
          193         snprint(buf, Maxmsg, "%s/store/%s/%s", SECSTORE_DIR, id, f);
          194 
          195         if((d = dirstat(buf)) == nil){
          196                 snprint(buf, sizeof buf, "remove failed: %r");
          197                 writerr(conn, buf);
          198                 return -1;
          199         }else if(d->mode & DMDIR){
          200                 snprint(buf, sizeof buf, "can't remove a directory");
          201                 writerr(conn, buf);
          202                 free(d);
          203                 return -1;
          204         }
          205 
          206         free(d);
          207         if(remove(buf) < 0){
          208                 snprint(buf, sizeof buf, "remove failed: %r");
          209                 writerr(conn, buf);
          210                 return -1;
          211         }
          212         return 0;
          213 }
          214 
          215 /* given line directory from accept, returns ipaddr!port */
          216 static char*
          217 remoteIP(char *ldir)
          218 {
          219         int fd, n;
          220         char rp[100], ap[500];
          221 
          222         snprint(rp, sizeof rp, "%s/remote", ldir);
          223         fd = open(rp, OREAD);
          224         if(fd < 0)
          225                 return strdup("?!?");
          226         n = read(fd, ap, sizeof ap);
          227         if(n <= 0 || n == sizeof ap){
          228                 fprint(2, "error %d reading %s: %r\n", n, rp);
          229                 return strdup("?!?");
          230         }
          231         close(fd);
          232         ap[n--] = 0;
          233         if(ap[n] == '\n')
          234                 ap[n] = 0;
          235         return strdup(ap);
          236 }
          237 
          238 static int
          239 dologin(int fd, char *S, int forceSTA)
          240 {
          241         int i, n, rv;
          242         char *file, *mess;
          243         char msg[Maxmsg+1];
          244         PW *pw;
          245         SConn *conn;
          246 
          247         pw = nil;
          248         rv = -1;
          249 
          250         /* collect the first message */
          251         if((conn = newSConn(fd)) == nil)
          252                 return -1;
          253         if(readstr(conn, msg) < 0){
          254                 fprint(2, "remote: %s: %r\n", msg);
          255                 writerr(conn, "can't read your first message");
          256                 goto Out;
          257         }
          258 
          259         /* authenticate */
          260         if(PAKserver(conn, S, msg, &pw) < 0){
          261                 if(pw != nil)
          262                         syslog(0, LOG, "secstore denied for %s", pw->id);
          263                 goto Out;
          264         }
          265         if((forceSTA || pw->status&STA) != 0){
          266                 conn->write(conn, (uchar*)"STA", 3);
          267                 if(readstr(conn, msg) < 10 || strncmp(msg, "STA", 3) != 0){
          268                         syslog(0, LOG, "no STA from %s", pw->id);
          269                         goto Out;
          270                 }
          271                 mess = secureidcheck(pw->id, msg+3);
          272                 if(mess != nil){
          273                         syslog(0, LOG, "secureidcheck denied %s because %s", pw->id, mess);
          274                         goto Out;
          275                 }
          276         }
          277         conn->write(conn, (uchar*)"OK", 2);
          278         syslog(0, LOG, "AUTH %s", pw->id);
          279 
          280         /* perform operations as asked */
          281         while((n = readstr(conn, msg)) > 0){
          282                 syslog(0, LOG, "[%s] %s", pw->id, msg);
          283 
          284                 if(strncmp(msg, "GET ", 4) == 0){
          285                         file = validatefile(msg+4);
          286                         if(file==nil || getfile(conn, pw->id, file) < 0)
          287                                 goto Err;
          288 
          289                 }else if(strncmp(msg, "PUT ", 4) == 0){
          290                         file = validatefile(msg+4);
          291                         if(file==nil || putfile(conn, pw->id, file) < 0){
          292                                 syslog(0, LOG, "failed PUT %s/%s", pw->id, file);
          293                                 goto Err;
          294                         }
          295 
          296                 }else if(strncmp(msg, "RM ", 3) == 0){
          297                         file = validatefile(msg+3);
          298                         if(file==nil || removefile(conn, pw->id, file) < 0){
          299                                 syslog(0, LOG, "failed RM %s/%s", pw->id, file);
          300                                 goto Err;
          301                         }
          302 
          303                 }else if(strncmp(msg, "CHPASS", 6) == 0){
          304                         if(readstr(conn, msg) < 0){
          305                                 syslog(0, LOG, "protocol botch CHPASS for %s", pw->id);
          306                                 writerr(conn, "protocol botch while setting PAK");
          307                                 goto Out;
          308                         }
          309                         pw->Hi = strtomp(msg, nil, 64, pw->Hi);
          310                         for(i=0; i < 4 && putPW(pw) < 0; i++)
          311                                 syslog(0, LOG, "password change failed for %s (%d): %r", pw->id, i);
          312                         if(i==4)
          313                                 goto Out;
          314 
          315                 }else if(strncmp(msg, "BYE", 3) == 0){
          316                         rv = 0;
          317                         break;
          318 
          319                 }else{
          320                         writerr(conn, "unrecognized operation");
          321                         break;
          322                 }
          323 
          324         }
          325         if(n <= 0)
          326                 syslog(0, LOG, "%s closed connection without saying goodbye\n", pw->id);
          327 
          328 Out:
          329         freePW(pw);
          330         conn->free(conn);
          331         return rv;
          332 Err:
          333         writerr(conn, "operation failed");
          334         goto Out;
          335 }
          336 
          337 void
          338 main(int argc, char **argv)
          339 {
          340         int afd, dfd, lcfd, forceSTA = 0;
          341         char adir[40], ldir[40], *remote;
          342         char *serve = "tcp!*!5356", *p, aserve[128];
          343         char *S = "secstore";
          344         char *dbpath;
          345         Ndb *db2;
          346 
          347         S = sysname();
          348         SECSTORE_DIR = unsharp("#9/secstore");
          349 /*        setnetmtpt(net, sizeof(net), nil); */
          350         ARGBEGIN{
          351         case 'R':
          352                 forceSTA = 1;
          353                 break;
          354         case 's':
          355                 serve = EARGF(usage());
          356                 break;
          357         case 'S':
          358                 S = EARGF(usage());
          359                 break;
          360         case 'x':
          361                 p = ARGF();
          362                 if(p == nil)
          363                         usage();
          364                 USED(p);
          365         /*        setnetmtpt(net, sizeof(net), p); */
          366                 break;
          367         case 'v':
          368                 verbose++;
          369                 break;
          370         default:
          371                 usage();
          372         }ARGEND;
          373 
          374         if(!verbose)
          375                 switch(rfork(RFNOTEG|RFPROC|RFFDG)) {
          376                 case -1:
          377                         sysfatal("fork: %r");
          378                 case 0:
          379                         break;
          380                 default:
          381                         exits(0);
          382                 }
          383 
          384         snprint(aserve, sizeof aserve, "%s", serve);
          385         afd = announce(aserve, adir);
          386         if(afd < 0)
          387                 sysfatal("%s: %r\n", aserve);
          388         syslog(0, LOG, "ANNOUNCE %s", aserve);
          389         for(;;){
          390                 if((lcfd = listen(adir, ldir)) < 0)
          391                         exits("can't listen");
          392                 switch(rfork(RFPROC|RFFDG|RFNOWAIT)){
          393                 case -1:
          394                         fprint(2, "secstore forking: %r\n");
          395                         close(lcfd);
          396                         break;
          397                 case 0:
          398                         /* "/lib/ndb/common.radius does not exist" if db set before fork */
          399                         db = ndbopen(dbpath=unsharp("#9/ndb/auth"));
          400                         if(db == 0)
          401                                 syslog(0, LOG, "no ndb/auth");
          402                         db2 = ndbopen(0);
          403                         if(db2 == 0)
          404                                 syslog(0, LOG, "no ndb/local");
          405                         db = ndbcat(db, db2);
          406                         if((dfd = accept(lcfd, ldir)) < 0)
          407                                 exits("can't accept");
          408                         alarm(30*60*1000);         /* 30 min */
          409                         remote = remoteIP(ldir);
          410                         syslog(0, LOG, "secstore from %s", remote);
          411                         free(remote);
          412                         dologin(dfd, S, forceSTA);
          413                         exits(nil);
          414                 default:
          415                         close(lcfd);
          416                         break;
          417                 }
          418         }
          419 }