plan9fox/sys/src/cmd/cifs/trans.c

786 lines
17 KiB
C

#include <u.h>
#include <libc.h>
#include <fcall.h>
#include <thread.h>
#include <9p.h>
#include "cifs.h"
#include "remsmb.h"
#include "apinums.h"
static Pkt *
thdr(Session *s, Share *sp)
{
Pkt *p;
p = cifshdr(s, sp, SMB_COM_TRANSACTION);
p->tbase = pl16(p, 0); /* 0 Total parameter bytes to be sent, filled later */
pl16(p, 0); /* 2 Total data bytes to be sent, filled later */
pl16(p, 64); /* 4 Max parameter to return */
pl16(p, s->mtu - T2HDRLEN - 128); /* 6 Max data to return */
pl16(p, 1); /* 8 Max setup count to return */
pl16(p, 0); /* 10 Flags */
pl32(p, 1000); /* 12 Timeout (ms) */
pl16(p, 0); /* 16 Reserved */
pl16(p, 0); /* 18 Parameter count, filled later */
pl16(p, 0); /* 20 Parameter offset, filled later */
pl16(p, 0); /* 22 Data count, filled later */
pl16(p, 0); /* 24 Data offset, filled later */
pl16(p, 0); /* 26 Setup count (in words) */
pbytes(p); /* end of cifs words section */
return p;
}
static void
ptparam(Pkt *p)
{
uchar *pos;
if(((p->pos - p->tbase) % 2) != 0)
p8(p, 0); /* pad to word boundry */
pos = p->pos;
p->pos = p->tbase + 20;
pl16(p, pos - p->buf - NBHDRLEN); /* param offset */
p->tparam = p->pos = pos;
}
static void
ptdata(Pkt *p)
{
uchar *pos = p->pos;
assert(p->tparam != 0);
if(((p->pos - p->tbase) % 2) != 0)
p8(p, 0); /* pad to word boundry */
p->pos = p->tbase + 0;
pl16(p, pos - p->tparam); /* total param count */
p->pos = p->tbase + 18;
pl16(p, pos - p->tparam); /* param count */
p->pos = p->tbase + 24;
pl16(p, pos - p->buf - NBHDRLEN); /* data offset */
p->tdata = p->pos = pos;
}
static int
trpc(Pkt *p)
{
int got;
uchar *pos = p->pos;
assert(p->tbase != 0);
assert(p->tdata != 0);
p->pos = p->tbase + 2;
pl16(p, pos - p->tdata); /* total data count */
p->pos = p->tbase + 22;
pl16(p, pos - p->tdata); /* data count */
p->pos = pos;
if((got = cifsrpc(p)) == -1)
return -1;
gl16(p); /* Total parameter count */
gl16(p); /* Total data count */
gl16(p); /* Reserved */
gl16(p); /* Parameter count in this buffer */
p->tparam = p->buf + NBHDRLEN + gl16(p); /* Parameter offset */
gl16(p); /* Parameter displacement */
gl16(p); /* Data count (this buffer); */
p->tdata = p->buf + NBHDRLEN + gl16(p); /* Data offset */
gl16(p); /* Data displacement */
g8(p); /* Setup count */
g8(p); /* Reserved */
return got;
}
static void
gtparam(Pkt *p)
{
p->pos = p->tparam;
}
static void
gtdata(Pkt *p)
{
p->pos = p->tdata;
}
int
RAPshareenum(Session *s, Share *sp, Share **ent)
{
int ngot = 0, err, navail, nret;
char tmp[1024];
Pkt *p;
Share *q;
p = thdr(s, sp);
pstr(p, "\\PIPE\\LANMAN");
ptparam(p);
pl16(p, API_WShareEnum);
pascii(p, REMSmb_NetShareEnum_P); /* request descriptor */
pascii(p, REMSmb_share_info_0); /* reply descriptor */
pl16(p, 0); /* detail level */
pl16(p, s->mtu - 1024); /* receive buffer length */
ptdata(p);
if(trpc(p) == -1){
free(p);
return -1;
}
gtparam(p);
err = gl16(p); /* error code */
gl16(p); /* rx buffer offset */
nret = gl16(p); /* number of entries returned */
navail = gl16(p); /* number of entries available */
if(err && err != RAP_ERR_MOREINFO){
werrstr("%s", raperrstr(err));
free(p);
return -1;
}
if(ngot == 0){
*ent = emalloc9p(sizeof(Share) * navail);
memset(*ent, 0, sizeof(Share) * navail);
}
q = *ent + ngot;
for (; ngot < navail && nret--; ngot++){
gmem(p, tmp, 13); /* name */
tmp[13] = 0;
q->name = estrdup9p(tmp);
q++;
}
if(ngot < navail)
fprint(2, "%s: %d/%d - share list incomplete\n", argv0, ngot, navail);
free(p);
return ngot;
}
int
RAPshareinfo(Session *s, Share *sp, char *share, Shareinfo2 *si2p)
{
int conv, err;
char tmp[1024];
Pkt *p;
p = thdr(s, sp);
pstr(p, "\\PIPE\\LANMAN");
ptparam(p);
pl16(p, API_WShareGetInfo);
pascii(p, REMSmb_NetShareGetInfo_P); /* request descriptor */
pascii(p, REMSmb_share_info_2); /* reply descriptor */
pascii(p, share);
pl16(p, 1); /* detail level */
pl16(p, s->mtu - 1024); /* receive buffer length */
ptdata(p);
if(trpc(p) == -1){
free(p);
return -1;
}
gtparam(p);
err = gl16(p); /* error code */
conv = gl16(p); /* rx buffer offset */
gl16(p); /* number of entries returned */
gl16(p); /* number of entries available */
if(err){
werrstr("%s", raperrstr(err));
free(p);
return -1;
}
memset(si2p, 0, sizeof(Shareinfo2));
gmem(p, tmp, 13);
tmp[13] = 0;
g8(p); /* padding */
si2p->name = estrdup9p(tmp);
si2p->type = gl16(p);
gconv(p, conv, tmp, sizeof tmp);
si2p->comment = estrdup9p(tmp);
gl16(p); /* comment offset high (unused) */
si2p->perms = gl16(p);
si2p->maxusrs = gl16(p);
si2p->activeusrs = gl16(p);
gconv(p, conv, tmp, sizeof tmp);
si2p->path = estrdup9p(tmp);
gl16(p); /* path offset high (unused) */
gmem(p, tmp, 9);
tmp[9] = 0;
si2p->passwd = estrdup9p(tmp);
free(p);
return 0;
}
/*
* Tried to split sessionenum into two passes, one getting the names
* of the connected workstations and the other collecting the detailed info,
* however API_WSessionGetInfo doesn't seem to work agains win2k3 for infolevel
* ten and infolevel one and two are priviledged calls. This means this code
* will work for small numbers of sessions agains win2k3 and fail for samba 3.0
* as it supports info levels zero and two only.
*/
int
RAPsessionenum(Session *s, Share *sp, Sessinfo **sip)
{
int ngot = 0, conv, err, navail, nret;
char tmp[1024];
Pkt *p;
Sessinfo *q;
p = thdr(s, sp);
pstr(p, "\\PIPE\\LANMAN");
ptparam(p);
pl16(p, API_WSessionEnum);
pascii(p, REMSmb_NetSessionEnum_P); /* request descriptor */
pascii(p, REMSmb_session_info_10); /* reply descriptor */
pl16(p, 10); /* detail level */
pl16(p, s->mtu - 1024); /* receive buffer length */
ptdata(p);
if(trpc(p) == -1){
free(p);
return -1;
}
gtparam(p);
err = gl16(p); /* error code */
conv = gl16(p); /* rx buffer offset */
nret = gl16(p); /* number of entries returned */
navail = gl16(p); /* number of entries available */
if(err && err != RAP_ERR_MOREINFO){
werrstr("%s", raperrstr(err));
free(p);
return -1;
}
if(ngot == 0){
*sip = emalloc9p(sizeof(Sessinfo) * navail);
memset(*sip, 0, sizeof(Sessinfo) * navail);
}
q = *sip + ngot;
while(nret-- != 0){
gconv(p, conv, tmp, sizeof tmp);
q->wrkstn = estrdup9p(tmp);
gconv(p, conv, tmp, sizeof tmp);
q->user = estrdup9p(tmp);
q->sesstime = gl32(p);
q->idletime = gl32(p);
ngot++;
q++;
}
if(ngot < navail)
fprint(2, "warning: %d/%d - session list incomplete\n", ngot, navail);
free(p);
return ngot;
}
int
RAPgroupenum(Session *s, Share *sp, Namelist **nlp)
{
int ngot, err, navail, nret;
char tmp[1024];
Pkt *p;
Namelist *q;
ngot = 0;
p = thdr(s, sp);
pstr(p, "\\PIPE\\LANMAN");
ptparam(p);
pl16(p, API_WGroupEnum);
pascii(p, REMSmb_NetGroupEnum_P); /* request descriptor */
pascii(p, REMSmb_group_info_0); /* reply descriptor */
pl16(p, 0); /* detail level */
pl16(p, s->mtu - 1024); /* receive buffer length */
ptdata(p);
if(trpc(p) == -1){
free(p);
return -1;
}
gtparam(p);
err = gl16(p); /* error code */
gl16(p); /* rx buffer offset */
nret = gl16(p); /* number of entries returned */
navail = gl16(p); /* number of entries available */
if(err && err != RAP_ERR_MOREINFO){
werrstr("%s", raperrstr(err));
free(p);
return -1;
}
*nlp = emalloc9p(sizeof(Namelist) * navail);
memset(*nlp, 0, sizeof(Namelist) * navail);
q = *nlp + ngot;
while(ngot < navail && nret--){
gmem(p, tmp, 21);
tmp[21] = 0;
q->name = estrdup9p(tmp);
ngot++;
q++;
if(p->pos >= p->eop) /* Windows seems to lie somtimes */
break;
}
free(p);
return ngot;
}
int
RAPgroupusers(Session *s, Share *sp, char *group, Namelist **nlp)
{
int ngot, err, navail, nret;
char tmp[1024];
Pkt *p;
Namelist *q;
ngot = 0;
p = thdr(s, sp);
pstr(p, "\\PIPE\\LANMAN");
ptparam(p);
pl16(p, API_WGroupGetUsers);
pascii(p, REMSmb_NetGroupGetUsers_P); /* request descriptor */
pascii(p, REMSmb_user_info_0); /* reply descriptor */
pascii(p, group); /* group name for list */
pl16(p, 0); /* detail level */
pl16(p, s->mtu - 1024); /* receive buffer length */
ptdata(p);
if(trpc(p) == -1){
free(p);
return -1;
}
gtparam(p);
err = gl16(p); /* error code */
gl16(p); /* rx buffer offset */
nret = gl16(p); /* number of entries returned */
navail = gl16(p); /* number of entries available */
if(err && err != RAP_ERR_MOREINFO){
werrstr("%s", raperrstr(err));
free(p);
return -1;
}
*nlp = emalloc9p(sizeof(Namelist) * navail);
memset(*nlp, 0, sizeof(Namelist) * navail);
q = *nlp + ngot;
while(ngot < navail && nret--){
gmem(p, tmp, 21);
tmp[21] = 0;
q->name = estrdup9p(tmp);
ngot++;
q++;
if(p->pos >= p->eop) /* Windows seems to lie somtimes */
break;
}
free(p);
return ngot;
}
int
RAPuserenum(Session *s, Share *sp, Namelist **nlp)
{
int ngot, err, navail, nret;
char tmp[1024];
Pkt *p;
Namelist *q;
ngot = 0;
p = thdr(s, sp);
pstr(p, "\\PIPE\\LANMAN");
ptparam(p);
pl16(p, API_WUserEnum);
pascii(p, REMSmb_NetUserEnum_P); /* request descriptor */
pascii(p, REMSmb_user_info_0); /* reply descriptor */
pl16(p, 0); /* detail level */
pl16(p, s->mtu - 1024); /* receive buffer length */
ptdata(p);
if(trpc(p) == -1){
free(p);
return -1;
}
gtparam(p);
err = gl16(p); /* error code */
gl16(p); /* rx buffer offset */
nret = gl16(p); /* number of entries returned */
navail = gl16(p); /* number of entries available */
if(err && err != RAP_ERR_MOREINFO){
werrstr("%s", raperrstr(err));
free(p);
return -1;
}
*nlp = emalloc9p(sizeof(Namelist) * navail);
memset(*nlp, 0, sizeof(Namelist) * navail);
q = *nlp + ngot;
while(ngot < navail && nret--){
gmem(p, tmp, 21);
tmp[21] = 0;
q->name = estrdup9p(tmp);
ngot++;
q++;
if(p->pos >= p->eop) /* Windows seems to lie somtimes */
break;
}
free(p);
return ngot;
}
int
RAPuserenum2(Session *s, Share *sp, Namelist **nlp)
{
int ngot, resume, err, navail, nret;
char tmp[1024];
Pkt *p;
Namelist *q;
ngot = 0;
resume = 0;
more:
p = thdr(s, sp);
pstr(p, "\\PIPE\\LANMAN");
ptparam(p);
pl16(p, API_WUserEnum2);
pascii(p, REMSmb_NetUserEnum2_P); /* request descriptor */
pascii(p, REMSmb_user_info_0); /* reply descriptor */
pl16(p, 0); /* detail level */
pl16(p, s->mtu - 1024); /* receive buffer length */
pl32(p, resume); /* resume key to allow multiple fetches */
ptdata(p);
if(trpc(p) == -1){
free(p);
return -1;
}
gtparam(p);
err = gl16(p); /* error code */
gl16(p); /* rx buffer offset */
resume = gl32(p); /* resume key returned */
nret = gl16(p); /* number of entries returned */
navail = gl16(p); /* number of entries available */
if(err && err != RAP_ERR_MOREINFO){
werrstr("%s", raperrstr(err));
free(p);
return -1;
}
if(ngot == 0){
*nlp = emalloc9p(sizeof(Namelist) * navail);
memset(*nlp, 0, sizeof(Namelist) * navail);
}
q = *nlp + ngot;
while(ngot < navail && nret--){
gmem(p, tmp, 21);
tmp[21] = 0;
q->name = estrdup9p(tmp);
ngot++;
q++;
if(p->pos >= p->eop) /* Windows seems to lie somtimes */
break;
}
free(p);
if(ngot < navail)
goto more;
return ngot;
}
int
RAPuserinfo(Session *s, Share *sp, char *user, Userinfo *uip)
{
int conv, err;
char tmp[1024];
Pkt *p;
p = thdr(s, sp);
pstr(p, "\\PIPE\\LANMAN");
ptparam(p);
pl16(p, API_WUserGetInfo);
pascii(p, REMSmb_NetUserGetInfo_P); /* request descriptor */
pascii(p, REMSmb_user_info_10); /* reply descriptor */
pascii(p, user); /* username */
pl16(p, 10); /* detail level */
pl16(p, s->mtu - 1024); /* receive buffer length */
ptdata(p);
if(trpc(p) == -1){
free(p);
return -1;
}
gtparam(p);
err = gl16(p); /* error code */
conv = gl16(p); /* rx buffer offset */
gl16(p); /* number of entries returned */
gl16(p); /* number of entries available */
if(err && err != RAP_ERR_MOREINFO){
werrstr("%s", raperrstr(err));
free(p);
return -1;
}
gmem(p, tmp, 21);
tmp[21] = 0;
uip->user = estrdup9p(tmp);
g8(p); /* padding */
gconv(p, conv, tmp, sizeof tmp);
uip->comment = estrdup9p(tmp);
gconv(p, conv, tmp, sizeof tmp);
uip->user_comment = estrdup9p(tmp);
gconv(p, conv, tmp, sizeof tmp);
uip->fullname = estrdup9p(tmp);
free(p);
return 0;
}
/*
* This works agains win2k3 but fails
* against XP with the undocumented error 71/0x47
*/
int
RAPServerenum2(Session *s, Share *sp, char *workgroup, int type, int *more,
Serverinfo **si)
{
int ngot = 0, conv, err, nret, navail;
char tmp[1024];
Pkt *p;
Serverinfo *q;
p = thdr(s, sp);
pstr(p, "\\PIPE\\LANMAN");
ptparam(p);
pl16(p, API_NetServerEnum2);
pascii(p, REMSmb_NetServerEnum2_P); /* request descriptor */
pascii(p, REMSmb_server_info_1); /* reply descriptor */
pl16(p, 1); /* detail level */
pl16(p, s->mtu - 1024); /* receive buffer length */
pl32(p, type);
pascii(p, workgroup);
ptdata(p);
if(trpc(p) == -1){
free(p);
return -1;
}
gtparam(p);
err = gl16(p); /* error code */
conv = gl16(p); /* rx buffer offset */
nret = gl16(p); /* number of entries returned */
navail = gl16(p); /* number of entries available */
if(err && err != RAP_ERR_MOREINFO){
werrstr("%s", raperrstr(err));
free(p);
return -1;
}
*si = emalloc9p(sizeof(Serverinfo) * navail);
memset(*si, 0, sizeof(Serverinfo) * navail);
q = *si;
for (; nret-- != 0 && ngot < navail; ngot++){
gmem(p, tmp, 16);
tmp[16] = 0;
q->name = estrdup9p(tmp);
q->major = g8(p);
q->minor = g8(p);
q->type = gl32(p);
gconv(p, conv, tmp, sizeof tmp);
q->comment = estrdup9p(tmp);
q++;
}
free(p);
*more = err == RAP_ERR_MOREINFO;
return ngot;
}
int
RAPServerenum3(Session *s, Share *sp, char *workgroup, int type, int last,
Serverinfo *si)
{
int conv, err, ngot, nret, navail;
char *first, tmp[1024];
Pkt *p;
Serverinfo *q;
ngot = last +1;
first = si[last].name;
more:
p = thdr(s, sp);
pstr(p, "\\PIPE\\LANMAN");
ptparam(p);
pl16(p, API_NetServerEnum3);
pascii(p, REMSmb_NetServerEnum3_P); /* request descriptor */
pascii(p, REMSmb_server_info_1); /* reply descriptor */
pl16(p, 1); /* detail level */
pl16(p, s->mtu - 1024); /* receive buffer length */
pl32(p, type);
pascii(p, workgroup);
pascii(p, first);
ptdata(p);
if(trpc(p) == -1){
free(p);
return -1;
}
gtparam(p);
err = gl16(p); /* error code */
conv = gl16(p); /* rx buffer offset */
nret = gl16(p); /* number of entries returned */
navail = gl16(p); /* number of entries available */
if(err && err != RAP_ERR_MOREINFO){
werrstr("%s", raperrstr(err));
free(p);
return -1;
}
if(nret < 2){ /* paranoia */
free(p);
return ngot;
}
q = si+ngot;
while(nret-- != 0 && ngot < navail){
gmem(p, tmp, 16);
tmp[16] = 0;
q->name = estrdup9p(tmp);
q->major = g8(p);
q->minor = g8(p);
q->type = gl32(p);
gconv(p, conv, tmp, sizeof tmp);
tmp[sizeof tmp - 1] = 0;
q->comment = estrdup9p(tmp);
if(strcmp(first, tmp) == 0){ /* 1st one thru _may_ be a repeat */
free(q->name);
free(q->comment);
continue;
}
ngot++;
q++;
}
free(p);
if(ngot < navail)
goto more;
return ngot;
}
/* Only the Administrator has permission to do this */
int
RAPFileenum2(Session *s, Share *sp, char *user, char *path, Fileinfo **fip)
{
int conv, err, ngot, resume, nret, navail;
char tmp[1024];
Pkt *p;
Fileinfo *q;
ngot = 0;
resume = 0;
more:
p = thdr(s, sp);
pstr(p, "\\PIPE\\LANMAN");
ptparam(p);
pl16(p, API_WFileEnum2);
pascii(p, REMSmb_NetFileEnum2_P); /* request descriptor */
pascii(p, REMSmb_file_info_1); /* reply descriptor */
pascii(p, path);
pascii(p, user);
pl16(p, 1); /* detail level */
pl16(p, s->mtu - 1024); /* receive buffer length */
pl32(p, resume); /* resume key */
/* FIXME: maybe the padding and resume key are the wrong way around? */
pl32(p, 0); /* padding ? */
ptdata(p);
if(trpc(p) == -1){
free(p);
return -1;
}
gtparam(p);
err = gl16(p); /* error code */
conv = gl16(p); /* rx buffer offset */
resume = gl32(p); /* resume key returned */
nret = gl16(p); /* number of entries returned */
navail = gl16(p); /* number of entries available */
if(err && err != RAP_ERR_MOREINFO){
werrstr("%s", raperrstr(err));
free(p);
return -1;
}
if(nret < 2){ /* paranoia */
free(p);
return ngot;
}
if(ngot == 0){
*fip = emalloc9p(sizeof(Fileinfo) * navail);
memset(*fip, 0, sizeof(Fileinfo) * navail);
}
q = *fip + ngot;
for(; nret-- && ngot < navail; ngot++){
q->ident = gl16(p);
q->perms = gl16(p);
q->locks = gl16(p);
gconv(p, conv, tmp, sizeof tmp);
tmp[sizeof tmp - 1] = 0;
q->path = estrdup9p(tmp);
gconv(p, conv, tmp, sizeof tmp);
tmp[sizeof tmp - 1] = 0;
q->user = estrdup9p(tmp);
q++;
}
free(p);
if(ngot < navail)
goto more;
return ngot;
}