s3:libsmb: don't reuse the callers stype variable in cli_NetServerEnum()
[ira/wip.git] / source3 / libsmb / clirap.c
index 410b7cb13832e4908ec217416daf0fd5801bfb63..a9a13f7b96d3a2b0173761766f4dec86331d8c82 100644 (file)
@@ -3,6 +3,7 @@
    client RAP calls
    Copyright (C) Andrew Tridgell         1994-1998
    Copyright (C) Gerald (Jerry) Carter   2004
+   Copyright (C) James Peach            2007
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
 */
 
 #include "includes.h"
-
-/****************************************************************************
- Call a remote api on an arbitrary pipe.  takes param, data and setup buffers.
-****************************************************************************/
-
-bool cli_api_pipe(struct cli_state *cli, const char *pipe_name,
-                  uint16 *setup, uint32 setup_count, uint32 max_setup_count,
-                  char *params, uint32 param_count, uint32 max_param_count,
-                  char *data, uint32 data_count, uint32 max_data_count,
-                  char **rparam, uint32 *rparam_count,
-                  char **rdata, uint32 *rdata_count)
-{
-       cli_send_trans(cli, SMBtrans,
-                 pipe_name,
-                 0,0,                         /* fid, flags */
-                 setup, setup_count, max_setup_count,
-                 params, param_count, max_param_count,
-                 data, data_count, max_data_count);
-
-       return (cli_receive_trans(cli, SMBtrans,
-                            rparam, (unsigned int *)rparam_count,
-                            rdata, (unsigned int *)rdata_count));
-}
+#include "../libcli/auth/libcli_auth.h"
 
 /****************************************************************************
  Call a remote api
@@ -190,12 +169,13 @@ int cli_RNetShareEnum(struct cli_state *cli, void (*fn)(const char *, uint32, co
 
                                        sname = p;
                                        type = SVAL(p,14);
-                                       comment_offset = IVAL(p,16) & 0xFFFF;
-                                       if (comment_offset < 0 || comment_offset > (int)rdrcnt) {
+                                       comment_offset = (IVAL(p,16) & 0xFFFF) - converter;
+                                       if (comment_offset < 0 ||
+                                                       comment_offset > (int)rdrcnt) {
                                                TALLOC_FREE(frame);
                                                break;
                                        }
-                                       cmnt = comment_offset?(rdata+comment_offset-converter):"";
+                                       cmnt = comment_offset?(rdata+comment_offset):"";
 
                                        /* Work out the comment length. */
                                        for (p1 = cmnt, len = 0; *p1 &&
@@ -244,62 +224,125 @@ bool cli_NetServerEnum(struct cli_state *cli, char *workgroup, uint32 stype,
 {
        char *rparam = NULL;
        char *rdata = NULL;
+       char *rdata_end = NULL;
        unsigned int rdrcnt,rprcnt;
        char *p;
        char param[1024];
        int uLevel = 1;
-       int count = -1;
        size_t len;
+       uint32 func = RAP_NetServerEnum2;
+       char *last_entry = NULL;
+       int total_cnt = 0;
+       int return_cnt = 0;
+       int res;
 
        errno = 0; /* reset */
 
-       /* send a SMBtrans command with api NetServerEnum */
-       p = param;
-       SSVAL(p,0,0x68); /* api number */
-       p += 2;
-       strlcpy(p,"WrLehDz", sizeof(param)-PTR_DIFF(p,param));
-       p = skip_string(param,sizeof(param),p);
+       /*
+        * This may take more than one transaction, so we should loop until
+        * we no longer get a more data to process or we have all of the
+        * items.
+        */
+       do {
+               /* send a SMBtrans command with api NetServerEnum */
+               p = param;
+               SIVAL(p,0,func); /* api number */
+               p += 2;
+               /* Next time through we need to use the continue api */
+               func = RAP_NetServerEnum3;
+
+               if (last_entry) {
+                       strlcpy(p,"WrLehDOz", sizeof(param)-PTR_DIFF(p,param));
+               } else {
+                       strlcpy(p,"WrLehDz", sizeof(param)-PTR_DIFF(p,param));
+               }
 
-       strlcpy(p,"B16BBDz", sizeof(param)-PTR_DIFF(p,param));
+               p = skip_string(param, sizeof(param), p);
+               strlcpy(p,"B16BBDz", sizeof(param)-PTR_DIFF(p,param));
+
+               p = skip_string(param, sizeof(param), p);
+               SSVAL(p,0,uLevel);
+               SSVAL(p,2,CLI_BUFFER_SIZE);
+               p += 4;
+               SIVAL(p,0,stype);
+               p += 4;
+
+               /* If we have more data, tell the server where
+                * to continue from.
+                */
+               len = push_ascii(p,
+                               last_entry ? last_entry : workgroup,
+                               sizeof(param) - PTR_DIFF(p,param) - 1,
+                               STR_TERMINATE|STR_UPPER);
+
+               if (len == (size_t)-1) {
+                       SAFE_FREE(last_entry);
+                       return false;
+               }
+               p += len;
 
-       p = skip_string(param,sizeof(param),p);
-       SSVAL(p,0,uLevel);
-       SSVAL(p,2,CLI_BUFFER_SIZE);
-       p += 4;
-       SIVAL(p,0,stype);
-       p += 4;
+               if (!cli_api(cli,
+                       param, PTR_DIFF(p,param), 8, /* params, length, max */
+                       NULL, 0, CLI_BUFFER_SIZE, /* data, length, max */
+                           &rparam, &rprcnt, /* return params, return size */
+                           &rdata, &rdrcnt)) { /* return data, return size */
 
-       len = push_ascii(p, workgroup, sizeof(param)-PTR_DIFF(p,param)-1,
-                       STR_TERMINATE|STR_UPPER);
-       if (len == (size_t)-1) {
-               return false;
-       }
-       p += len;
+                       /* break out of the loop on error */
+                       res = -1;
+                       break;
+               }
 
-       if (cli_api(cli,
-                    param, PTR_DIFF(p,param), 8,        /* params, length, max */
-                    NULL, 0, CLI_BUFFER_SIZE,               /* data, length, max */
-                    &rparam, &rprcnt,                   /* return params, return size */
-                    &rdata, &rdrcnt                     /* return data, return size */
-                   )) {
-               int res = rparam? SVAL(rparam,0) : -1;
-               char *rdata_end = rdata + rdrcnt;
+               rdata_end = rdata + rdrcnt;
+               res = rparam ? SVAL(rparam,0) : -1;
 
                if (res == 0 || res == ERRmoredata ||
                     (res != -1 && cli_errno(cli) == 0)) {
-                       int i;
+                       char *sname = NULL;
+                       int i, count;
                        int converter=SVAL(rparam,2);
 
-                       count=SVAL(rparam,4);
+                       /* Get the number of items returned in this buffer */
+                       count = SVAL(rparam, 4);
+
+                       /* The next field contains the number of items left,
+                        * including those returned in this buffer. So the
+                        * first time through this should contain all of the
+                        * entries.
+                        */
+                       if (total_cnt == 0) {
+                               total_cnt = SVAL(rparam, 6);
+                       }
+
+                       /* Keep track of how many we have read */
+                       return_cnt += count;
                        p = rdata;
 
-                       for (i = 0;i < count;i++, p += 26) {
-                               char *sname;
+                       /* The last name in the previous NetServerEnum reply is
+                        * sent back to server in the NetServerEnum3 request
+                        * (last_entry). The next reply should repeat this entry
+                        * as the first element. We have no proof that this is
+                        * always true, but from traces that seems to be the
+                        * behavior from Window Servers. So first lets do a lot
+                        * of checking, just being paranoid. If the string
+                        * matches then we already saw this entry so skip it.
+                        *
+                        * NOTE: sv1_name field must be null terminated and has
+                        * a max size of 16 (NetBIOS Name).
+                        */
+                       if (last_entry && count && p &&
+                               (strncmp(last_entry, p, 16) == 0)) {
+                           count -= 1; /* Skip this entry */
+                           return_cnt = -1; /* Not part of total, so don't count. */
+                           p = rdata + 26; /* Skip the whole record */
+                       }
+
+                       for (i = 0; i < count; i++, p += 26) {
                                int comment_offset;
                                const char *cmnt;
                                const char *p1;
                                char *s1, *s2;
                                TALLOC_CTX *frame = talloc_stackframe();
+                               uint32_t entry_stype;
 
                                if (p + 26 > rdata_end) {
                                        TALLOC_FREE(frame);
@@ -323,7 +366,7 @@ bool cli_NetServerEnum(struct cli_state *cli, char *workgroup, uint32 stype,
                                        len++;
                                }
 
-                               stype = IVAL(p,18) & ~SV_TYPE_LOCAL_LIST_ONLY;
+                               entry_stype = IVAL(p,18) & ~SV_TYPE_LOCAL_LIST_ONLY;
 
                                pull_string_talloc(frame,rdata,0,
                                        &s1,sname,16,STR_ASCII);
@@ -335,27 +378,48 @@ bool cli_NetServerEnum(struct cli_state *cli, char *workgroup, uint32 stype,
                                        continue;
                                }
 
-                               fn(s1, stype, s2, state);
+                               fn(s1, entry_stype, s2, state);
                                TALLOC_FREE(frame);
                        }
+
+                       /* We are done with the old last entry, so now we can free it */
+                       if (last_entry) {
+                               SAFE_FREE(last_entry); /* This will set it to null */
+                       }
+
+                       /* We always make a copy of  the last entry if we have one */
+                       if (sname) {
+                               last_entry = smb_xstrdup(sname);
+                       }
+
+                       /* If we have more data, but no last entry then error out */
+                       if (!last_entry && (res == ERRmoredata)) {
+                               errno = EINVAL;
+                               res = 0;
+                       }
+
                }
-       }
+
+               SAFE_FREE(rparam);
+               SAFE_FREE(rdata);
+       } while ((res == ERRmoredata) && (total_cnt > return_cnt));
 
        SAFE_FREE(rparam);
        SAFE_FREE(rdata);
+       SAFE_FREE(last_entry);
 
-       if (count < 0) {
-           errno = cli_errno(cli);
+       if (res == -1) {
+               errno = cli_errno(cli);
        } else {
-           if (!count) {
-               /* this is a very special case, when the domain master for the
-                  work group isn't part of the work group itself, there is something
-                  wild going on */
-               errno = ENOENT;
+               if (!return_cnt) {
+                       /* this is a very special case, when the domain master for the
+                          work group isn't part of the work group itself, there is something
+                          wild going on */
+                       errno = ENOENT;
+               }
            }
-       }
 
-       return(count > 0);
+       return(return_cnt > 0);
 }
 
 /****************************************************************************
@@ -406,7 +470,7 @@ bool cli_oem_change_password(struct cli_state *cli, const char *user, const char
        DEBUG(100,("make_oem_passwd_hash\n"));
        dump_data(100, data, 516);
 #endif
-       SamOEMhash( (unsigned char *)data, (unsigned char *)old_pw_hash, 516);
+       arcfour_crypt( (unsigned char *)data, (unsigned char *)old_pw_hash, 516);
 
        /*
         * Now place the old password hash in the data.
@@ -721,11 +785,142 @@ bool cli_qpathinfo2(struct cli_state *cli, const char *fname,
        return True;
 }
 
+/****************************************************************************
+ Get the stream info
+****************************************************************************/
+
+bool cli_qpathinfo_streams(struct cli_state *cli, const char *fname,
+                          TALLOC_CTX *mem_ctx,
+                          unsigned int *pnum_streams,
+                          struct stream_struct **pstreams)
+{
+       unsigned int data_len = 0;
+       unsigned int param_len = 0;
+       uint16 setup = TRANSACT2_QPATHINFO;
+       char *param;
+       char *rparam=NULL, *rdata=NULL;
+       char *p;
+       unsigned int num_streams;
+       struct stream_struct *streams;
+       unsigned int ofs;
+       size_t namelen = 2*(strlen(fname)+1);
+
+       param = SMB_MALLOC_ARRAY(char, 6+namelen+2);
+       if (param == NULL) {
+               return false;
+       }
+       p = param;
+       memset(p, 0, 6);
+       SSVAL(p, 0, SMB_FILE_STREAM_INFORMATION);
+       p += 6;
+       p += clistr_push(cli, p, fname, namelen, STR_TERMINATE);
+
+       param_len = PTR_DIFF(p, param);
+
+       if (!cli_send_trans(cli, SMBtrans2,
+                            NULL,                     /* name */
+                            -1, 0,                    /* fid, flags */
+                            &setup, 1, 0,             /* setup, len, max */
+                            param, param_len, 10,     /* param, len, max */
+                            NULL, data_len, cli->max_xmit /* data, len, max */
+                           )) {
+               return false;
+       }
+
+       if (!cli_receive_trans(cli, SMBtrans2,
+                               &rparam, &param_len,
+                               &rdata, &data_len)) {
+               return false;
+       }
+
+       if (!rdata) {
+               SAFE_FREE(rparam);
+               return false;
+       }
+
+       num_streams = 0;
+       streams = NULL;
+       ofs = 0;
+
+       while ((data_len > ofs) && (data_len - ofs >= 24)) {
+               uint32_t nlen, len;
+               size_t size;
+               void *vstr;
+               struct stream_struct *tmp;
+               uint8_t *tmp_buf;
+
+               tmp = TALLOC_REALLOC_ARRAY(mem_ctx, streams,
+                                          struct stream_struct,
+                                          num_streams+1);
+
+               if (tmp == NULL) {
+                       goto fail;
+               }
+               streams = tmp;
+
+               nlen                      = IVAL(rdata, ofs + 0x04);
+
+               streams[num_streams].size = IVAL_TO_SMB_OFF_T(
+                       rdata, ofs + 0x08);
+               streams[num_streams].alloc_size = IVAL_TO_SMB_OFF_T(
+                       rdata, ofs + 0x10);
+
+               if (nlen > data_len - (ofs + 24)) {
+                       goto fail;
+               }
+
+               /*
+                * We need to null-terminate src, how do I do this with
+                * convert_string_talloc??
+                */
+
+               tmp_buf = TALLOC_ARRAY(streams, uint8_t, nlen+2);
+               if (tmp_buf == NULL) {
+                       goto fail;
+               }
+
+               memcpy(tmp_buf, rdata+ofs+24, nlen);
+               tmp_buf[nlen] = 0;
+               tmp_buf[nlen+1] = 0;
+
+               if (!convert_string_talloc(streams, CH_UTF16, CH_UNIX, tmp_buf,
+                                          nlen+2, &vstr, &size, false))
+               {
+                       TALLOC_FREE(tmp_buf);
+                       goto fail;
+               }
+
+               TALLOC_FREE(tmp_buf);
+               streams[num_streams].name = (char *)vstr;
+               num_streams++;
+
+               len = IVAL(rdata, ofs);
+               if (len > data_len - ofs) {
+                       goto fail;
+               }
+               if (len == 0) break;
+               ofs += len;
+       }
+
+       SAFE_FREE(rdata);
+       SAFE_FREE(rparam);
+
+       *pnum_streams = num_streams;
+       *pstreams = streams;
+       return true;
+
+ fail:
+       TALLOC_FREE(streams);
+       SAFE_FREE(rdata);
+       SAFE_FREE(rparam);
+       return false;
+}
+
 /****************************************************************************
  Send a qfileinfo QUERY_FILE_NAME_INFO call.
 ****************************************************************************/
 
-bool cli_qfilename(struct cli_state *cli, int fnum, char *name, size_t namelen)
+bool cli_qfilename(struct cli_state *cli, uint16_t fnum, char *name, size_t namelen)
 {
        unsigned int data_len = 0;
        unsigned int param_len = 0;
@@ -754,10 +949,16 @@ bool cli_qfilename(struct cli_state *cli, int fnum, char *name, size_t namelen)
        }
 
        if (!rdata || data_len < 4) {
+               SAFE_FREE(rparam);
+               SAFE_FREE(rdata);
                return False;
        }
 
-       clistr_pull(cli, name, rdata+4, namelen, IVAL(rdata, 0), STR_UNICODE);
+       clistr_pull(cli->inbuf, name, rdata+4, namelen, IVAL(rdata, 0),
+                   STR_UNICODE);
+
+       SAFE_FREE(rparam);
+       SAFE_FREE(rdata);
 
        return True;
 }
@@ -766,7 +967,7 @@ bool cli_qfilename(struct cli_state *cli, int fnum, char *name, size_t namelen)
  Send a qfileinfo call.
 ****************************************************************************/
 
-bool cli_qfileinfo(struct cli_state *cli, int fnum,
+bool cli_qfileinfo(struct cli_state *cli, uint16_t fnum,
                   uint16 *mode, SMB_OFF_T *size,
                   struct timespec *create_time,
                    struct timespec *access_time,
@@ -774,52 +975,45 @@ bool cli_qfileinfo(struct cli_state *cli, int fnum,
                   struct timespec *change_time,
                    SMB_INO_T *ino)
 {
-       unsigned int data_len = 0;
-       unsigned int param_len = 0;
-       uint16 setup = TRANSACT2_QFILEINFO;
-       char param[4];
-       char *rparam=NULL, *rdata=NULL;
+       uint32_t data_len = 0;
+       uint16 setup;
+       uint8_t param[4];
+       uint8_t *rdata=NULL;
+       NTSTATUS status;
 
        /* if its a win95 server then fail this - win95 totally screws it
           up */
        if (cli->win95) return False;
 
-       param_len = 4;
-
        SSVAL(param, 0, fnum);
        SSVAL(param, 2, SMB_QUERY_FILE_ALL_INFO);
 
-       if (!cli_send_trans(cli, SMBtrans2,
-                            NULL,                         /* name */
-                            -1, 0,                        /* fid, flags */
-                            &setup, 1, 0,                 /* setup, length, max */
-                            param, param_len, 2,          /* param, length, max */
-                            NULL, data_len, cli->max_xmit /* data, length, max */
-                           )) {
-               return False;
-       }
+       SSVAL(&setup, 0, TRANSACT2_QFILEINFO);
 
-       if (!cli_receive_trans(cli, SMBtrans2,
-                               &rparam, &param_len,
-                               &rdata, &data_len)) {
-               return False;
-       }
+       status = cli_trans(talloc_tos(), cli, SMBtrans2,
+                          NULL, -1, 0, 0, /* name, fid, function, flags */
+                          &setup, 1, 0,          /* setup, length, max */
+                          param, 4, 2,   /* param, length, max */
+                          NULL, 0, MIN(cli->max_xmit, 0xffff), /* data, length, max */
+                          NULL, 0, NULL, /* rsetup, length */
+                          NULL, 0, NULL,       /* rparam, length */
+                          &rdata, 68, &data_len);
 
-       if (!rdata || data_len < 68) {
-               return False;
+       if (!NT_STATUS_IS_OK(status)) {
+               return false;
        }
 
        if (create_time) {
-               *create_time = interpret_long_date(rdata+0);
+               *create_time = interpret_long_date((char *)rdata+0);
        }
        if (access_time) {
-               *access_time = interpret_long_date(rdata+8);
+               *access_time = interpret_long_date((char *)rdata+8);
        }
        if (write_time) {
-               *write_time = interpret_long_date(rdata+16);
+               *write_time = interpret_long_date((char *)rdata+16);
        }
        if (change_time) {
-               *change_time = interpret_long_date(rdata+24);
+               *change_time = interpret_long_date((char *)rdata+24);
        }
        if (mode) {
                *mode = SVAL(rdata, 32);
@@ -831,8 +1025,7 @@ bool cli_qfileinfo(struct cli_state *cli, int fnum,
                *ino = IVAL(rdata, 64);
        }
 
-       SAFE_FREE(rdata);
-       SAFE_FREE(rparam);
+       TALLOC_FREE(rdata);
        return True;
 }
 
@@ -905,9 +1098,9 @@ bool cli_qpathinfo_basic( struct cli_state *cli, const char *name,
                return False;
        }
 
-       set_atimespec(sbuf, interpret_long_date( rdata+8 )); /* Access time. */
-       set_mtimespec(sbuf, interpret_long_date( rdata+16 )); /* Write time. */
-       set_ctimespec(sbuf, interpret_long_date( rdata+24 )); /* Change time. */
+       sbuf->st_ex_atime = interpret_long_date( rdata+8 ); /* Access time. */
+       sbuf->st_ex_mtime = interpret_long_date( rdata+16 ); /* Write time. */
+       sbuf->st_ex_ctime = interpret_long_date( rdata+24 ); /* Change time. */
 
        *attributes = IVAL( rdata, 32 );
 
@@ -921,7 +1114,7 @@ bool cli_qpathinfo_basic( struct cli_state *cli, const char *name,
  Send a qfileinfo call.
 ****************************************************************************/
 
-bool cli_qfileinfo_test(struct cli_state *cli, int fnum, int level, char **poutdata, uint32 *poutlen)
+bool cli_qfileinfo_test(struct cli_state *cli, uint16_t fnum, int level, char **poutdata, uint32 *poutlen)
 {
        unsigned int data_len = 0;
        unsigned int param_len = 0;
@@ -1034,7 +1227,8 @@ NTSTATUS cli_qpathinfo_alt_name(struct cli_state *cli, const char *fname, fstrin
                return NT_STATUS_INVALID_NETWORK_RESPONSE;
        }
 
-       clistr_pull(cli, alt_name, rdata+4, sizeof(fstring), len, STR_UNICODE);
+       clistr_pull(cli->inbuf, alt_name, rdata+4, sizeof(fstring), len,
+                   STR_UNICODE);
 
        SAFE_FREE(rdata);
        SAFE_FREE(rparam);