r1032: Fix RPC backend segfault
[kai/samba.git] / source4 / lib / registry / reg_backend_rpc / reg_backend_rpc.c
index ad4d537f9b1456d437ac21a1be53b8951fb55aa9..4e6944a1e3617213e2c0cadd452a204824d818ca 100644 (file)
 #include "includes.h"
 #include "lib/registry/common/registry.h"
 
+/**
+ * This is the RPC backend for the registry library.
+ */
+
 static void init_winreg_String(struct winreg_String *name, const char *s)
 {
     name->name = s;
@@ -33,12 +37,11 @@ static void init_winreg_String(struct winreg_String *name, const char *s)
 }
 
 
-#define openhive(u) static struct policy_handle *open_ ## u(struct dcerpc_pipe *p, REG_HANDLE *h) \
+#define openhive(u) static WERROR open_ ## u(struct dcerpc_pipe *p, REG_KEY *h, struct policy_handle *hnd) \
 { \
-       NTSTATUS status; \
        struct winreg_Open ## u r; \
        struct winreg_OpenUnknown unknown; \
-       struct policy_handle *hnd = malloc(sizeof(struct policy_handle)); \
+       NTSTATUS status; \
        \
        unknown.unknown0 = 0x84e0; \
        unknown.unknown1 = 0x0000; \
@@ -46,12 +49,13 @@ static void init_winreg_String(struct winreg_String *name, const char *s)
        r.in.access_required = SEC_RIGHTS_MAXIMUM_ALLOWED; \
        r.out.handle = hnd;\
        \
-       if (!NT_STATUS_IS_OK(dcerpc_winreg_Open ## u(p, h->mem_ctx, &r))) {\
-               printf("Error executing open\n");\
-               return NULL;\
+       status = dcerpc_winreg_Open ## u(p, h->mem_ctx, &r); \
+       if (NT_STATUS_IS_ERR(status)) {\
+               DEBUG(0,("Error executing open\n"));\
+               return ntstatus_to_werror(status);\
        }\
 \
-       return hnd;\
+       return r.out.result;\
 }
 
 openhive(HKLM)
@@ -60,14 +64,17 @@ openhive(HKPD)
 openhive(HKU)
 openhive(HKCR)
 
-struct rpc_data {
-       struct dcerpc_pipe *pipe;
-       struct policy_handle *hives[10];
+struct rpc_key_data {
+       struct policy_handle pol;
+       int num_subkeys;
+       int num_values;
+       int max_valnamelen;
+       int max_valdatalen;
 };
 
 struct {
-       char *name;
-       struct policy_handle *(*open) (struct dcerpc_pipe *p, REG_HANDLE *h);
+       const char *name;
+       WERROR (*open) (struct dcerpc_pipe *p, REG_KEY *k, struct policy_handle *h);
 } known_hives[] = {
 { "HKEY_LOCAL_MACHINE", open_HKLM },
 { "HKEY_CURRENT_USER", open_HKCU },
@@ -77,126 +84,185 @@ struct {
 { NULL, NULL }
 };
 
-static BOOL rpc_open_registry(REG_HANDLE *h, const char *location, BOOL try_full)
+static WERROR rpc_query_key(REG_KEY *k);
+
+static WERROR rpc_open_registry(REG_HANDLE *h, const char *location, const char *credentials)
 {
-       BOOL res = True;
-       struct rpc_data *mydata = talloc(h->mem_ctx, sizeof(struct rpc_data));
-       char *binding = strdup(location);
        NTSTATUS status;
-       
-       ZERO_STRUCTP(mydata);
-       
-       status = dcerpc_pipe_connect(&mydata->pipe, binding, 
+       char *user, *pass;
+
+       if(!credentials || !location) return WERR_INVALID_PARAM;
+
+       user = talloc_strdup(h->mem_ctx, credentials);
+       pass = strchr(user, '%');
+       *pass = '\0'; pass++;
+
+       status = dcerpc_pipe_connect((struct dcerpc_pipe **)&h->backend_data, h->location, 
                     DCERPC_WINREG_UUID,
                     DCERPC_WINREG_VERSION,
                      lp_workgroup(),
-                     "tridge", "samba");
-
-       if(!NT_STATUS_IS_OK(status)) return False;
-
-       h->backend_data = mydata;
+                     user, pass);
        
-       return True;
+       return ntstatus_to_werror(status);
 }
 
-static REG_KEY *rpc_open_root(REG_HANDLE *h)
+static WERROR rpc_get_hive(REG_HANDLE *h, int n, REG_KEY **k)
 {
-       /* There's not really a 'root' key here */
-       return reg_key_new_abs("\\", h, h->backend_data);
+       struct rpc_key_data *mykeydata;
+       WERROR error;
+       if(!known_hives[n].name) return WERR_NO_MORE_ITEMS;
+       *k = reg_key_new_abs(known_hives[n].name, h, NULL);
+       (*k)->backend_data = mykeydata = talloc_p((*k)->mem_ctx, struct rpc_key_data);
+       mykeydata->num_values = -1;
+       mykeydata->num_subkeys = -1;
+       error = known_hives[n].open((struct dcerpc_pipe *)h->backend_data, *k, &mykeydata->pol);
+       return error;
 }
 
-static BOOL rpc_close_registry(REG_HANDLE *h)
+static WERROR rpc_close_registry(REG_HANDLE *h)
 {
-       dcerpc_pipe_close(((struct rpc_data *)h->backend_data)->pipe);
-       free(h->backend_data);
-       return True;
+       dcerpc_pipe_close((struct dcerpc_pipe *)h->backend_data);
+       return WERR_OK;
 }
 
-static struct policy_handle *rpc_get_key_handle(REG_HANDLE *h, const char *path)
+static WERROR rpc_key_put_rpc_data(REG_KEY *k, struct rpc_key_data **data)
 {
-       char *hivename;
-       int i = 0;
-       struct rpc_data *mydata = h->backend_data;
-       struct policy_handle *hive = NULL;
-       char *end = strchr(path+1, '\\');
-    NTSTATUS status;
     struct winreg_OpenKey r;
-       struct policy_handle *key_handle = talloc(h->mem_ctx, sizeof(struct policy_handle));
-       TALLOC_CTX *mem_ctx;
-       if(end) hivename = strndup(path+1, end-path-1);
-       else hivename = strdup(path+1);
-
-       for(i = 0; known_hives[i].name; i++) {
-               if(!strcmp(hivename, known_hives[i].name)) {
-               if(!mydata->hives[i]) mydata->hives[i] = known_hives[i].open(mydata->pipe, h);
-                       hive = mydata->hives[i];
-               }
+       int i;
+       struct rpc_data *mydata = k->handle->backend_data;
+       WERROR error;
+       REG_KEY *hivekey;
+       struct rpc_key_data *mykeydata;
+
+       if(k->backend_data) { 
+               *data = k->backend_data; 
+               return WERR_OK;
        }
+
+       k->backend_data = mykeydata = talloc_p(k->mem_ctx, struct rpc_key_data);
+       *data = mykeydata;
+       mykeydata->num_values = -1;
+       mykeydata->num_subkeys = -1;
+
+       /* Then, open the handle using the hive */
+
+       memset(&r, 0, sizeof(struct winreg_OpenKey));
+       error = rpc_get_hive(k->handle, k->hive, &hivekey);
+       if(!W_ERROR_IS_OK(error))return error;
+    r.in.handle = &(((struct rpc_key_data *)hivekey->backend_data)->pol);
+    init_winreg_String(&r.in.keyname, reg_key_get_path(k));
+    r.in.unknown = 0x00000000;
+    r.in.access_mask = 0x02000000;
+    r.out.handle = &mykeydata->pol;
+
+    dcerpc_winreg_OpenKey((struct dcerpc_pipe *)k->handle->backend_data, k->mem_ctx, &r);
+
+       return r.out.result;
+}
+
+static WERROR rpc_open_key(REG_HANDLE *h, int hive, const char *name, REG_KEY **key)
+{
+       struct rpc_key_data *mykeydata;
+    struct winreg_OpenKey r;
+       REG_KEY *hivekey;
+       WERROR error;
        
-       if(!hive) {
-               DEBUG(0, ("No such hive: %s\n", hivename));
-               return NULL;
-       }
+       *key = reg_key_new_abs(name, h, NULL);
 
-       DEBUG(2, ("Opening %s, hive: %s\n", path, hivename));
+       (*key)->backend_data = mykeydata = talloc_p((*key)->mem_ctx, struct rpc_key_data);
+       mykeydata->num_values = -1;
+       mykeydata->num_subkeys = -1;
 
-       if(!end || !(*end) || !(*(end+1))) return hive;
+       /* Then, open the handle using the hive */
 
        memset(&r, 0, sizeof(struct winreg_OpenKey));
-    r.in.handle = hive;
-    init_winreg_String(&r.in.keyname, end+1);
+       error = rpc_get_hive(h, hive, &hivekey);
+       if(!W_ERROR_IS_OK(error))return error;
+    r.in.handle = &(((struct rpc_key_data *)hivekey->backend_data)->pol);
+    init_winreg_String(&r.in.keyname, name);
     r.in.unknown = 0x00000000;
     r.in.access_mask = 0x02000000;
-    r.out.handle = key_handle;
-                        
-       mem_ctx = talloc_init("openkey");
-    status = dcerpc_winreg_OpenKey(mydata->pipe, mem_ctx, &r);
-       talloc_destroy(mem_ctx);
-                                                                                                                               
-    if (!NT_STATUS_IS_OK(status) || !W_ERROR_IS_OK(r.out.result)) {
-        return NULL;
-    }
+    r.out.handle = &mykeydata->pol;
 
-       return key_handle;
+    dcerpc_winreg_OpenKey((struct dcerpc_pipe *)(*key)->handle->backend_data, (*key)->mem_ctx, &r);
+
+       return r.out.result;
 }
 
-static REG_KEY *rpc_open_key(REG_HANDLE *h, const char *name)
+static WERROR rpc_get_value_by_index(REG_KEY *parent, int n, REG_VAL **value)  
 {
-       return reg_key_new_abs(name, h, rpc_get_key_handle(h, name));
+       struct winreg_EnumValue r;
+       struct winreg_Uint8buf vb;
+       struct winreg_EnumValueName vn;
+       NTSTATUS status;
+       struct rpc_key_data *mykeydata;
+       uint32_t type = 0x0, requested_len = 0, returned_len = 0;
+       WERROR error;
+
+       error = rpc_key_put_rpc_data(parent, &mykeydata);
+       if(!W_ERROR_IS_OK(error)) return error;
+
+       if(mykeydata->num_values == -1) {
+               error = rpc_query_key(parent);
+               if(!W_ERROR_IS_OK(error)) return error;
+       }
+
+       requested_len = mykeydata->max_valdatalen;
+
+       r.in.handle = &mykeydata->pol;
+       r.in.enum_index = n;
+       r.in.type = r.out.type = &type;
+       r.in.requested_len = r.out.requested_len = &requested_len;
+       r.in.returned_len = r.out.returned_len = &returned_len;
+       vn.max_len = mykeydata->max_valnamelen * 2;
+       vn.len = 0;
+       vn.buf = NULL;
+       if(vn.max_len > 0) {
+               vn.len = 0;
+               vn.max_len = mykeydata->max_valnamelen*2;
+               /* FIXME: we should not point a 'char *' to a const buffer!!! --metze*/
+               vn.buf = "";
+       }
+       r.in.name = r.out.name = &vn;
+       vb.max_len = mykeydata->max_valdatalen;
+       vb.offset = 0x0;
+       vb.len = 0x0;
+       vb.buffer = talloc_array_p(parent->mem_ctx, uint8, mykeydata->max_valdatalen);
+       r.in.value = r.out.value = &vb;
+
+       status = dcerpc_winreg_EnumValue((struct dcerpc_pipe *)parent->handle->backend_data, parent->mem_ctx, &r);
+       if(NT_STATUS_IS_ERR(status)) {
+               DEBUG(0, ("Error in EnumValue: %s\n", nt_errstr(status)));
+       }
+       
+       if(NT_STATUS_IS_OK(status) && W_ERROR_IS_OK(r.out.result)) {
+               *value = reg_val_new(parent, NULL);
+               (*value)->name = r.out.name->buf;
+               (*value)->data_type = type;
+               (*value)->data_len = r.out.value->len;
+               (*value)->data_blk = r.out.value->buffer;
+               exit(1);
+               return WERR_OK;
+       }
+       
+       return r.out.result;
 }
 
-static BOOL rpc_fetch_subkeys(REG_KEY *parent, int *count, REG_KEY ***subkeys
+static WERROR rpc_get_subkey_by_index(REG_KEY *parent, int n, REG_KEY **subkey
 {
        struct winreg_EnumKey r;
        struct winreg_EnumKeyNameRequest keyname;
        struct winreg_String classname;
        struct winreg_Time tm;
        struct rpc_data *mydata = parent->handle->backend_data;
-       int i;
-       REG_KEY **ar = talloc(parent->mem_ctx, sizeof(REG_KEY *));
-       NTSTATUS status = NT_STATUS_OK;
-       TALLOC_CTX *mem_ctx;
-
-       /* List the hives */
-       if(parent->backend_data == parent->handle->backend_data) { 
-               for(i = 0; known_hives[i].name; i++) {
-                       ar[i] = reg_key_new_rel(known_hives[i].name, parent, NULL);
-                       (*count)++;
-                       ar = talloc_realloc(parent->mem_ctx, ar, sizeof(REG_KEY *) * ((*count)+1));
-               }
-
-               *subkeys = ar;
-
-               return True;
-       }
-
-       if(!parent->backend_data) parent->backend_data = rpc_get_key_handle(parent->handle, reg_key_get_path(parent));
+       struct rpc_key_data *mykeydata = parent->backend_data;
+       WERROR error;
+       NTSTATUS status;
 
-       if(!parent->backend_data) return False;
+       error = rpc_key_put_rpc_data(parent, &mykeydata);
+       if(!W_ERROR_IS_OK(error)) return error;
 
-       (*count) = 0;
-       r.in.handle = parent->backend_data;
+       r.in.handle = &mykeydata->pol;
        keyname.unknown = 0x0000020a;
        init_winreg_String(&keyname.key_name, NULL);
        init_winreg_String(&classname, NULL);
@@ -204,113 +270,135 @@ static BOOL rpc_fetch_subkeys(REG_KEY *parent, int *count, REG_KEY ***subkeys)
        r.in.class = &classname;
        tm.low = tm.high = 0x7fffffff;
        r.in.last_changed_time = &tm;
-       r.out.result.v = 0;
-
-       for(i = 0; NT_STATUS_IS_OK(status) && W_ERROR_IS_OK(r.out.result); i++) {
-               r.in.enum_index = i;
-               r.in.unknown = r.out.unknown = 0x0414;
-               r.in.key_name_len = r.out.key_name_len = 0;
-               status = dcerpc_winreg_EnumKey(mydata->pipe, parent->mem_ctx, &r);
-               if(NT_STATUS_IS_OK(status) && W_ERROR_IS_OK(r.out.result)) {
-                       ar[(*count)] = reg_key_new_rel(r.out.out_name->name, parent, NULL);
-                       (*count)++;
-                       ar = talloc_realloc(parent->mem_ctx, ar, ((*count)+1) * sizeof(REG_KEY *));
-               }
+
+       r.in.enum_index = n;
+       r.in.unknown = r.out.unknown = 0x0414;
+       r.in.key_name_len = r.out.key_name_len = 0;
+       status = dcerpc_winreg_EnumKey((struct dcerpc_pipe *)parent->handle->backend_data, parent->mem_ctx, &r);
+       if(NT_STATUS_IS_OK(status) && W_ERROR_IS_OK(r.out.result)) {
+               *subkey = reg_key_new_rel(r.out.out_name->name, parent, NULL);
        }
 
-       *subkeys = ar;
-       return True;
+       return r.out.result;
 }
 
-static BOOL rpc_fetch_values(REG_KEY *parent, int *count, REG_VAL ***values) 
+static WERROR rpc_add_key(REG_KEY *parent, const char *name, uint32_t access_mask, SEC_DESC *sec, REG_KEY **key)
 {
-       struct winreg_EnumValue r;
-       struct winreg_Uint8buf value;
-       struct winreg_String valuename;
-       struct rpc_data *mydata = parent->handle->backend_data;
-       TALLOC_CTX *mem_ctx;
-       uint32 type, requested_len, returned_len;
-       NTSTATUS status = NT_STATUS_OK;
-       REG_VAL **ar = malloc(sizeof(REG_VAL *));
+       struct rpc_key_data *mykeydata;
+       WERROR error;
 
-       (*count) = 0;
+       error = rpc_key_put_rpc_data(parent, &mykeydata);
+       if(!W_ERROR_IS_OK(error)) return error;
 
-       /* Root */
-       if(parent->backend_data == parent->handle->backend_data) {
-               *values = ar;
-               return True;
-       }
-       
-       if(!parent->backend_data) parent->backend_data = rpc_get_key_handle(parent->handle, reg_key_get_path(parent));
+       /* FIXME */
+       return WERR_NOT_SUPPORTED;
+}
 
-       if(!parent->backend_data) return False;
+static WERROR rpc_query_key(REG_KEY *k)
+{
+    NTSTATUS status;
+       WERROR error;
+    struct winreg_QueryInfoKey r;
+    struct rpc_key_data *mykeydata;
 
-       r.in.handle = parent->backend_data;
-       r.in.enum_index = 0;
+       error = rpc_key_put_rpc_data(k, &mykeydata);
+       if(!W_ERROR_IS_OK(error)) return error;
 
-       init_winreg_String(&valuename, NULL);
-       r.in.name = r.out.name = &valuename;
+    init_winreg_String(&r.in.class, NULL);
+    r.in.handle = &mykeydata->pol;
+       
+    status = dcerpc_winreg_QueryInfoKey((struct dcerpc_pipe *)k->handle->backend_data, k->mem_ctx, &r);
 
-       type = 0;
-       r.in.type = r.out.type = &type;
-       value.max_len = 0x7fff;
-       value.offset = 0;
-       value.len = 0;
-       value.buffer = NULL;
-
-       r.in.value = r.out.value = &value;
-
-       requested_len = value.max_len;
-       r.in.requested_len = &requested_len;
-       returned_len = 0;
-       r.in.returned_len = &returned_len;
-       r.out.result.v = 0;
-
-       while(1) {
-               status = dcerpc_winreg_EnumValue(mydata->pipe, parent->mem_ctx, &r);
-               if(NT_STATUS_IS_OK(status) && W_ERROR_IS_OK(r.out.result)) {
-                       r.in.enum_index++;
-                       ar[(*count)] = reg_val_new(parent, NULL);
-                       ar[(*count)]->name = talloc_strdup(ar[*count]->mem_ctx, r.out.name->name);
-                       ar[(*count)]->data_type = *r.out.type;
-                       ar[(*count)]->data_len = value.len;
-                       ar[(*count)]->data_blk = talloc(ar[*count]->mem_ctx, value.len);
-                       memcpy(ar[(*count)]->data_blk, value.buffer, value.len);
-                       (*count)++;
-                       ar = talloc_realloc(parent->mem_ctx, ar, ((*count)+1) * sizeof(REG_VAL *));
-               } else break;
+    if (!NT_STATUS_IS_OK(status)) {
+        printf("QueryInfoKey failed - %s\n", nt_errstr(status));
+        return ntstatus_to_werror(status);
+    }
+                                                                                                       
+    if (W_ERROR_IS_OK(r.out.result)) {
+               mykeydata->num_subkeys = r.out.num_subkeys;
+               mykeydata->num_values = r.out.num_values;
+               mykeydata->max_valnamelen = r.out.max_valnamelen;
+               mykeydata->max_valdatalen = r.out.max_valbufsize;
        } 
-       
-       *values = ar;
 
-       return True;
+       return r.out.result;
 }
 
-static BOOL rpc_add_key(REG_KEY *parent, const char *name)
+static WERROR rpc_del_key(REG_KEY *k)
 {
-       /* FIXME */
-       return False;   
+       NTSTATUS status;
+       struct rpc_key_data *mykeydata = k->backend_data;
+       struct winreg_DeleteKey r;
+       REG_KEY *parent;
+       WERROR error;
+       
+       error = reg_key_get_parent(k, &parent);
+       if(!W_ERROR_IS_OK(error)) return error;
+
+       error = rpc_key_put_rpc_data(parent, &mykeydata);
+       if(!W_ERROR_IS_OK(error)) return error;
+       
+    r.in.handle = &mykeydata->pol;
+    init_winreg_String(&r.in.key, k->name);
+    status = dcerpc_winreg_DeleteKey((struct dcerpc_pipe *)k->handle->backend_data, k->mem_ctx, &r);
+
+       return r.out.result;
 }
 
-static BOOL rpc_del_key(REG_KEY *k)
+static void rpc_close_key(REG_KEY *k)
 {
-       /* FIXME */
-       return False;
+       reg_key_free(k);
+}
+
+static WERROR rpc_num_values(REG_KEY *key, int *count) {
+       struct rpc_key_data *mykeydata = key->backend_data;
+       WERROR error;
+               
+       error = rpc_key_put_rpc_data(key, &mykeydata);
+       if(!W_ERROR_IS_OK(error)) return error;
+
+       if(mykeydata->num_values == -1) {
+               error = rpc_query_key(key);
+               if(!W_ERROR_IS_OK(error)) return error;
+       }
+                       
+       *count = mykeydata->num_values;
+       return WERR_OK;
+}
+
+static WERROR rpc_num_subkeys(REG_KEY *key, int *count) {
+       struct rpc_key_data *mykeydata = key->backend_data;
+       WERROR error;
+
+       error = rpc_key_put_rpc_data(key, &mykeydata);
+       if(!W_ERROR_IS_OK(error)) return error;
+       
+       if(mykeydata->num_subkeys == -1) {
+               error = rpc_query_key(key);
+               if(!W_ERROR_IS_OK(error)) return error;
+       }
+                       
+       *count = mykeydata->num_subkeys;
+       return WERR_OK;
 }
 
-static REG_OPS reg_backend_rpc = {
+static struct registry_ops reg_backend_rpc = {
        .name = "rpc",
        .open_registry = rpc_open_registry,
        .close_registry = rpc_close_registry,
-       .open_root_key = rpc_open_root,
+       .get_hive = rpc_get_hive,
        .open_key = rpc_open_key,
-       .fetch_subkeys = rpc_fetch_subkeys,
-       .fetch_values = rpc_fetch_values,
+       .get_subkey_by_index = rpc_get_subkey_by_index,
+       .get_value_by_index = rpc_get_value_by_index,
        .add_key = rpc_add_key,
        .del_key = rpc_del_key,
+       .free_key_backend_data = rpc_close_key,
+       .num_subkeys = rpc_num_subkeys,
+       .num_values = rpc_num_values,
 };
 
-NTSTATUS reg_rpc_init(void)
+NTSTATUS registry_rpc_init(void)
 {
        return register_backend("registry", &reg_backend_rpc);
 }