ref: 11954a19a6252415c4eae086cc6b09bfbbc1aa9f
parent: 562fd5b134dc3a9e22e32e53f81858954efd4bf1
author: aiju <devnull@localhost>
date: Fri Apr 28 12:34:24 EDT 2017
sshfs: look up uid/gid from /etc/^(passwd group)
--- a/sys/src/cmd/sshfs.c
+++ b/sys/src/cmd/sshfs.c
@@ -15,7 +15,8 @@
MAXWRITE = 32768,
MAXATTRIB = 64,
VERSION = 3,
- MAXREQID = 32
+ MAXREQID = 32,
+ HASH = 64
};
enum {
@@ -84,6 +85,7 @@
typedef struct SFid SFid;
typedef struct SReq SReq;
+typedef struct IDEnt IDEnt;
struct SFid {
RWLock;
@@ -103,6 +105,13 @@
SReq *next;
};
+struct IDEnt {
+ char *name;
+ int id;
+ IDEnt *next;
+};
+IDEnt *uidtab[HASH], *gidtab[HASH];
+
int rdfd, wrfd;
SReq *sreqrd[MAXREQID];
QLock sreqidlock;
@@ -154,7 +163,35 @@
return 0;
}
+char *
+idlookup(IDEnt **tab, int id)
+{
+ IDEnt *p;
+
+ for(p = tab[(ulong)id % HASH]; p != nil; p = p->next)
+ if(p->id == id)
+ return strdup(p->name);
+ return smprint("%d", id);
+}
+
int
+namelookup(IDEnt **tab, char *name)
+{
+ IDEnt *p;
+ int i;
+ char *q;
+
+ for(i = 0; i < HASH; i++)
+ for(p = tab[i]; p != nil; p = p->next)
+ if(strcmp(p->name, name) == 0)
+ return p->id;
+ i = strtol(name, &q, 10);
+ if(*q == 0) return i;
+ werrstr("unknown %s '%s'", tab == uidtab ? "user" : "group", name);
+ return -1;
+}
+
+int
vpack(uchar *p, int n, char *fmt, va_list a)
{
uchar *p0 = p, *e = p+n;
@@ -471,8 +508,8 @@
}
if((flags & SSH_FILEXFER_ATTR_UIDGID) != 0){
rc = unpack(p, ep - p, "uu", &uid, &gid); if(rc < 0) return -1; p += rc;
- d->uid = smprint("%d", uid);
- d->gid = smprint("%d", gid);
+ d->uid = idlookup(uidtab, uid);
+ d->gid = idlookup(gidtab, gid);
}else{
d->uid = strdup("sshfs");
d->gid = strdup("sshfs");
@@ -502,7 +539,6 @@
{
int rc;
uchar *r, *p, *e;
- char *pp;
u32int fl;
int uid, gid;
@@ -518,19 +554,15 @@
if(d->uid != nil && *d->uid != 0 || d->gid != nil && *d->gid != 0){
/* FIXME: sending -1 for "don't change" works with openssh, but violates the spec */
if(d->uid != nil && *d->uid != 0){
- uid = strtol(d->uid, &pp, 10);
- if(*pp != 0){
- werrstr("uid not a number");
+ uid = namelookup(uidtab, d->uid);
+ if(uid == -1)
return -1;
- }
}else
uid = -1;
if(d->gid != nil && *d->gid != 0){
- gid = strtol(d->gid, &pp, 10);
- if(*pp != 0){
- werrstr("gid not a number");
+ gid = namelookup(gidtab, d->gid);
+ if(gid == -1)
return -1;
- }
}else
gid = -1;
fl |= SSH_FILEXFER_ATTR_UIDGID;
@@ -1113,6 +1145,79 @@
.end sshfsend
};
+char *
+readfile(char *fn)
+{
+ char *hand, *dat;
+ int handn, datn;
+ u32int code;
+ char *p;
+ int off;
+
+ if(fn == nil) return nil;
+ sendpkt("busuu", SSH_FXP_OPEN, 0, fn, strlen(fn), SSH_FXF_READ, 0);
+ if(recvpkt() != SSH_FXP_HANDLE) return nil;
+ if(unpack(rxpkt, rxlen, "_____s", &dat, &handn) < 0) return nil;
+ hand = emalloc9p(handn);
+ memcpy(hand, dat, handn);
+ off = 0;
+ p = nil;
+ for(;;){
+ sendpkt("busvu", SSH_FXP_READ, 0, hand, handn, (uvlong)off, MAXWRITE);
+ switch(recvpkt()){
+ case SSH_FXP_STATUS:
+ if(unpack(rxpkt, rxlen, "_____u", &code) < 0) goto err;
+ print("%d\n", code);
+ if(code == SSH_FX_EOF) goto out;
+ default:
+ goto err;
+ case SSH_FXP_DATA:
+ if(unpack(rxpkt, rxlen, "_____s", &dat, &datn) < 0) goto err;
+ break;
+ }
+ p = erealloc9p(p, off + datn + 1);
+ memcpy(p + off, dat, datn);
+ off += datn;
+ p[off] = 0;
+ }
+err:
+ p = nil;
+out:
+ sendpkt("bus", SSH_FXP_CLOSE, 0, hand, handn);
+ free(hand);
+ recvpkt();
+ return p;
+}
+
+void
+passwdparse(IDEnt **tab, char *s)
+{
+ char *p;
+ char *n;
+ int id;
+ IDEnt *e, **b;
+
+ p = s;
+ for(;;){
+ n = p;
+ p = strpbrk(p, ":\n"); if(p == nil) break; if(*p != ':'){ p++; continue; }
+ *p = 0;
+ p = strpbrk(p+1, ":\n");
+ p = strpbrk(p, ":\n"); if(p == nil) break; if(*p != ':'){ p++; continue; }
+ id = strtol(p+1, &p, 10);
+ p = strchr(p, '\n');
+ if(p == nil) break;
+ p++;
+ e = emalloc9p(sizeof(IDEnt));
+ e->name = strdup(n);
+ e->id = id;
+ b = &tab[((ulong)e->id) % HASH];
+ e->next = *b;
+ *b = e;
+ }
+ free(s);
+}
+
int pfd[2];
int sshargc;
char **sshargv;
@@ -1137,9 +1242,10 @@
void
usage(void)
{
- fprint(2, "usage: %s [-abdR] [-s service] [-m mtpt] [-- ssh options] host\n", argv0);
- fprint(2, " %s [-abdR] [-s service] [-m mtpt] -c cmdline\n", argv0);
- fprint(2, " %s [-abdR] [-s service] [-m mtpt] -p\n", argv0);
+ static char *common = "[-abdRUG] [-s service] [-m mtpt] [-u uidfile] [-g gidfile]";
+ fprint(2, "usage: %s %s [-- ssh-options] host\n", argv0, common);
+ fprint(2, " %s %s -c cmdline\n", argv0, common);
+ fprint(2, " %s %s -p\n", argv0, common);
exits("usage");
}
@@ -1150,10 +1256,13 @@
static int pflag, cflag;
static char *svc, *mtpt;
static int mflag;
+ static char *uidfile, *gidfile;
fmtinstall(L'Σ', fxpfmt);
mtpt = "/n/ssh";
+ uidfile = "/etc/passwd";
+ gidfile = "/etc/group";
ARGBEGIN{
case 'R': readonly++; break;
case 'd': debug++; chatty9p++; break;
@@ -1163,6 +1272,10 @@
case 'a': mflag |= MAFTER; break;
case 'b': mflag |= MBEFORE; break;
case 'm': mtpt = EARGF(usage()); break;
+ case 'u': uidfile = EARGF(usage()); break;
+ case 'U': uidfile = nil; break;
+ case 'g': gidfile = EARGF(usage()); break;
+ case 'G': gidfile = nil; break;
default: usage();
}ARGEND;
@@ -1197,6 +1310,9 @@
sendpkt("bu", SSH_FXP_INIT, VERSION);
if(recvpkt() != SSH_FXP_VERSION || unpack(rxpkt, rxlen, "_u", &x) < 0) sysfatal("received garbage");
if(x != VERSION) sysfatal("server replied with incompatible version %d", x);
+
+ passwdparse(uidtab, readfile(uidfile));
+ passwdparse(gidtab, readfile(gidfile));
procrfork(sendproc, 0, mainstacksize, RFNOTEG);
procrfork(recvproc, 0, mainstacksize, RFNOTEG);