diff options
Diffstat (limited to 'source3/lib')
88 files changed, 6358 insertions, 1450 deletions
diff --git a/source3/lib/access.c b/source3/lib/access.c index 1e4df83607..6a463446d1 100644 --- a/source3/lib/access.c +++ b/source3/lib/access.c @@ -97,13 +97,29 @@ static bool string_match(const char *tok,const char *s) } } else if (tok[0] == '@') { /* netgroup: look it up */ #ifdef HAVE_NETGROUP - static char *mydomain = NULL; + DATA_BLOB tmp; + char *mydomain = NULL; char *hostname = NULL; bool netgroup_ok = false; - if (!mydomain) + if (memcache_lookup( + NULL, SINGLETON_CACHE, + data_blob_string_const("yp_default_domain"), + &tmp)) { + + SMB_ASSERT(tmp.length > 0); + mydomain = (tmp.data[0] == '\0') + ? NULL : (char *)tmp.data; + } + else { yp_get_default_domain(&mydomain); + memcache_add( + NULL, SINGLETON_CACHE, + data_blob_string_const("yp_default_domain"), + data_blob_string_const(mydomain?mydomain:"")); + } + if (!mydomain) { DEBUG(0,("Unable to get default yp domain. " "Try without it.\n")); diff --git a/source3/lib/afs.c b/source3/lib/afs.c index a7d6f6c9f7..9f5d81f442 100644 --- a/source3/lib/afs.c +++ b/source3/lib/afs.c @@ -42,20 +42,23 @@ static char *afs_encode_token(const char *cell, const DATA_BLOB ticket, const struct ClearToken *ct) { char *base64_ticket; - char *result; + char *result = NULL; DATA_BLOB key = data_blob(ct->HandShakeKey, 8); char *base64_key; + TALLOC_CTX *mem_ctx; + + mem_ctx = talloc_stackframe(); + if (mem_ctx == NULL) + goto done; - base64_ticket = base64_encode_data_blob(ticket); + base64_ticket = base64_encode_data_blob(mem_ctx, ticket); if (base64_ticket == NULL) - return NULL; + goto done; - base64_key = base64_encode_data_blob(key); - if (base64_key == NULL) { - TALLOC_FREE(base64_ticket); - return NULL; - } + base64_key = base64_encode_data_blob(mem_ctx, key); + if (base64_key == NULL) + goto done; asprintf(&result, "%s\n%u\n%s\n%u\n%u\n%u\n%s\n", cell, ct->AuthHandle, base64_key, ct->ViceId, ct->BeginTimestamp, @@ -63,8 +66,8 @@ static char *afs_encode_token(const char *cell, const DATA_BLOB ticket, DEBUG(10, ("Got ticket string:\n%s\n", result)); - TALLOC_FREE(base64_ticket); - TALLOC_FREE(base64_key); +done: + TALLOC_FREE(mem_ctx); return result; } diff --git a/source3/lib/afs_settoken.c b/source3/lib/afs_settoken.c index aeef1c3520..444f09efca 100644 --- a/source3/lib/afs_settoken.c +++ b/source3/lib/afs_settoken.c @@ -53,20 +53,21 @@ static bool afs_decode_token(const char *string, char **cell, { DATA_BLOB blob; struct ClearToken result_ct; + char *saveptr; char *s = SMB_STRDUP(string); char *t; - if ((t = strtok(s, "\n")) == NULL) { - DEBUG(10, ("strtok failed\n")); + if ((t = strtok_r(s, "\n", &saveptr)) == NULL) { + DEBUG(10, ("strtok_r failed\n")); return False; } *cell = SMB_STRDUP(t); - if ((t = strtok(NULL, "\n")) == NULL) { - DEBUG(10, ("strtok failed\n")); + if ((t = strtok_r(NULL, "\n", &saveptr)) == NULL) { + DEBUG(10, ("strtok_r failed\n")); return False; } @@ -75,8 +76,8 @@ static bool afs_decode_token(const char *string, char **cell, return False; } - if ((t = strtok(NULL, "\n")) == NULL) { - DEBUG(10, ("strtok failed\n")); + if ((t = strtok_r(NULL, "\n", &saveptr)) == NULL) { + DEBUG(10, ("strtok_r failed\n")); return False; } @@ -93,8 +94,8 @@ static bool afs_decode_token(const char *string, char **cell, data_blob_free(&blob); - if ((t = strtok(NULL, "\n")) == NULL) { - DEBUG(10, ("strtok failed\n")); + if ((t = strtok_r(NULL, "\n", &saveptr)) == NULL) { + DEBUG(10, ("strtok_r failed\n")); return False; } @@ -103,8 +104,8 @@ static bool afs_decode_token(const char *string, char **cell, return False; } - if ((t = strtok(NULL, "\n")) == NULL) { - DEBUG(10, ("strtok failed\n")); + if ((t = strtok_r(NULL, "\n", &saveptr)) == NULL) { + DEBUG(10, ("strtok_r failed\n")); return False; } @@ -113,8 +114,8 @@ static bool afs_decode_token(const char *string, char **cell, return False; } - if ((t = strtok(NULL, "\n")) == NULL) { - DEBUG(10, ("strtok failed\n")); + if ((t = strtok_r(NULL, "\n", &saveptr)) == NULL) { + DEBUG(10, ("strtok_r failed\n")); return False; } @@ -123,8 +124,8 @@ static bool afs_decode_token(const char *string, char **cell, return False; } - if ((t = strtok(NULL, "\n")) == NULL) { - DEBUG(10, ("strtok failed\n")); + if ((t = strtok_r(NULL, "\n", &saveptr)) == NULL) { + DEBUG(10, ("strtok_r failed\n")); return False; } diff --git a/source3/lib/charcnv.c b/source3/lib/charcnv.c index 8a00b235cc..eeff805459 100644 --- a/source3/lib/charcnv.c +++ b/source3/lib/charcnv.c @@ -614,10 +614,16 @@ size_t convert_string_allocate(TALLOC_CTX *ctx, charset_t from, charset_t to, out: destlen = destlen - o_len; - if (ctx) { - ob = (char *)TALLOC_REALLOC(ctx,ob,destlen); - } else { - ob = (char *)SMB_REALLOC(ob,destlen); + /* Don't shrink unless we're reclaiming a lot of + * space. This is in the hot codepath and these + * reallocs *cost*. JRA. + */ + if (o_len > 1024) { + if (ctx) { + ob = (char *)TALLOC_REALLOC(ctx,ob,destlen); + } else { + ob = (char *)SMB_REALLOC(ob,destlen); + } } if (destlen && !ob) { @@ -778,7 +784,7 @@ char *strdup_upper(const char *s) while (*p) { if (*p & 0x80) break; - *q++ = toupper_ascii(*p); + *q++ = toupper_ascii_fast(*p); p++; } @@ -844,7 +850,7 @@ char *talloc_strdup_upper(TALLOC_CTX *ctx, const char *s) while (*p) { if (*p & 0x80) break; - *q++ = toupper_ascii(*p); + *q++ = toupper_ascii_fast(*p); p++; } diff --git a/source3/lib/conn_tdb.c b/source3/lib/conn_tdb.c index dd0a354a85..22d85c873d 100644 --- a/source3/lib/conn_tdb.c +++ b/source3/lib/conn_tdb.c @@ -34,7 +34,7 @@ static struct db_context *connections_db_ctx(bool rw) } else { db_ctx = db_open(NULL, lock_path("connections.tdb"), 0, - TDB_DEFAULT, O_RDONLY, 0); + TDB_CLEAR_IF_FIRST|TDB_DEFAULT, O_RDONLY, 0); } return db_ctx; diff --git a/source3/lib/ctdbd_conn.c b/source3/lib/ctdbd_conn.c index 899bbcfcce..18e9879601 100644 --- a/source3/lib/ctdbd_conn.c +++ b/source3/lib/ctdbd_conn.c @@ -1203,6 +1203,42 @@ NTSTATUS ctdbd_register_reconfigure(struct ctdbd_connection *conn) return register_with_ctdbd(conn, CTDB_SRVID_RECONFIGURE); } +/* + persstent store. Used when we update a record in a persistent database + */ +NTSTATUS ctdbd_persistent_store(struct ctdbd_connection *conn, uint32_t db_id, TDB_DATA key, TDB_DATA data) +{ + int cstatus=0; + struct ctdb_rec_data *rec; + TDB_DATA recdata; + size_t length; + NTSTATUS status; + + length = offsetof(struct ctdb_rec_data, data) + key.dsize + data.dsize; + + rec = (struct ctdb_rec_data *)talloc_size(conn, length); + NT_STATUS_HAVE_NO_MEMORY(rec); + + rec->length = length; + rec->reqid = db_id; + rec->keylen = key.dsize; + rec->datalen= data.dsize; + memcpy(&rec->data[0], key.dptr, key.dsize); + memcpy(&rec->data[key.dsize], data.dptr, data.dsize); + + recdata.dptr = (uint8_t *)rec; + recdata.dsize = length; + + status = ctdbd_control(conn, CTDB_CURRENT_NODE, + CTDB_CONTROL_PERSISTENT_STORE, + 0, recdata, NULL, NULL, &cstatus); + if (cstatus != 0) { + return NT_STATUS_INTERNAL_DB_CORRUPTION; + } + return status; +} + + #else NTSTATUS ctdbd_init_connection(TALLOC_CTX *mem_ctx, diff --git a/source3/lib/data_blob.c b/source3/lib/data_blob.c index e64e6a19a1..8bbbc32d7b 100644 --- a/source3/lib/data_blob.c +++ b/source3/lib/data_blob.c @@ -128,7 +128,7 @@ DATA_BLOB data_blob_string_const(const char *str) { DATA_BLOB blob; blob.data = CONST_DISCARD(uint8 *, str); - blob.length = strlen(str); + blob.length = strlen(str) + 1; blob.free = NULL; return blob; } diff --git a/source3/lib/dbwrap.c b/source3/lib/dbwrap.c index 4e16d18682..001424a6c0 100644 --- a/source3/lib/dbwrap.c +++ b/source3/lib/dbwrap.c @@ -20,7 +20,9 @@ */ #include "includes.h" - +#ifdef CLUSTER_SUPPORT +#include "ctdb_private.h" +#endif /* * Fall back using fetch_locked if no genuine fetch operation is provided */ @@ -46,10 +48,16 @@ struct db_context *db_open(TALLOC_CTX *mem_ctx, int open_flags, mode_t mode) { struct db_context *result = NULL; +#ifdef CLUSTER_SUPPORT + const char *sockname = lp_ctdbd_socket(); +#endif #ifdef CLUSTER_SUPPORT + if(!sockname || !*sockname) { + sockname = CTDB_PATH; + } - if (lp_clustering()) { + if (lp_clustering() && socket_exist(sockname)) { const char *partname; /* ctdb only wants the file part of the name */ partname = strrchr(name, '/'); diff --git a/source3/lib/dbwrap_ctdb.c b/source3/lib/dbwrap_ctdb.c index 90d0b260c5..f497f871d2 100644 --- a/source3/lib/dbwrap_ctdb.c +++ b/source3/lib/dbwrap_ctdb.c @@ -18,16 +18,14 @@ */ #include "includes.h" - #ifdef CLUSTER_SUPPORT - #include "ctdb.h" #include "ctdb_private.h" +#include "ctdbd_conn.h" struct db_ctdb_ctx { struct tdb_wrap *wtdb; uint32 db_id; - struct ctdbd_connection *conn; }; struct db_ctdb_rec { @@ -35,8 +33,6 @@ struct db_ctdb_rec { struct ctdb_ltdb_header header; }; -static struct ctdbd_connection *db_ctdbd_conn(struct db_ctdb_ctx *ctx); - static NTSTATUS db_ctdb_store(struct db_record *rec, TDB_DATA data, int flag) { struct db_ctdb_rec *crec = talloc_get_type_abort( @@ -60,6 +56,42 @@ static NTSTATUS db_ctdb_store(struct db_record *rec, TDB_DATA data, int flag) return (ret == 0) ? NT_STATUS_OK : NT_STATUS_INTERNAL_DB_CORRUPTION; } + +/* for persistent databases the store is a bit different. We have to + ask the ctdb daemon to push the record to all nodes after the + store */ +static NTSTATUS db_ctdb_store_persistent(struct db_record *rec, TDB_DATA data, int flag) +{ + struct db_ctdb_rec *crec = talloc_get_type_abort( + rec->private_data, struct db_ctdb_rec); + TDB_DATA cdata; + int ret; + NTSTATUS status; + + cdata.dsize = sizeof(crec->header) + data.dsize; + + if (!(cdata.dptr = SMB_MALLOC_ARRAY(uint8, cdata.dsize))) { + return NT_STATUS_NO_MEMORY; + } + + crec->header.rsn++; + + memcpy(cdata.dptr, &crec->header, sizeof(crec->header)); + memcpy(cdata.dptr + sizeof(crec->header), data.dptr, data.dsize); + + ret = tdb_store(crec->ctdb_ctx->wtdb->tdb, rec->key, cdata, TDB_REPLACE); + status = (ret == 0) ? NT_STATUS_OK : NT_STATUS_INTERNAL_DB_CORRUPTION; + + /* now tell ctdbd to update this record on all other nodes */ + if (NT_STATUS_IS_OK(status)) { + status = ctdbd_persistent_store(messaging_ctdbd_connection(), crec->ctdb_ctx->db_id, rec->key, cdata); + } + + SAFE_FREE(cdata.dptr); + + return status; +} + static NTSTATUS db_ctdb_delete(struct db_record *rec) { struct db_ctdb_rec *crec = talloc_get_type_abort( @@ -110,6 +142,7 @@ static struct db_record *db_ctdb_fetch_locked(struct db_context *db, struct db_ctdb_rec *crec; NTSTATUS status; TDB_DATA ctdb_data; + int migrate_attempts = 0; if (!(result = talloc(mem_ctx, struct db_record))) { DEBUG(0, ("talloc failed\n")); @@ -153,7 +186,11 @@ again: return NULL; } - result->store = db_ctdb_store; + if (db->persistent) { + result->store = db_ctdb_store_persistent; + } else { + result->store = db_ctdb_store; + } result->delete_rec = db_ctdb_delete; talloc_set_destructor(result, db_ctdb_record_destr); @@ -175,12 +212,14 @@ again: tdb_chainunlock(ctx->wtdb->tdb, key); talloc_set_destructor(result, NULL); + migrate_attempts += 1; + DEBUG(10, ("ctdb_data.dptr = %p, dmaster = %u (%u)\n", ctdb_data.dptr, ctdb_data.dptr ? ((struct ctdb_ltdb_header *)ctdb_data.dptr)->dmaster : -1, get_my_vnn())); - status = ctdbd_migrate(db_ctdbd_conn(ctx), ctx->db_id, key); + status = ctdbd_migrate(messaging_ctdbd_connection(),ctx->db_id, key); if (!NT_STATUS_IS_OK(status)) { DEBUG(5, ("ctdb_migrate failed: %s\n", nt_errstr(status))); @@ -191,6 +230,11 @@ again: goto again; } + if (migrate_attempts > 10) { + DEBUG(0, ("db_ctdb_fetch_locked needed %d attempts\n", + migrate_attempts)); + } + memcpy(&crec->header, ctdb_data.dptr, sizeof(crec->header)); result->value.dsize = ctdb_data.dsize - sizeof(crec->header); @@ -226,10 +270,12 @@ static int db_ctdb_fetch(struct db_context *db, TALLOC_CTX *mem_ctx, /* * See if we have a valid record and we are the dmaster. If so, we can * take the shortcut and just return it. + * we bypass the dmaster check for persistent databases */ if ((ctdb_data.dptr != NULL) && (ctdb_data.dsize >= sizeof(struct ctdb_ltdb_header)) && - ((struct ctdb_ltdb_header *)ctdb_data.dptr)->dmaster == get_my_vnn()) { + (db->persistent || + ((struct ctdb_ltdb_header *)ctdb_data.dptr)->dmaster == get_my_vnn())) { /* we are the dmaster - avoid the ctdb protocol op */ data->dsize = ctdb_data.dsize - sizeof(struct ctdb_ltdb_header); @@ -254,8 +300,7 @@ static int db_ctdb_fetch(struct db_context *db, TALLOC_CTX *mem_ctx, SAFE_FREE(ctdb_data.dptr); /* we weren't able to get it locally - ask ctdb to fetch it for us */ - status = ctdbd_fetch(db_ctdbd_conn(ctx), ctx->db_id, key, mem_ctx, - data); + status = ctdbd_fetch(messaging_ctdbd_connection(),ctx->db_id, key, mem_ctx, data); if (!NT_STATUS_IS_OK(status)) { DEBUG(5, ("ctdbd_fetch failed: %s\n", nt_errstr(status))); return -1; @@ -283,6 +328,22 @@ static void traverse_callback(TDB_DATA key, TDB_DATA data, void *private_data) talloc_free(tmp_ctx); } +static int traverse_persistent_callback(TDB_CONTEXT *tdb, TDB_DATA kbuf, TDB_DATA dbuf, + void *private_data) +{ + struct traverse_state *state = (struct traverse_state *)private_data; + struct db_record *rec; + TALLOC_CTX *tmp_ctx = talloc_new(state->db); + int ret = 0; + /* we have to give them a locked record to prevent races */ + rec = db_ctdb_fetch_locked(state->db, tmp_ctx, kbuf); + if (rec && rec->value.dsize > 0) { + ret = state->fn(rec, state->private_data); + } + talloc_free(tmp_ctx); + return ret; +} + static int db_ctdb_traverse(struct db_context *db, int (*fn)(struct db_record *rec, void *private_data), @@ -296,6 +357,13 @@ static int db_ctdb_traverse(struct db_context *db, state.fn = fn; state.private_data = private_data; + if (db->persistent) { + /* for persistent databases we don't need to do a ctdb traverse, + we can do a faster local traverse */ + return tdb_traverse(ctx->wtdb->tdb, traverse_persistent_callback, &state); + } + + ctdbd_traverse(ctx->db_id, traverse_callback, &state); return 0; } @@ -322,6 +390,27 @@ static void traverse_read_callback(TDB_DATA key, TDB_DATA data, void *private_da state->fn(&rec, state->private_data); } +static int traverse_persistent_callback_read(TDB_CONTEXT *tdb, TDB_DATA kbuf, TDB_DATA dbuf, + void *private_data) +{ + struct traverse_state *state = (struct traverse_state *)private_data; + struct db_record rec; + rec.key = kbuf; + rec.value = dbuf; + rec.store = db_ctdb_store_deny; + rec.delete_rec = db_ctdb_delete_deny; + rec.private_data = state->db; + + if (rec.value.dsize <= sizeof(struct ctdb_ltdb_header)) { + /* a deleted record */ + return 0; + } + rec.value.dsize -= sizeof(struct ctdb_ltdb_header); + rec.value.dptr += sizeof(struct ctdb_ltdb_header); + + return state->fn(&rec, state->private_data); +} + static int db_ctdb_traverse_read(struct db_context *db, int (*fn)(struct db_record *rec, void *private_data), @@ -335,6 +424,12 @@ static int db_ctdb_traverse_read(struct db_context *db, state.fn = fn; state.private_data = private_data; + if (db->persistent) { + /* for persistent databases we don't need to do a ctdb traverse, + we can do a faster local traverse */ + return tdb_traverse_read(ctx->wtdb->tdb, traverse_persistent_callback_read, &state); + } + ctdbd_traverse(ctx->db_id, traverse_read_callback, &state); return 0; } @@ -346,41 +441,6 @@ static int db_ctdb_get_seqnum(struct db_context *db) return tdb_get_seqnum(ctx->wtdb->tdb); } -/* - * Get the ctdbd connection for a database. If possible, re-use the messaging - * ctdbd connection - */ -static struct ctdbd_connection *db_ctdbd_conn(struct db_ctdb_ctx *ctx) -{ - struct ctdbd_connection *result; - - result = messaging_ctdbd_connection(); - - if (result != NULL) { - - if (ctx->conn == NULL) { - /* - * Someone has initialized messaging since we - * initialized our own connection, we don't need it - * anymore. - */ - TALLOC_FREE(ctx->conn); - } - - return result; - } - - if (ctx->conn == NULL) { - NTSTATUS status; - status = ctdbd_init_connection(ctx, &ctx->conn); - if (!NT_STATUS_IS_OK(status)) { - return NULL; - } - set_my_vnn(ctdbd_vnn(ctx->conn)); - } - - return ctx->conn; -} struct db_context *db_open_ctdb(TALLOC_CTX *mem_ctx, const char *name, @@ -390,7 +450,6 @@ struct db_context *db_open_ctdb(TALLOC_CTX *mem_ctx, struct db_context *result; struct db_ctdb_ctx *db_ctdb; char *db_path; - NTSTATUS status; if (!lp_clustering()) { DEBUG(10, ("Clustering disabled -- no ctdb\n")); @@ -409,20 +468,15 @@ struct db_context *db_open_ctdb(TALLOC_CTX *mem_ctx, return NULL; } - db_ctdb->conn = NULL; - - status = ctdbd_db_attach(db_ctdbd_conn(db_ctdb), name, - &db_ctdb->db_id, tdb_flags); - - if (!NT_STATUS_IS_OK(status)) { - DEBUG(0, ("ctdbd_db_attach failed for %s: %s\n", name, - nt_errstr(status))); + if (!NT_STATUS_IS_OK(ctdbd_db_attach(messaging_ctdbd_connection(),name, &db_ctdb->db_id, tdb_flags))) { + DEBUG(0, ("ctdbd_db_attach failed for %s\n", name)); TALLOC_FREE(result); return NULL; } - db_path = ctdbd_dbpath(db_ctdbd_conn(db_ctdb), db_ctdb, - db_ctdb->db_id); + db_path = ctdbd_dbpath(messaging_ctdbd_connection(), db_ctdb, db_ctdb->db_id); + + result->persistent = ((tdb_flags & TDB_CLEAR_IF_FIRST) == 0); /* only pass through specific flags */ tdb_flags &= TDB_SEQNUM; @@ -447,16 +501,4 @@ struct db_context *db_open_ctdb(TALLOC_CTX *mem_ctx, return result; } - -#else - -struct db_context *db_open_ctdb(TALLOC_CTX *mem_ctx, - const char *name, - int hash_size, int tdb_flags, - int open_flags, mode_t mode) -{ - DEBUG(0, ("no clustering compiled in\n")); - return NULL; -} - #endif diff --git a/source3/lib/dbwrap_file.c b/source3/lib/dbwrap_file.c index 5b41f5941b..e3779de1e4 100644 --- a/source3/lib/dbwrap_file.c +++ b/source3/lib/dbwrap_file.c @@ -17,10 +17,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -/* - * Be aware that this is just sample code that has not seen too much testing - */ - #include "includes.h" struct db_file_ctx { @@ -367,6 +363,7 @@ struct db_context *db_open_file(TALLOC_CTX *mem_ctx, result->fetch_locked = db_file_fetch_locked; result->traverse = db_file_traverse; result->traverse_read = db_file_traverse; + result->persistent = ((tdb_flags & TDB_CLEAR_IF_FIRST) == 0); ctx->locked_record = NULL; if (!(ctx->dirname = talloc_strdup(ctx, name))) { diff --git a/source3/lib/dbwrap_rbt.c b/source3/lib/dbwrap_rbt.c index df568a0410..633b695b52 100644 --- a/source3/lib/dbwrap_rbt.c +++ b/source3/lib/dbwrap_rbt.c @@ -1,7 +1,7 @@ /* Unix SMB/CIFS implementation. Database interface wrapper around red-black trees - Copyright (C) Volker Lendecke 2007 + Copyright (C) Volker Lendecke 2007, 2008 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 @@ -20,6 +20,8 @@ #include "includes.h" #include "rbtree.h" +#define ALIGN(_size_) (((_size_)+15)&~15) + struct db_rbt_ctx { struct rb_root tree; }; @@ -44,6 +46,35 @@ struct db_rbt_node { }; /* + * Hide the ugly pointer calculations in a function + */ + +static struct db_rbt_node *db_rbt2node(struct rb_node *node) +{ + return (struct db_rbt_node *) + ((char *)node - offsetof(struct db_rbt_node, rb_node)); +} + +/* + * Compare two keys + */ + +static int db_rbt_compare(TDB_DATA a, TDB_DATA b) +{ + int res; + + res = memcmp(a.dptr, b.dptr, MIN(a.dsize, b.dsize)); + + if ((res < 0) || ((res == 0) && (a.dsize < b.dsize))) { + return -1; + } + if ((res > 0) || ((res == 0) && (a.dsize > b.dsize))) { + return 1; + } + return 0; +} + +/* * dissect a db_rbt_node into its implicit key and value parts */ @@ -58,9 +89,7 @@ static void db_rbt_parse_node(struct db_rbt_node *node, static NTSTATUS db_rbt_store(struct db_record *rec, TDB_DATA data, int flag) { - struct db_rbt_rec *rec_priv = talloc_get_type_abort( - rec->private_data, struct db_rbt_rec); - + struct db_rbt_rec *rec_priv = (struct db_rbt_rec *)rec->private_data; struct db_rbt_node *node; struct rb_node ** p; @@ -95,7 +124,11 @@ static NTSTATUS db_rbt_store(struct db_record *rec, TDB_DATA data, int flag) */ rb_erase(&rec_priv->node->rb_node, &rec_priv->db_ctx->tree); - SAFE_FREE(rec_priv->node); + + /* + * Keep the existing node around for a while: If the record + * existed before, we reference the key data in there. + */ } node = (struct db_rbt_node *)SMB_MALLOC( @@ -103,6 +136,7 @@ static NTSTATUS db_rbt_store(struct db_record *rec, TDB_DATA data, int flag) + data.dsize); if (node == NULL) { + SAFE_FREE(rec_priv->node); return NT_STATUS_NO_MEMORY; } @@ -114,6 +148,8 @@ static NTSTATUS db_rbt_store(struct db_record *rec, TDB_DATA data, int flag) db_rbt_parse_node(node, &this_key, &this_val); memcpy(this_key.dptr, rec->key.dptr, node->keysize); + SAFE_FREE(rec_priv->node); + memcpy(this_val.dptr, data.dptr, node->valuesize); parent = NULL; @@ -126,22 +162,16 @@ static NTSTATUS db_rbt_store(struct db_record *rec, TDB_DATA data, int flag) parent = (*p); - r = (struct db_rbt_node *) - ((char *)(*p) - offsetof(struct db_rbt_node, rb_node)); + r = db_rbt2node(*p); db_rbt_parse_node(r, &search_key, &search_val); - res = memcmp(this_key.dptr, search_key.dptr, - MIN(this_key.dsize, search_key.dsize)); + res = db_rbt_compare(this_key, search_key); - if ((res < 0) - || ((res == 0) - && (this_key.dsize < search_key.dsize))) { + if (res == -1) { p = &(*p)->rb_left; } - else if ((res > 0) - || ((res == 0) - && (this_key.dsize > search_key.dsize))) { + else if (res == 1) { p = &(*p)->rb_right; } else { @@ -157,8 +187,7 @@ static NTSTATUS db_rbt_store(struct db_record *rec, TDB_DATA data, int flag) static NTSTATUS db_rbt_delete(struct db_record *rec) { - struct db_rbt_rec *rec_priv = talloc_get_type_abort( - rec->private_data, struct db_rbt_rec); + struct db_rbt_rec *rec_priv = (struct db_rbt_rec *)rec->private_data; if (rec_priv->node == NULL) { return NT_STATUS_OK; @@ -180,85 +209,128 @@ static struct db_record *db_rbt_fetch_locked(struct db_context *db_ctx, struct db_rbt_rec *rec_priv; struct db_record *result; struct rb_node *n; + size_t size; + bool found = false; + struct db_rbt_node *r = NULL; + TDB_DATA search_key = tdb_null, search_val = tdb_null; - result = talloc(mem_ctx, struct db_record); + n = ctx->tree.rb_node; - if (result == NULL) { - return NULL; + while (n != NULL) { + int res; + + r = db_rbt2node(n); + + db_rbt_parse_node(r, &search_key, &search_val); + + res = db_rbt_compare(key, search_key); + + if (res == -1) { + n = n->rb_left; + } + else if (res == 1) { + n = n->rb_right; + } + else { + found = true; + break; + } } - rec_priv = talloc(result, struct db_rbt_rec); + /* + * In this low-level routine, play tricks to reduce the number of + * tallocs to one. Not recommened for general use, but here it pays + * off. + */ + + size = ALIGN(sizeof(struct db_record)) + sizeof(struct db_rbt_rec); - if (rec_priv == NULL) { - TALLOC_FREE(result); + if (!found) { + /* + * We need to keep the key around for later store + */ + size += key.dsize; + } + + result = (struct db_record *)talloc_size(mem_ctx, size); + if (result == NULL) { return NULL; } + rec_priv = (struct db_rbt_rec *) + ((char *)result + ALIGN(sizeof(struct db_record))); rec_priv->db_ctx = ctx; result->store = db_rbt_store; result->delete_rec = db_rbt_delete; result->private_data = rec_priv; + if (found) { + rec_priv->node = r; + result->key = search_key; + result->value = search_val; + } + else { + rec_priv->node = NULL; + result->key.dptr = (uint8 *) + ((char *)rec_priv + sizeof(*rec_priv)); + result->key.dsize = key.dsize; + memcpy(result->key.dptr, key.dptr, key.dsize); + + result->value = tdb_null; + } + + return result; +} + +static int db_rbt_fetch(struct db_context *db, TALLOC_CTX *mem_ctx, + TDB_DATA key, TDB_DATA *data) +{ + struct db_rbt_ctx *ctx = talloc_get_type_abort( + db->private_data, struct db_rbt_ctx); + + struct rb_node *n; + bool found = false; + struct db_rbt_node *r = NULL; + TDB_DATA search_key, search_val; + uint8_t *result; + n = ctx->tree.rb_node; while (n != NULL) { - struct db_rbt_node *r; - TDB_DATA search_key, search_val; int res; - r = (struct db_rbt_node *) - ((char *)n - offsetof(struct db_rbt_node, rb_node)); + r = db_rbt2node(n); db_rbt_parse_node(r, &search_key, &search_val); - res = memcmp(key.dptr, search_key.dptr, - MIN(key.dsize, search_key.dsize)); + res = db_rbt_compare(key, search_key); - if ((res < 0) - || ((res == 0) && (key.dsize < search_key.dsize))) { + if (res == -1) { n = n->rb_left; } - else if ((res > 0) - || ((res == 0) && (key.dsize > search_key.dsize))) { + else if (res == 1) { n = n->rb_right; } else { - rec_priv->node = r; - result->key = search_key; - result->value = search_val; - return result; + found = true; + break; } } - result->key.dsize = key.dsize; - result->key.dptr = (uint8_t *)talloc_memdup( - result, key.dptr, key.dsize); - - if (result->key.dptr == NULL) { - TALLOC_FREE(result); - return NULL; + if (!found) { + *data = tdb_null; + return 0; } - rec_priv->node = NULL; - result->value.dsize = 0; - result->value.dptr = NULL; - return result; -} - -static int db_rbt_fetch(struct db_context *db, TALLOC_CTX *mem_ctx, - TDB_DATA key, TDB_DATA *data) -{ - struct db_record *rec; - - if (!(rec = db->fetch_locked(db, mem_ctx, key))) { + result = (uint8 *)talloc_memdup(mem_ctx, search_val.dptr, + search_val.dsize); + if (result == NULL) { return -1; } - data->dsize = rec->value.dsize; - data->dptr = (uint8 *)talloc_memdup(mem_ctx, rec->value.dptr, - rec->value.dsize); - TALLOC_FREE(rec); + data->dptr = result; + data->dsize = search_val.dsize; return 0; } @@ -268,6 +340,9 @@ static int db_rbt_traverse(struct db_context *db, void *private_data), void *private_data) { + /* + * Nobody uses this so far, and unused code is broken code :-) + */ return -1; } diff --git a/source3/lib/dbwrap_tdb.c b/source3/lib/dbwrap_tdb.c index b24fd0618a..18f9495931 100644 --- a/source3/lib/dbwrap_tdb.c +++ b/source3/lib/dbwrap_tdb.c @@ -31,6 +31,11 @@ static int db_tdb_record_destr(struct db_record* data) struct db_tdb_ctx *ctx = talloc_get_type_abort(data->private_data, struct db_tdb_ctx); + /* This hex_encode() call allocates memory on data context. By way how current + __talloc_free() code works, it is OK to allocate in the destructor as + the children of data will be freed after call to the destructor and this + new 'child' will be caught and freed correctly. + */ DEBUG(10, (DEBUGLEVEL > 10 ? "Unlocking key %s\n" : "Unlocking key %.20s\n", hex_encode(data, (unsigned char *)data->key.dptr, @@ -43,36 +48,53 @@ static int db_tdb_record_destr(struct db_record* data) return 0; } -static struct db_record *db_tdb_fetch_locked(struct db_context *db, - TALLOC_CTX *mem_ctx, TDB_DATA key) -{ - struct db_tdb_ctx *ctx = talloc_get_type_abort(db->private_data, - struct db_tdb_ctx); +struct tdb_fetch_locked_state { + TALLOC_CTX *mem_ctx; struct db_record *result; - TDB_DATA value; +}; - result = TALLOC_P(mem_ctx, struct db_record); - if (result == NULL) { - DEBUG(0, ("talloc failed\n")); - return NULL; +static int db_tdb_fetchlock_parse(TDB_DATA key, TDB_DATA data, + void *private_data) +{ + struct tdb_fetch_locked_state *state = + (struct tdb_fetch_locked_state *)private_data; + + state->result = (struct db_record *)talloc_size( + state->mem_ctx, + sizeof(struct db_record) + key.dsize + data.dsize); + + if (state->result == NULL) { + return 0; } - result->key.dsize = key.dsize; - result->key.dptr = (uint8 *)talloc_memdup(result, key.dptr, key.dsize); - if (result->key.dptr == NULL) { - DEBUG(0, ("talloc failed\n")); - TALLOC_FREE(result); - return NULL; + state->result->key.dsize = key.dsize; + state->result->key.dptr = ((uint8 *)state->result) + + sizeof(struct db_record); + memcpy(state->result->key.dptr, key.dptr, key.dsize); + + state->result->value.dsize = data.dsize; + + if (data.dsize > 0) { + state->result->value.dptr = state->result->key.dptr+key.dsize; + memcpy(state->result->value.dptr, data.dptr, data.dsize); + } + else { + state->result->value.dptr = NULL; } - result->value.dptr = NULL; - result->value.dsize = 0; - result->private_data = talloc_reference(result, ctx); - result->store = db_tdb_store; - result->delete_rec = db_tdb_delete; + return 0; +} - if (DEBUGLEVEL >= 10) { - char *keystr = hex_encode(NULL, key.dptr, key.dsize); +static struct db_record *db_tdb_fetch_locked(struct db_context *db, + TALLOC_CTX *mem_ctx, TDB_DATA key) +{ + struct db_tdb_ctx *ctx = talloc_get_type_abort(db->private_data, + struct db_tdb_ctx); + struct tdb_fetch_locked_state state; + + /* Do not accidently allocate/deallocate w/o need when debug level is lower than needed */ + if(DEBUGLEVEL >= 10) { + char *keystr = hex_encode(NULL, (unsigned char*)key.dptr, key.dsize); DEBUG(10, (DEBUGLEVEL > 10 ? "Locking key %s\n" : "Locking key %.20s\n", keystr)); @@ -81,32 +103,55 @@ static struct db_record *db_tdb_fetch_locked(struct db_context *db, if (tdb_chainlock(ctx->wtdb->tdb, key) != 0) { DEBUG(3, ("tdb_chainlock failed\n")); - TALLOC_FREE(result); return NULL; } - talloc_set_destructor(result, db_tdb_record_destr); + state.mem_ctx = mem_ctx; + state.result = NULL; - value = tdb_fetch(ctx->wtdb->tdb, key); + tdb_parse_record(ctx->wtdb->tdb, key, db_tdb_fetchlock_parse, &state); - if (value.dptr == NULL) { - return result; + if (state.result == NULL) { + db_tdb_fetchlock_parse(key, tdb_null, &state); } - result->value.dsize = value.dsize; - result->value.dptr = (uint8 *)talloc_memdup(result, value.dptr, - value.dsize); - if (result->value.dptr == NULL) { - DEBUG(3, ("talloc failed\n")); - TALLOC_FREE(result); + if (state.result == NULL) { + tdb_chainunlock(ctx->wtdb->tdb, key); return NULL; } - SAFE_FREE(value.dptr); + talloc_set_destructor(state.result, db_tdb_record_destr); - DEBUG(10, ("Allocated locked data 0x%p\n", result)); + state.result->private_data = talloc_reference(state.result, ctx); + state.result->store = db_tdb_store; + state.result->delete_rec = db_tdb_delete; - return result; + DEBUG(10, ("Allocated locked data 0x%p\n", state.result)); + + return state.result; +} + +struct tdb_fetch_state { + TALLOC_CTX *mem_ctx; + int result; + TDB_DATA data; +}; + +static int db_tdb_fetch_parse(TDB_DATA key, TDB_DATA data, + void *private_data) +{ + struct tdb_fetch_state *state = + (struct tdb_fetch_state *)private_data; + + state->data.dptr = (uint8 *)talloc_memdup(state->mem_ctx, data.dptr, + data.dsize); + if (state->data.dptr == NULL) { + state->result = -1; + return 0; + } + + state->data.dsize = data.dsize; + return 0; } static int db_tdb_fetch(struct db_context *db, TALLOC_CTX *mem_ctx, @@ -115,23 +160,19 @@ static int db_tdb_fetch(struct db_context *db, TALLOC_CTX *mem_ctx, struct db_tdb_ctx *ctx = talloc_get_type_abort( db->private_data, struct db_tdb_ctx); - TDB_DATA data; - - data = tdb_fetch(ctx->wtdb->tdb, key); + struct tdb_fetch_state state; - if (data.dptr == NULL) { - pdata->dptr = NULL; - pdata->dsize = 0; - return 0; - } + state.mem_ctx = mem_ctx; + state.result = 0; + state.data = tdb_null; - pdata->dptr = (uint8 *)talloc_memdup(mem_ctx, data.dptr, data.dsize); - SAFE_FREE(data.dptr); + tdb_parse_record(ctx->wtdb->tdb, key, db_tdb_fetch_parse, &state); - if (pdata->dptr == NULL) { + if (state.result == -1) { return -1; } - pdata->dsize = data.dsize; + + *pdata = state.data; return 0; } @@ -154,15 +195,9 @@ static NTSTATUS db_tdb_delete(struct db_record *rec) { struct db_tdb_ctx *ctx = talloc_get_type_abort(rec->private_data, struct db_tdb_ctx); - int res; - - res = tdb_delete(ctx->wtdb->tdb, rec->key); - if (res == 0) { - return NT_STATUS_OK; - } - - return map_nt_error_from_tdb(tdb_error(ctx->wtdb->tdb)); + return (tdb_delete(ctx->wtdb->tdb, rec->key) == 0) ? + NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL; } struct db_tdb_traverse_ctx { @@ -281,6 +316,7 @@ struct db_context *db_open_tdb(TALLOC_CTX *mem_ctx, result->traverse = db_tdb_traverse; result->traverse_read = db_tdb_traverse_read; result->get_seqnum = db_tdb_get_seqnum; + result->persistent = ((tdb_flags & TDB_CLEAR_IF_FIRST) == 0); return result; fail: diff --git a/source3/lib/dbwrap_util.c b/source3/lib/dbwrap_util.c new file mode 100644 index 0000000000..002d572019 --- /dev/null +++ b/source3/lib/dbwrap_util.c @@ -0,0 +1,90 @@ +/* + Unix SMB/CIFS implementation. + Utility functions for the dbwrap API + Copyright (C) Volker Lendecke 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 + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" + +int32_t dbwrap_fetch_int32(struct db_context *db, const char *keystr) +{ + TDB_DATA dbuf; + int32 ret; + + if (db->fetch(db, NULL, string_term_tdb_data(keystr), &dbuf) != 0) { + return -1; + } + + if ((dbuf.dptr == NULL) || (dbuf.dsize != sizeof(int32_t))) { + TALLOC_FREE(dbuf.dptr); + return -1; + } + + ret = IVAL(dbuf.dptr, 0); + TALLOC_FREE(dbuf.dptr); + return ret; +} + +int dbwrap_store_int32(struct db_context *db, const char *keystr, int32_t v) +{ + struct db_record *rec; + int32 v_store; + NTSTATUS status; + + rec = db->fetch_locked(db, NULL, string_term_tdb_data(keystr)); + if (rec == NULL) { + return -1; + } + + SIVAL(&v_store, 0, v); + + status = rec->store(rec, make_tdb_data((const uint8 *)&v_store, + sizeof(v_store)), + TDB_REPLACE); + TALLOC_FREE(rec); + return NT_STATUS_IS_OK(status) ? 0 : -1; +} + +uint32_t dbwrap_change_uint32_atomic(struct db_context *db, const char *keystr, + uint32_t *oldval, uint32_t change_val) +{ + struct db_record *rec; + uint32 val = -1; + TDB_DATA data; + + if (!(rec = db->fetch_locked(db, NULL, + string_term_tdb_data(keystr)))) { + return -1; + } + + if ((rec->value.dptr != NULL) + && (rec->value.dsize == sizeof(val))) { + val = IVAL(rec->value.dptr, 0); + } + + val += change_val; + + data.dsize = sizeof(val); + data.dptr = (uint8 *)&val; + + rec->store(rec, data, TDB_REPLACE); + + TALLOC_FREE(rec); + + return 0; +} + diff --git a/source3/lib/debug.c b/source3/lib/debug.c index 87ec9ed8f5..51bb0d7541 100644 --- a/source3/lib/debug.c +++ b/source3/lib/debug.c @@ -199,6 +199,8 @@ void gfree_debugsyms(void) if ( DEBUGLEVEL_CLASS_ISSET != &debug_all_class_isset_hack ) SAFE_FREE( DEBUGLEVEL_CLASS_ISSET ); + + SAFE_FREE(format_bufr); } /**************************************************************************** @@ -427,8 +429,9 @@ static bool debug_parse_params(char **params) /* Fill in new debug class levels */ for (; i < debug_num_classes && params[i]; i++) { - if ((class_name=strtok(params[i],":")) && - (class_level=strtok(NULL, "\0")) && + char *saveptr; + if ((class_name = strtok_r(params[i],":", &saveptr)) && + (class_level = strtok_r(NULL, "\0", &saveptr)) && ((ndx = debug_lookup_classname(class_name)) != -1)) { DEBUGLEVEL_CLASS[ndx] = atoi(class_level); DEBUGLEVEL_CLASS_ISSET[ndx] = True; @@ -785,13 +788,13 @@ void check_log_size( void ) (void)x_vfprintf( dbf, format_str, ap ); va_end( ap ); errno = old_errno; - return( 0 ); + goto done; } /* prevent recursion by checking if reopen_logs() has temporaily set the debugf string to NULL */ if( debugf == NULL) - return( 0 ); + goto done; #ifdef WITH_SYSLOG if( !lp_syslog_only() ) @@ -806,7 +809,7 @@ void check_log_size( void ) x_setbuf( dbf, NULL ); } else { errno = old_errno; - return(0); + goto done; } } } @@ -855,10 +858,11 @@ void check_log_size( void ) (void)x_fflush( dbf ); } - errno = old_errno; - + done: TALLOC_FREE(tmp_debug_ctx); + errno = old_errno; + return( 0 ); } diff --git a/source3/lib/display_sec.c b/source3/lib/display_sec.c index f6a6bb6465..67392e4568 100644 --- a/source3/lib/display_sec.c +++ b/source3/lib/display_sec.c @@ -23,34 +23,92 @@ /**************************************************************************** convert a security permissions into a string ****************************************************************************/ -char *get_sec_mask_str(uint32 type) + +char *get_sec_mask_str(TALLOC_CTX *ctx, uint32 type) { - static fstring typestr=""; + char *typestr = talloc_strdup(ctx, ""); - typestr[0] = 0; + if (!typestr) { + return NULL; + } - if (type & GENERIC_ALL_ACCESS) - fstrcat(typestr, "Generic all access "); - if (type & GENERIC_EXECUTE_ACCESS) - fstrcat(typestr, "Generic execute access "); - if (type & GENERIC_WRITE_ACCESS) - fstrcat(typestr, "Generic write access "); - if (type & GENERIC_READ_ACCESS) - fstrcat(typestr, "Generic read access "); - if (type & MAXIMUM_ALLOWED_ACCESS) - fstrcat(typestr, "MAXIMUM_ALLOWED_ACCESS "); - if (type & SYSTEM_SECURITY_ACCESS) - fstrcat(typestr, "SYSTEM_SECURITY_ACCESS "); - if (type & SYNCHRONIZE_ACCESS) - fstrcat(typestr, "SYNCHRONIZE_ACCESS "); - if (type & WRITE_OWNER_ACCESS) - fstrcat(typestr, "WRITE_OWNER_ACCESS "); - if (type & WRITE_DAC_ACCESS) - fstrcat(typestr, "WRITE_DAC_ACCESS "); - if (type & READ_CONTROL_ACCESS) - fstrcat(typestr, "READ_CONTROL_ACCESS "); - if (type & DELETE_ACCESS) - fstrcat(typestr, "DELETE_ACCESS "); + if (type & GENERIC_ALL_ACCESS) { + typestr = talloc_asprintf_append(typestr, + "Generic all access "); + if (!typestr) { + return NULL; + } + } + if (type & GENERIC_EXECUTE_ACCESS) { + typestr = talloc_asprintf_append(typestr, + "Generic execute access"); + if (!typestr) { + return NULL; + } + } + if (type & GENERIC_WRITE_ACCESS) { + typestr = talloc_asprintf_append(typestr, + "Generic write access "); + if (!typestr) { + return NULL; + } + } + if (type & GENERIC_READ_ACCESS) { + typestr = talloc_asprintf_append(typestr, + "Generic read access "); + if (!typestr) { + return NULL; + } + } + if (type & MAXIMUM_ALLOWED_ACCESS) { + typestr = talloc_asprintf_append(typestr, + "MAXIMUM_ALLOWED_ACCESS "); + if (!typestr) { + return NULL; + } + } + if (type & SYSTEM_SECURITY_ACCESS) { + typestr = talloc_asprintf_append(typestr, + "SYSTEM_SECURITY_ACCESS "); + if (!typestr) { + return NULL; + } + } + if (type & SYNCHRONIZE_ACCESS) { + typestr = talloc_asprintf_append(typestr, + "SYNCHRONIZE_ACCESS "); + if (!typestr) { + return NULL; + } + } + if (type & WRITE_OWNER_ACCESS) { + typestr = talloc_asprintf_append(typestr, + "WRITE_OWNER_ACCESS "); + if (!typestr) { + return NULL; + } + } + if (type & WRITE_DAC_ACCESS) { + typestr = talloc_asprintf_append(typestr, + "WRITE_DAC_ACCESS "); + if (!typestr) { + return NULL; + } + } + if (type & READ_CONTROL_ACCESS) { + typestr = talloc_asprintf_append(typestr, + "READ_CONTROL_ACCESS "); + if (!typestr) { + return NULL; + } + } + if (type & DELETE_ACCESS) { + typestr = talloc_asprintf_append(typestr, + "DELETE_ACCESS "); + if (!typestr) { + return NULL; + } + } printf("\t\tSpecific bits: 0x%lx\n", (unsigned long)type&SPECIFIC_RIGHTS_MASK); @@ -62,7 +120,9 @@ char *get_sec_mask_str(uint32 type) ****************************************************************************/ void display_sec_access(SEC_ACCESS *info) { - printf("\t\tPermissions: 0x%x: %s\n", *info, get_sec_mask_str(*info)); + char *mask_str = get_sec_mask_str(NULL, *info); + printf("\t\tPermissions: 0x%x: %s\n", *info, mask_str ? mask_str : ""); + TALLOC_FREE(mask_str); } /**************************************************************************** diff --git a/source3/lib/dprintf.c b/source3/lib/dprintf.c index 18b261623e..a3bb5be43a 100644 --- a/source3/lib/dprintf.c +++ b/source3/lib/dprintf.c @@ -47,7 +47,10 @@ lang_msg_free(msgstr); - if (ret <= 0) return ret; + if (ret <= 0) { + va_end(ap2); + return ret; + } /* now we have the string in unix format, convert it to the display charset, but beware of it growing */ @@ -56,6 +59,7 @@ again: p2 = (char *)SMB_MALLOC(maxlen); if (!p2) { SAFE_FREE(p); + va_end(ap2); return -1; } clen = convert_string(CH_UNIX, CH_DISPLAY, p, ret, p2, maxlen, True); @@ -72,6 +76,8 @@ again: ret = fwrite(p2, 1, clen, f); SAFE_FREE(p2); + va_end(ap2); + return ret; } diff --git a/source3/lib/dummysmbd.c b/source3/lib/dummysmbd.c index e3b179b763..dbe886e3d1 100644 --- a/source3/lib/dummysmbd.c +++ b/source3/lib/dummysmbd.c @@ -51,4 +51,3 @@ NTSTATUS can_delete_directory(struct connection_struct *conn, { return NT_STATUS_OK; } - diff --git a/source3/lib/errmap_unix.c b/source3/lib/errmap_unix.c index 885a1c55b2..8194cf80cc 100644 --- a/source3/lib/errmap_unix.c +++ b/source3/lib/errmap_unix.c @@ -92,6 +92,9 @@ const struct unix_error_map unix_dos_nt_errmap[] = { #ifdef EWOULDBLOCK { EWOULDBLOCK, ERRDOS, 111, NT_STATUS_NETWORK_BUSY }, #endif +#ifdef ENOATTR + { ENOATTR, ERRDOS, ERRbadfile, NT_STATUS_NOT_FOUND }, +#endif { 0, 0, 0, NT_STATUS_OK } }; diff --git a/source3/lib/fault.c b/source3/lib/fault.c index 33e1401d7a..1964955f1b 100644 --- a/source3/lib/fault.c +++ b/source3/lib/fault.c @@ -132,7 +132,6 @@ void dump_core_setup(const char *progname) sys_chown(corepath,getuid(),getgid()); chmod(corepath,0700); - SAFE_FREE(corepath); SAFE_FREE(logbase); #ifdef HAVE_GETRLIMIT diff --git a/source3/lib/gencache.c b/source3/lib/gencache.c index a50e5d01fa..663385cfe3 100644 --- a/source3/lib/gencache.c +++ b/source3/lib/gencache.c @@ -32,7 +32,6 @@ #define BLOB_TYPE_LEN 9 static TDB_CONTEXT *cache; -static bool cache_readonly; /** * @file gencache.c @@ -67,7 +66,6 @@ bool gencache_init(void) if (!cache && (errno == EACCES)) { cache = tdb_open_log(cache_fname, 0, TDB_DEFAULT, O_RDONLY, 0644); if (cache) { - cache_readonly = True; DEBUG(5, ("gencache_init: Opening cache file %s read-only.\n", cache_fname)); } } @@ -95,7 +93,6 @@ bool gencache_shutdown(void) DEBUG(5, ("Closing cache file\n")); ret = tdb_close(cache); cache = NULL; - cache_readonly = False; return ret != -1; } @@ -123,10 +120,6 @@ bool gencache_set(const char *keystr, const char *value, time_t timeout) if (!gencache_init()) return False; - if (cache_readonly) { - return False; - } - asprintf(&valstr, CACHE_DATA_FMT, (int)timeout, value); if (!valstr) return False; @@ -161,10 +154,6 @@ bool gencache_del(const char *keystr) if (!gencache_init()) return False; - if (cache_readonly) { - return False; - } - DEBUG(10, ("Deleting cache entry (key = %s)\n", keystr)); ret = tdb_delete_bystring(cache, keystr); @@ -351,10 +340,6 @@ bool gencache_set_data_blob(const char *keystr, DATA_BLOB *blob, time_t timeout) return False; } - if (cache_readonly) { - return False; - } - asprintf(&valstr, "%12u/%s", (int)timeout, BLOB_TYPE); if (!valstr) { return False; diff --git a/source3/lib/interfaces.c b/source3/lib/interfaces.c index 722ab56abd..3797fc679d 100644 --- a/source3/lib/interfaces.c +++ b/source3/lib/interfaces.c @@ -84,6 +84,7 @@ #include <net/if.h> #endif +#define SOCKET_WRAPPER_NOT_REPLACE #include "interfaces.h" #include "lib/replace/replace.h" diff --git a/source3/lib/md4.c b/source3/lib/md4.c index 61d5848aae..bae0091e36 100644 --- a/source3/lib/md4.c +++ b/source3/lib/md4.c @@ -24,7 +24,14 @@ It assumes that a int is at least 32 bits long */ +#if 0 static uint32 A, B, C, D; +#else +#define A (state[0]) +#define B (state[1]) +#define C (state[2]) +#define D (state[3]) +#endif static uint32 F(uint32 X, uint32 Y, uint32 Z) { @@ -52,7 +59,7 @@ static uint32 lshift(uint32 x, int s) #define ROUND3(a,b,c,d,k,s) a = lshift(a + H(b,c,d) + X[k] + (uint32)0x6ED9EBA1,s) /* this applies md4 to 64 byte chunks */ -static void mdfour64(uint32 *M) +static void mdfour64(uint32_t *state, uint32 *M) { int j; uint32 AA, BB, CC, DD; @@ -121,6 +128,7 @@ void mdfour(unsigned char *out, const unsigned char *in, int n) { unsigned char buf[128]; uint32 M[16]; + uint32 state[4]; uint32 b = n * 8; int i; @@ -131,7 +139,7 @@ void mdfour(unsigned char *out, const unsigned char *in, int n) while (n > 64) { copy64(M, in); - mdfour64(M); + mdfour64(state, M); in += 64; n -= 64; } @@ -144,13 +152,13 @@ void mdfour(unsigned char *out, const unsigned char *in, int n) if (n <= 55) { copy4(buf+56, b); copy64(M, buf); - mdfour64(M); + mdfour64(state, M); } else { copy4(buf+120, b); copy64(M, buf); - mdfour64(M); + mdfour64(state, M); copy64(M, buf+64); - mdfour64(M); + mdfour64(state, M); } for (i=0;i<128;i++) @@ -161,8 +169,6 @@ void mdfour(unsigned char *out, const unsigned char *in, int n) copy4(out+4, B); copy4(out+8, C); copy4(out+12, D); - - A = B = C = D = 0; } diff --git a/source3/lib/memcache.c b/source3/lib/memcache.c new file mode 100644 index 0000000000..6dee61af50 --- /dev/null +++ b/source3/lib/memcache.c @@ -0,0 +1,388 @@ +/* + Unix SMB/CIFS implementation. + In-memory cache + Copyright (C) Volker Lendecke 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 + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "memcache.h" +#include "rbtree.h" + +static struct memcache *global_cache; + +struct memcache_element { + struct rb_node rb_node; + struct memcache_element *prev, *next; + size_t keylength, valuelength; + uint8 n; /* This is really an enum, but save memory */ + char data[1]; /* placeholder for offsetof */ +}; + +struct memcache { + struct memcache_element *mru, *lru; + struct rb_root tree; + size_t size; + size_t max_size; +}; + +static void memcache_element_parse(struct memcache_element *e, + DATA_BLOB *key, DATA_BLOB *value); + +static bool memcache_is_talloc(enum memcache_number n) +{ + bool result; + + switch (n) { + case GETPWNAM_CACHE: + case PDB_GETPWSID_CACHE: + case SINGLETON_CACHE_TALLOC: + result = true; + break; + default: + result = false; + break; + } + + return result; +} + +static int memcache_destructor(struct memcache *cache) { + struct memcache_element *e, *next; + + for (e = cache->mru; e != NULL; e = next) { + next = e->next; + if (memcache_is_talloc((enum memcache_number)e->n) + && (e->valuelength == sizeof(void *))) { + DATA_BLOB key, value; + void *ptr; + memcache_element_parse(e, &key, &value); + memcpy(&ptr, value.data, sizeof(ptr)); + TALLOC_FREE(ptr); + } + SAFE_FREE(e); + } + return 0; +} + +struct memcache *memcache_init(TALLOC_CTX *mem_ctx, size_t max_size) +{ + struct memcache *result; + + result = TALLOC_ZERO_P(mem_ctx, struct memcache); + if (result == NULL) { + return NULL; + } + result->max_size = max_size; + talloc_set_destructor(result, memcache_destructor); + return result; +} + +void memcache_set_global(struct memcache *cache) +{ + TALLOC_FREE(global_cache); + global_cache = cache; +} + +static struct memcache_element *memcache_node2elem(struct rb_node *node) +{ + return (struct memcache_element *) + ((char *)node - offsetof(struct memcache_element, rb_node)); +} + +static void memcache_element_parse(struct memcache_element *e, + DATA_BLOB *key, DATA_BLOB *value) +{ + key->data = ((uint8 *)e) + offsetof(struct memcache_element, data); + key->length = e->keylength; + value->data = key->data + e->keylength; + value->length = e->valuelength; +} + +static size_t memcache_element_size(size_t key_length, size_t value_length) +{ + return sizeof(struct memcache_element) - 1 + key_length + value_length; +} + +static int memcache_compare(struct memcache_element *e, enum memcache_number n, + DATA_BLOB key) +{ + DATA_BLOB this_key, this_value; + + if ((int)e->n < (int)n) return -1; + if ((int)e->n > (int)n) return 1; + + if (e->keylength < key.length) return -1; + if (e->keylength > key.length) return 1; + + memcache_element_parse(e, &this_key, &this_value); + return memcmp(this_key.data, key.data, key.length); +} + +static struct memcache_element *memcache_find( + struct memcache *cache, enum memcache_number n, DATA_BLOB key) +{ + struct rb_node *node; + + node = cache->tree.rb_node; + + while (node != NULL) { + struct memcache_element *elem = memcache_node2elem(node); + int cmp; + + cmp = memcache_compare(elem, n, key); + if (cmp == 0) { + return elem; + } + node = (cmp < 0) ? node->rb_left : node->rb_right; + } + + return NULL; +} + +bool memcache_lookup(struct memcache *cache, enum memcache_number n, + DATA_BLOB key, DATA_BLOB *value) +{ + struct memcache_element *e; + + if (cache == NULL) { + cache = global_cache; + } + if (cache == NULL) { + return false; + } + + e = memcache_find(cache, n, key); + if (e == NULL) { + return false; + } + + if (cache->size != 0) { + /* + * Do LRU promotion only when we will ever shrink + */ + if (e == cache->lru) { + cache->lru = e->prev; + } + DLIST_PROMOTE(cache->mru, e); + if (cache->mru == NULL) { + cache->mru = e; + } + } + + memcache_element_parse(e, &key, value); + return true; +} + +void *memcache_lookup_talloc(struct memcache *cache, enum memcache_number n, + DATA_BLOB key) +{ + DATA_BLOB value; + void *result; + + if (!memcache_lookup(cache, n, key, &value)) { + return NULL; + } + + if (value.length != sizeof(result)) { + return NULL; + } + + memcpy(&result, value.data, sizeof(result)); + + return result; +} + +static void memcache_delete_element(struct memcache *cache, + struct memcache_element *e) +{ + rb_erase(&e->rb_node, &cache->tree); + + if (e == cache->lru) { + cache->lru = e->prev; + } + DLIST_REMOVE(cache->mru, e); + + cache->size -= memcache_element_size(e->keylength, e->valuelength); + + SAFE_FREE(e); +} + +static void memcache_trim(struct memcache *cache) +{ + if (cache->max_size == 0) { + return; + } + + while ((cache->size > cache->max_size) && (cache->lru != NULL)) { + memcache_delete_element(cache, cache->lru); + } +} + +void memcache_delete(struct memcache *cache, enum memcache_number n, + DATA_BLOB key) +{ + struct memcache_element *e; + + if (cache == NULL) { + cache = global_cache; + } + if (cache == NULL) { + return; + } + + e = memcache_find(cache, n, key); + if (e == NULL) { + return; + } + + memcache_delete_element(cache, e); +} + +void memcache_add(struct memcache *cache, enum memcache_number n, + DATA_BLOB key, DATA_BLOB value) +{ + struct memcache_element *e; + struct rb_node **p; + struct rb_node *parent; + DATA_BLOB cache_key, cache_value; + size_t element_size; + + if (cache == NULL) { + cache = global_cache; + } + if (cache == NULL) { + return; + } + + if (key.length == 0) { + return; + } + + e = memcache_find(cache, n, key); + + if (e != NULL) { + memcache_element_parse(e, &cache_key, &cache_value); + + if (value.length <= cache_value.length) { + /* + * We can reuse the existing record + */ + memcpy(cache_value.data, value.data, value.length); + e->valuelength = value.length; + return; + } + + memcache_delete_element(cache, e); + } + + element_size = memcache_element_size(key.length, value.length); + + + e = (struct memcache_element *)SMB_MALLOC(element_size); + + if (e == NULL) { + DEBUG(0, ("malloc failed\n")); + return; + } + + e->n = n; + e->keylength = key.length; + e->valuelength = value.length; + + memcache_element_parse(e, &cache_key, &cache_value); + memcpy(cache_key.data, key.data, key.length); + memcpy(cache_value.data, value.data, value.length); + + parent = NULL; + p = &cache->tree.rb_node; + + while (*p) { + struct memcache_element *elem = memcache_node2elem(*p); + int cmp; + + parent = (*p); + + cmp = memcache_compare(elem, n, key); + + p = (cmp < 0) ? &(*p)->rb_left : &(*p)->rb_right; + } + + rb_link_node(&e->rb_node, parent, p); + rb_insert_color(&e->rb_node, &cache->tree); + + DLIST_ADD(cache->mru, e); + if (cache->lru == NULL) { + cache->lru = e; + } + + cache->size += element_size; + memcache_trim(cache); +} + +void memcache_add_talloc(struct memcache *cache, enum memcache_number n, + DATA_BLOB key, void *ptr) +{ + memcache_add(cache, n, key, data_blob_const(&ptr, sizeof(ptr))); +} + +void memcache_flush(struct memcache *cache, enum memcache_number n) +{ + struct rb_node *node; + + if (cache == NULL) { + cache = global_cache; + } + if (cache == NULL) { + return; + } + + /* + * Find the smallest element of number n + */ + + node = cache->tree.rb_node; + if (node == NULL) { + return; + } + + while (true) { + struct memcache_element *elem = memcache_node2elem(node); + struct rb_node *next; + + if ((int)elem->n < (int)n) { + next = node->rb_right; + } + else { + next = node->rb_left; + } + if (next == NULL) { + break; + } + node = next; + } + + node = rb_next(node); + if (node == NULL) { + return; + } + + while (node != NULL) { + struct memcache_element *e = memcache_node2elem(node); + struct rb_node *next = rb_next(node); + + memcache_delete_element(cache, e); + node = next; + } +} diff --git a/source3/lib/messages_ctdbd.c b/source3/lib/messages_ctdbd.c index 6e9b934a75..f1a02e6af9 100644 --- a/source3/lib/messages_ctdbd.c +++ b/source3/lib/messages_ctdbd.c @@ -22,6 +22,10 @@ #ifdef CLUSTER_SUPPORT #include "librpc/gen_ndr/messaging.h" +#include "ctdb.h" +#include "ctdb_private.h" +#include "ctdbd_conn.h" + struct messaging_ctdbd_context { struct ctdbd_connection *conn; diff --git a/source3/lib/module.c b/source3/lib/module.c index fa06d14b49..285bd9c4c0 100644 --- a/source3/lib/module.c +++ b/source3/lib/module.c @@ -57,6 +57,7 @@ static NTSTATUS do_smb_load_module(const char *module_name, bool is_probe) if (error) { DEBUG(0, ("Error trying to resolve symbol 'init_module' in %s: %s\n", module_name, error)); + sys_dlclose(handle); return NT_STATUS_UNSUCCESSFUL; } @@ -66,6 +67,7 @@ static NTSTATUS do_smb_load_module(const char *module_name, bool is_probe) if (!NT_STATUS_IS_OK(status)) { DEBUG(0, ("Module '%s' initialization failed: %s\n", module_name, get_friendly_nt_error_msg(status))); + sys_dlclose(handle); } return status; diff --git a/source3/lib/ms_fnmatch.c b/source3/lib/ms_fnmatch.c index a839b42588..8b69f1c2d2 100644 --- a/source3/lib/ms_fnmatch.c +++ b/source3/lib/ms_fnmatch.c @@ -152,6 +152,8 @@ int ms_fnmatch(const char *pattern, const char *string, bool translate_pattern, smb_ucs2_t *s = NULL; int ret, count, i; struct max_n *max_n = NULL; + struct max_n *max_n_free = NULL; + struct max_n one_max_n; if (ISDOTDOT(string)) { string = "."; @@ -201,17 +203,27 @@ int ms_fnmatch(const char *pattern, const char *string, bool translate_pattern, } if (count != 0) { - max_n = SMB_CALLOC_ARRAY(struct max_n, count); - if (!max_n) { - SAFE_FREE(p); - SAFE_FREE(s); - return -1; + if (count == 1) { + /* + * We're doing this a LOT, so save the effort to allocate + */ + ZERO_STRUCT(one_max_n); + max_n = &one_max_n; + } + else { + max_n = SMB_CALLOC_ARRAY(struct max_n, count); + if (!max_n) { + SAFE_FREE(p); + SAFE_FREE(s); + return -1; + } + max_n_free = max_n; } } ret = ms_fnmatch_core(p, s, max_n, strrchr_w(s, UCS2_CHAR('.')), is_case_sensitive); - SAFE_FREE(max_n); + SAFE_FREE(max_n_free); SAFE_FREE(p); SAFE_FREE(s); return ret; diff --git a/source3/lib/netapi/examples/Makefile.in b/source3/lib/netapi/examples/Makefile.in new file mode 100644 index 0000000000..86e1b1bc2f --- /dev/null +++ b/source3/lib/netapi/examples/Makefile.in @@ -0,0 +1,67 @@ +GTK_FLAGS=`pkg-config gtk+-2.0 --cflags` +GTK_LIBS=`pkg-config gtk+-2.0 --libs` + +KRB5LIBS=@KRB5_LIBS@ +LDAP_LIBS=@LDAP_LIBS@ +LIBS=@LIBS@ -lnetapi +DEVELOPER_CFLAGS=@DEVELOPER_CFLAGS@ +FLAGS=-I../ -L../../../bin @CFLAGS@ $(GTK_FLAGS) +CC=@CC@ +LDFLAGS=@PIE_LDFLAGS@ @LDFLAGS@ +DYNEXP=@DYNEXP@ + +# Compile a source file. +COMPILE_CC = $(CC) -I. $(FLAGS) $(PICFLAG) -c $< -o $@ +COMPILE = $(COMPILE_CC) + +BINARY_PREREQS = proto_exists bin/.dummy + +MAKEDIR = || exec false; \ + if test -d "$$dir"; then :; else \ + echo mkdir "$$dir"; \ + mkdir -p "$$dir" >/dev/null 2>&1 || \ + test -d "$$dir" || \ + mkdir "$$dir" || \ + exec false; fi || exec false + +.c.o: + @if (: >> $@ || : > $@) >/dev/null 2>&1; then rm -f $@; else \ + dir=`echo $@ | sed 's,/[^/]*$$,,;s,^$$,.,'` $(MAKEDIR); fi + @echo Compiling $*.c + @$(COMPILE) && exit 0;\ + echo "The following command failed:" 1>&2;\ + echo "$(COMPILE_CC)" 1>&2;\ + $(COMPILE_CC) >/dev/null 2>&1 + +GETDC_OBJ = getdc/getdc.o +NETDOMJOIN_OBJ = netdomjoin/netdomjoin.o +NETDOMJOIN_GUI_OBJ = netdomjoin-gui/netdomjoin-gui.o +GETJOINABLEOUS_OBJ = getjoinableous/getjoinableous.o + +PROGS = bin/getdc@EXEEXT@ \ + bin/netdomjoin@EXEEXT@ \ + bin/netdomjoin-gui@EXEEXT@ \ + bin/getjoinableous@EXEEXT@ + +all: $(PROGS) + +bin/getdc@EXEEXT@: $(GETDC_OBJ) + @echo Linking $@ + @$(CC) $(FLAGS) -o $@ $(GETDC_OBJ) $(LDFLAGS) $(DYNEXP) $(LIBS) $(KRB5LIBS) $(LDAP_LIBS) + +bin/getjoinableous@EXEEXT@: $(GETJOINABLEOUS_OBJ) + @echo Linking $@ + @$(CC) $(FLAGS) -o $@ $(GETJOINABLEOUS_OBJ) $(LDFLAGS) $(DYNEXP) $(LIBS) $(KRB5LIBS) $(LDAP_LIBS) + +bin/netdomjoin@EXEEXT@: $(NETDOMJOIN_OBJ) + @echo Linking $@ + @$(CC) $(FLAGS) -o $@ $(NETDOMJOIN_OBJ) $(LDFLAGS) $(DYNEXP) $(LIBS) $(KRB5LIBS) $(LDAP_LIBS) + +bin/netdomjoin-gui@EXEEXT@: $(NETDOMJOIN_GUI_OBJ) + @echo Linking $@ + @$(CC) $(FLAGS) $(GTK_FLAGS) -o $@ $(NETDOMJOIN_GUI_OBJ) $(LDFLAGS) $(DYNEXP) $(LIBS) $(KRB5LIBS) $(LDAP_LIBS) $(GTK_LIBS) + +clean: + -rm -f $(PROGS) + -rm -f core */*~ *~ \ + */*.o */*/*.o */*/*/*.o \ diff --git a/source3/lib/netapi/examples/getdc/getdc.c b/source3/lib/netapi/examples/getdc/getdc.c new file mode 100644 index 0000000000..272ba1088e --- /dev/null +++ b/source3/lib/netapi/examples/getdc/getdc.c @@ -0,0 +1,57 @@ +/* + * Unix SMB/CIFS implementation. + * GetDCName query + * Copyright (C) Guenther Deschner 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 + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <sys/types.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <netapi.h> + +int main(int argc, char **argv) +{ + NET_API_STATUS status; + struct libnetapi_ctx *ctx = NULL; + uint8_t *buffer = NULL; + + if (argc < 3) { + printf("usage: getdc <hostname> <domain>\n"); + return -1; + } + + status = libnetapi_init(&ctx); + if (status != 0) { + return status; + } + + libnetapi_set_username(ctx, ""); + libnetapi_set_password(ctx, ""); + + status = NetGetDCName(argv[1], argv[2], &buffer); + if (status != 0) { + printf("GetDcName failed with: %s\n", libnetapi_errstr(status)); + } else { + printf("%s\n", (char *)buffer); + } + NetApiBufferFree(buffer); + libnetapi_free(ctx); + + return status; +} diff --git a/source3/lib/netapi/examples/getjoinableous/getjoinableous.c b/source3/lib/netapi/examples/getjoinableous/getjoinableous.c new file mode 100644 index 0000000000..5a3366c9dc --- /dev/null +++ b/source3/lib/netapi/examples/getjoinableous/getjoinableous.c @@ -0,0 +1,104 @@ +/* + * Unix SMB/CIFS implementation. + * Join Support (cmdline + netapi) + * Copyright (C) Guenther Deschner 2008 + * + * 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 + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <string.h> +#include <stdio.h> + +#include <netapi.h> + +char *get_string_param(const char *param) +{ + char *p; + + p = strchr(param, '='); + if (!p) { + return NULL; + } + + return (p+1); +} + +int main(int argc, char **argv) +{ + NET_API_STATUS status; + const char *server_name = NULL; + const char *domain_name = NULL; + const char *account = NULL; + const char *password = NULL; + const char **ous = NULL; + uint32_t num_ous = 0; + struct libnetapi_ctx *ctx = NULL; + int i; + + status = libnetapi_init(&ctx); + if (status != 0) { + return status; + } + + if (argc < 2) { + printf("usage: getjoinableous\n"); + printf("\t<hostname> [domain=DOMAIN] <user=USER> <password=PASSWORD>\n"); + return 0; + } + + if (argc > 2) { + server_name = argv[1]; + } + + for (i=0; i<argc; i++) { + if (strncasecmp(argv[i], "domain", strlen("domain"))== 0) { + domain_name = get_string_param(argv[i]); + } + if (strncasecmp(argv[i], "user", strlen("user"))== 0) { + account = get_string_param(argv[i]); + libnetapi_set_username(ctx, account); + } + if (strncasecmp(argv[i], "password", strlen("password"))== 0) { + password = get_string_param(argv[i]); + libnetapi_set_password(ctx, password); + } + if (strncasecmp(argv[i], "debug", strlen("debug"))== 0) { + const char *str = NULL; + str = get_string_param(argv[i]); + libnetapi_set_debuglevel(ctx, str); + } + } + + status = NetGetJoinableOUs(server_name, + domain_name, + account, + password, + &num_ous, + &ous); + if (status != 0) { + printf("failed with: %s\n", + libnetapi_get_error_string(ctx, status)); + } else { + printf("Successfully queried joinable ous:\n"); + for (i=0; i<num_ous; i++) { + printf("ou: %s\n", ous[i]); + } + } + + NetApiBufferFree(ous); + + libnetapi_free(ctx); + + return status; +} diff --git a/source3/lib/netapi/examples/netdomjoin-gui/logo-small.png b/source3/lib/netapi/examples/netdomjoin-gui/logo-small.png Binary files differnew file mode 100644 index 0000000000..f041198002 --- /dev/null +++ b/source3/lib/netapi/examples/netdomjoin-gui/logo-small.png diff --git a/source3/lib/netapi/examples/netdomjoin-gui/logo.png b/source3/lib/netapi/examples/netdomjoin-gui/logo.png Binary files differnew file mode 100644 index 0000000000..6df4ace659 --- /dev/null +++ b/source3/lib/netapi/examples/netdomjoin-gui/logo.png diff --git a/source3/lib/netapi/examples/netdomjoin-gui/netdomjoin-gui.c b/source3/lib/netapi/examples/netdomjoin-gui/netdomjoin-gui.c new file mode 100644 index 0000000000..73b14d4d87 --- /dev/null +++ b/source3/lib/netapi/examples/netdomjoin-gui/netdomjoin-gui.c @@ -0,0 +1,1408 @@ +/* + * Unix SMB/CIFS implementation. + * Join Support (gtk + netapi) + * Copyright (C) Guenther Deschner 2007-2008 + * + * 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 + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#define _GNU_SOURCE +#include <stdio.h> +#include <sys/types.h> +#include <inttypes.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <netdb.h> + +#include <gtk/gtk.h> +#include <glib/gprintf.h> + +#include <netapi.h> + +#define MAX_CRED_LEN 256 +#define MAX_NETBIOS_NAME_LEN 15 + +#define SAMBA_ICON_PATH "/usr/share/pixmaps/samba/samba.ico" +#define SAMBA_IMAGE_PATH "/usr/share/pixmaps/samba/logo.png" +#define SAMBA_IMAGE_PATH_SMALL "/usr/share/pixmaps/samba/logo-small.png" + +#define WKSSVC_JOIN_FLAGS_DOMAIN_JOIN_IF_JOINED ( 0x00000020 ) +#define WKSSVC_JOIN_FLAGS_ACCOUNT_DELETE ( 0x00000004 ) +#define WKSSVC_JOIN_FLAGS_ACCOUNT_CREATE ( 0x00000002 ) +#define WKSSVC_JOIN_FLAGS_JOIN_TYPE ( 0x00000001 ) + +#define NetSetupWorkgroupName ( 2 ) +#define NetSetupDomainName ( 3 ) + +#define SAFE_FREE(x) do { if ((x) != NULL) {free(x); x=NULL;} } while(0) + +struct srvsvc_NetSrvInfo1005 { + const char *comment;/* [unique,charset(UTF16)] */ +}; + +static gboolean verbose = FALSE; + +typedef struct join_state { + struct libnetapi_ctx *ctx; + GtkWidget *window_main; + GtkWidget *window_parent; + GtkWidget *window_do_change; + GtkWidget *window_creds_prompt; + GtkWidget *entry_account; + GtkWidget *entry_password; + GtkWidget *entry_domain; + GtkWidget *entry_workgroup; + GtkWidget *button_ok; + GtkWidget *button_apply; + GtkWidget *button_ok_creds; + GtkWidget *label_reboot; + GtkWidget *label_current_name_buffer; + GtkWidget *label_current_name_type; + GtkWidget *label_full_computer_name; + uint16_t name_type_initial; + uint16_t name_type_new; + char *name_buffer_initial; + char *name_buffer_new; + char *password; + char *account; + char *comment; + char *comment_new; + char *my_fqdn; + char *my_dnsdomain; + char *my_hostname; + uint16_t server_role; + gboolean settings_changed; + gboolean hostname_changed; +} join_state; + +static void debug(const char *format, ...) +{ + va_list args; + + if (!verbose) { + return; + } + + va_start(args, format); + g_vprintf(format, args); + va_end(args); +} + +static gboolean callback_delete_event(GtkWidget *widget, + GdkEvent *event, + gpointer data) +{ + gtk_main_quit(); + return FALSE; +} + +static void callback_do_close(GtkWidget *widget, + gpointer data) +{ + debug("Closing now...\n"); + gtk_widget_destroy(data); +} + +static void free_join_state(struct join_state *s) +{ + SAFE_FREE(s->name_buffer_initial); + SAFE_FREE(s->name_buffer_new); + SAFE_FREE(s->password); + SAFE_FREE(s->account); + SAFE_FREE(s->comment); + SAFE_FREE(s->comment_new); + SAFE_FREE(s->my_fqdn); + SAFE_FREE(s->my_dnsdomain); + SAFE_FREE(s->my_hostname); +} + +static void do_cleanup(struct join_state *state) +{ + libnetapi_free(state->ctx); + free_join_state(state); +} + +static void callback_apply_description_change(GtkWidget *widget, + gpointer data) +{ + struct join_state *state = (struct join_state *)data; + NET_API_STATUS status = 0; + uint32_t parm_err = 0; + struct srvsvc_NetSrvInfo1005 info1005; + GtkWidget *dialog; + + info1005.comment = state->comment_new; + + status = NetServerSetInfo(NULL, 1005, (uint8_t *)&info1005, &parm_err); + if (status) { + debug("NetServerSetInfo failed with: %s\n", + libnetapi_errstr(status)); + dialog = gtk_message_dialog_new(GTK_WINDOW(state->window_main), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + "Failed to change computer description: %s.", + libnetapi_errstr(status)); + g_signal_connect_swapped(dialog, "response", + G_CALLBACK(gtk_widget_destroy), + dialog); + + gtk_widget_show(dialog); + return; + } + + gtk_widget_set_sensitive(GTK_WIDGET(state->button_apply), FALSE); +} + +static void callback_do_exit(GtkWidget *widget, + gpointer data) +{ + GtkWidget *dialog; + gint result; + struct join_state *state = (struct join_state *)data; + + if (!state->settings_changed) { + callback_delete_event(NULL, NULL, NULL); + return; + } + + dialog = gtk_message_dialog_new(GTK_WINDOW(state->window_main), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_QUESTION, + GTK_BUTTONS_YES_NO, + "You must restart your computer before the new settings will take effect."); + result = gtk_dialog_run(GTK_DIALOG(dialog)); + switch (result) { + case GTK_RESPONSE_YES: + g_print("would reboot here\n"); + break; + case GTK_RESPONSE_NO: + default: + break; + } + gtk_widget_destroy(dialog); + gtk_widget_destroy(state->window_main); + do_cleanup(state); + exit(0); +} + + +static void callback_do_reboot(GtkWidget *widget, + gpointer data, + gpointer data2) +{ + GtkWidget *dialog; + struct join_state *state = (struct join_state *)data2; + + debug("callback_do_reboot\n"); + + state->settings_changed = TRUE; + dialog = gtk_message_dialog_new(GTK_WINDOW(data), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_INFO, + GTK_BUTTONS_OK, + "You must restart this computer for the changes to take effect."); +#if 0 + g_signal_connect_swapped(dialog, "response", + G_CALLBACK(gtk_widget_destroy), + dialog); + + debug("showing dialog\n"); + gtk_widget_show(dialog); +#else + gtk_dialog_run(GTK_DIALOG(dialog)); + gtk_widget_destroy(dialog); +#endif + + gtk_label_set_text(GTK_LABEL(state->label_reboot), + "Changes will take effect after you restart this computer"); + + debug("destroying do_change window\n"); + gtk_widget_destroy(GTK_WIDGET(state->window_do_change)); + + { + uint32_t status; + const char *buffer; + uint16_t type; + + status = NetGetJoinInformation(NULL, &buffer, &type); + if (status != 0) { + g_print("failed to query status\n"); + return; + } + + debug("got new status: %s\n", buffer); +#if 0 + SAFE_FREE(state->name_buffer_new); + state->name_buffer_new = strdup(buffer); + SAFE_FREE(buffer); + state->name_type_new = type; +#endif + NetApiBufferFree((void *)buffer); + + gtk_label_set_text(GTK_LABEL(state->label_current_name_buffer), + state->name_buffer_new); + if (state->name_type_new == NetSetupDomainName) { + gtk_label_set_text(GTK_LABEL(state->label_current_name_type), + "Domain:"); + } else { + gtk_label_set_text(GTK_LABEL(state->label_current_name_type), + "Workgroup:"); + } + } +} + +static void callback_return_username(GtkWidget *widget, + gpointer data) +{ + const gchar *entry_text; + struct join_state *state = (struct join_state *)data; + if (!widget) { + return; + } + entry_text = gtk_entry_get_text(GTK_ENTRY(widget)); + debug("callback_return_username: %s\n", entry_text); + SAFE_FREE(state->account); + state->account = strdup(entry_text); +} + +static void callback_return_username_and_enter(GtkWidget *widget, + gpointer data) +{ + const gchar *entry_text; + struct join_state *state = (struct join_state *)data; + if (!widget) { + return; + } + entry_text = gtk_entry_get_text(GTK_ENTRY(widget)); + debug("callback_return_username: %s\n", entry_text); + SAFE_FREE(state->account); + state->account = strdup(entry_text); + g_signal_emit_by_name(state->button_ok_creds, "clicked"); +} + +static void callback_return_password(GtkWidget *widget, + gpointer data) +{ + const gchar *entry_text; + struct join_state *state = (struct join_state *)data; + if (!widget) { + return; + } + entry_text = gtk_entry_get_text(GTK_ENTRY(widget)); +#ifdef DEBUG_PASSWORD + debug("callback_return_password: %s\n", entry_text); +#else + debug("callback_return_password: (not printed)\n"); +#endif + SAFE_FREE(state->password); + state->password = strdup(entry_text); +} + +static void callback_return_password_and_enter(GtkWidget *widget, + gpointer data) +{ + const gchar *entry_text; + struct join_state *state = (struct join_state *)data; + if (!widget) { + return; + } + entry_text = gtk_entry_get_text(GTK_ENTRY(widget)); +#ifdef DEBUG_PASSWORD + debug("callback_return_password: %s\n", entry_text); +#else + debug("callback_return_password: (not printed)\n"); +#endif + SAFE_FREE(state->password); + state->password = strdup(entry_text); + g_signal_emit_by_name(state->button_ok_creds, "clicked"); +} + +static void callback_do_hostname_change(GtkWidget *widget, + gpointer data) +{ + GtkWidget *dialog; + const char *str = NULL; + + struct join_state *state = (struct join_state *)data; + + switch (state->name_type_initial) { + case NetSetupDomainName: + str = "To be implemented: call NetRenameMachineInDomain\n"; + break; + case NetSetupWorkgroupName: + str = "To be implemented: call SetComputerNameEx\n"; + break; + default: + break; + } + + dialog = gtk_message_dialog_new(GTK_WINDOW(state->window_parent), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_CLOSE, + str); + + g_signal_connect_swapped(dialog, "response", + G_CALLBACK(gtk_widget_destroy), + dialog); + gtk_widget_show(dialog); +} + +static void callback_do_join(GtkWidget *widget, + gpointer data) +{ + GtkWidget *dialog; + + NET_API_STATUS status; + const char *err_str = NULL; + uint32_t join_flags = 0; + uint32_t unjoin_flags = 0; + gboolean domain_join = FALSE; + gboolean try_unjoin = FALSE; + const char *new_workgroup_type = NULL; + const char *initial_workgroup_type = NULL; + + struct join_state *state = (struct join_state *)data; + + callback_return_username(state->entry_account, state); + callback_return_password(state->entry_password, state); + + if (state->window_creds_prompt) { + gtk_widget_destroy(GTK_WIDGET(state->window_creds_prompt)); + } + + switch (state->name_type_initial) { + case NetSetupWorkgroupName: + initial_workgroup_type = "workgroup"; + break; + case NetSetupDomainName: + initial_workgroup_type = "domain"; + break; + default: + break; + } + + switch (state->name_type_new) { + case NetSetupWorkgroupName: + new_workgroup_type = "workgroup"; + break; + case NetSetupDomainName: + new_workgroup_type = "domain"; + break; + default: + break; + } + + if (state->name_type_new == NetSetupDomainName) { + domain_join = TRUE; + join_flags = WKSSVC_JOIN_FLAGS_JOIN_TYPE | + WKSSVC_JOIN_FLAGS_ACCOUNT_CREATE | + WKSSVC_JOIN_FLAGS_DOMAIN_JOIN_IF_JOINED; /* for testing */ + } + + if ((state->name_type_initial == NetSetupDomainName) && + (state->name_type_new == NetSetupWorkgroupName)) { + try_unjoin = TRUE; + unjoin_flags = WKSSVC_JOIN_FLAGS_JOIN_TYPE | + WKSSVC_JOIN_FLAGS_ACCOUNT_DELETE; + } + + debug("callback_do_join: Joining a %s named %s using join_flags 0x%08x ", + new_workgroup_type, + state->name_buffer_new, + join_flags); + if (domain_join) { + debug("as %s ", state->account); +#ifdef DEBUG_PASSWORD + debug("with %s ", state->password); +#endif + } + debug("\n"); + if (try_unjoin) { + + debug("callback_do_join: Unjoining\n"); + + status = NetUnjoinDomain(NULL, + state->account, + state->password, + unjoin_flags); + if (status != 0) { + err_str = libnetapi_get_error_string(state->ctx, status); + g_print("callback_do_join: failed to unjoin (%s)\n", + err_str); + + dialog = gtk_message_dialog_new(GTK_WINDOW(state->window_parent), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_CLOSE, + "The following error occured attempting to unjoin the %s: \"%s\": %s", + initial_workgroup_type, + state->name_buffer_initial, + err_str); + gtk_dialog_run(GTK_DIALOG(dialog)); + gtk_widget_destroy(dialog); + } + + } + status = NetJoinDomain(NULL, + state->name_buffer_new, + NULL, + state->account, + state->password, + join_flags); + if (status != 0) { + err_str = libnetapi_get_error_string(state->ctx, status); + g_print("callback_do_join: failed to join (%s)\n", err_str); + + dialog = gtk_message_dialog_new(GTK_WINDOW(state->window_parent), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_CLOSE, + "The following error occured attempting to join the %s: \"%s\": %s", + new_workgroup_type, + state->name_buffer_new, + err_str); + + g_signal_connect_swapped(dialog, "response", + G_CALLBACK(gtk_widget_destroy), + dialog); + + gtk_widget_show(dialog); + + return; + } + + debug("callback_do_join: Successfully joined %s\n", + new_workgroup_type); + + dialog = gtk_message_dialog_new(GTK_WINDOW(state->window_parent), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_INFO, + GTK_BUTTONS_OK, + "Welcome to the %s %s.", + state->name_buffer_new, + new_workgroup_type); + + gtk_dialog_run(GTK_DIALOG(dialog)); + gtk_widget_destroy(dialog); + + callback_do_reboot(NULL, state->window_parent, state); +} + +static void callback_creds_prompt(GtkWidget *widget, + gpointer data) +{ + GtkWidget *window; + GtkWidget *box1; + GtkWidget *bbox; + GtkWidget *button; + GtkWidget *label; + + struct join_state *state = (struct join_state *)data; + + debug("callback_creds_prompt:\n"); + + state->window_parent = state->window_do_change; + + if (state->hostname_changed) { + return callback_do_hostname_change(NULL, state); + } + + if ((state->name_type_initial != NetSetupDomainName) && + (state->name_type_new != NetSetupDomainName)) { + return callback_do_join(NULL, state); + } + + window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + + gtk_window_set_title(GTK_WINDOW(window), "Computer Name Changes"); + gtk_window_set_resizable(GTK_WINDOW(window), FALSE); + gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); + gtk_widget_set_size_request(GTK_WIDGET(window), 380, 280); + gtk_window_set_icon_from_file(GTK_WINDOW(window), SAMBA_ICON_PATH, NULL); +/* gtk_window_set_icon_name(GTK_WIDGET(window), GTK_STOCK_DIALOG_AUTHENTICATION); */ + state->window_creds_prompt = window; + + g_signal_connect(G_OBJECT(window), "delete_event", + G_CALLBACK(callback_do_close), window); + + gtk_container_set_border_width(GTK_CONTAINER(window), 10); + + box1 = gtk_vbox_new(FALSE, 0); + + gtk_container_add(GTK_CONTAINER(window), box1); + + if ((state->name_type_initial == NetSetupDomainName) && + (state->name_type_new == NetSetupWorkgroupName)) { + label = gtk_label_new("Enter the name and password of an account with permission to leave the domain.\n"); + } else { + label = gtk_label_new("Enter the name and password of an account with permission to join the domain.\n"); + } + gtk_misc_set_alignment(GTK_MISC(label), 0, 0); + gtk_label_set_line_wrap(GTK_LABEL(label), TRUE); + + gtk_box_pack_start(GTK_BOX(box1), label, FALSE, FALSE, 0); + + gtk_widget_show(label); + + /* USER NAME */ + label = gtk_label_new("User name:"); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0); + gtk_box_pack_start(GTK_BOX(box1), label, FALSE, FALSE, 0); + gtk_widget_show(label); + + state->entry_account = gtk_entry_new(); + gtk_entry_set_max_length(GTK_ENTRY(state->entry_account), MAX_CRED_LEN); + g_signal_connect(G_OBJECT(state->entry_account), "activate", + G_CALLBACK(callback_return_username_and_enter), + (gpointer)state); + gtk_editable_select_region(GTK_EDITABLE(state->entry_account), + 0, GTK_ENTRY(state->entry_account)->text_length); + gtk_box_pack_start(GTK_BOX(box1), state->entry_account, TRUE, TRUE, 0); + gtk_widget_show(state->entry_account); + + /* PASSWORD */ + label = gtk_label_new("Password:"); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0); + gtk_box_pack_start(GTK_BOX(box1), label, FALSE, FALSE, 0); + gtk_widget_show(label); + + state->entry_password = gtk_entry_new(); + gtk_entry_set_max_length(GTK_ENTRY(state->entry_password), MAX_CRED_LEN); + gtk_entry_set_visibility(GTK_ENTRY(state->entry_password), FALSE); + g_signal_connect(G_OBJECT(state->entry_password), "activate", + G_CALLBACK(callback_return_password_and_enter), + (gpointer)state); + gtk_editable_set_editable(GTK_EDITABLE(state->entry_password), TRUE); + gtk_editable_select_region(GTK_EDITABLE(state->entry_password), + 0, GTK_ENTRY(state->entry_password)->text_length); + gtk_box_pack_start(GTK_BOX(box1), state->entry_password, TRUE, TRUE, 0); + gtk_widget_show(state->entry_password); + + bbox = gtk_hbutton_box_new(); + gtk_container_set_border_width(GTK_CONTAINER(bbox), 5); + gtk_container_add(GTK_CONTAINER(box1), bbox); + gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END); + gtk_box_set_spacing(GTK_BOX(bbox), 10); + + state->button_ok_creds = gtk_button_new_from_stock(GTK_STOCK_OK); + gtk_widget_grab_focus(GTK_WIDGET(state->button_ok_creds)); + gtk_container_add(GTK_CONTAINER(bbox), state->button_ok_creds); + g_signal_connect(G_OBJECT(state->button_ok_creds), "clicked", + G_CALLBACK(callback_do_join), + (gpointer)state); + gtk_widget_show(state->button_ok_creds); + + button = gtk_button_new_from_stock(GTK_STOCK_CANCEL); + gtk_container_add(GTK_CONTAINER(bbox), button); + g_signal_connect(G_OBJECT(button), "clicked", + G_CALLBACK(callback_do_close), (gpointer) window); + gtk_widget_show_all(window); +} + +static void callback_enter_hostname_and_unlock(GtkWidget *widget, + gpointer data) +{ + const gchar *entry_text = NULL; + char *str = NULL; + struct join_state *state = (struct join_state *)data; + + entry_text = gtk_entry_get_text(GTK_ENTRY(widget)); + debug("callback_enter_hostname_and_unlock: %s\n", entry_text); + if (!entry_text || entry_text[0] == 0) { + state->hostname_changed = FALSE; + gtk_widget_set_sensitive(GTK_WIDGET(state->button_ok), FALSE); + return; + } + if (strcasecmp(state->my_hostname, entry_text) == 0) { + state->hostname_changed = FALSE; + gtk_widget_set_sensitive(GTK_WIDGET(state->button_ok), FALSE); + return; + } + state->hostname_changed = TRUE; + if (state->name_type_initial == NetSetupDomainName) { + asprintf(&str, "%s.%s", entry_text, state->my_dnsdomain); + } else { + asprintf(&str, "%s.", entry_text); + } + gtk_label_set_text(GTK_LABEL(state->label_full_computer_name), str); + free(str); + + if (state->hostname_changed && str && str[0] != 0 && str[0] != '.') { + gtk_widget_set_sensitive(GTK_WIDGET(state->button_ok), TRUE); + } +} + +static void callback_enter_computer_description_and_unlock(GtkWidget *widget, + gpointer data) +{ + const gchar *entry_text = NULL; + struct join_state *state = (struct join_state *)data; + int string_unchanged = 0; + + entry_text = gtk_entry_get_text(GTK_ENTRY(widget)); + debug("callback_enter_computer_description_and_unlock: %s\n", + entry_text); +#if 0 + if (!entry_text || entry_text[0] == 0) { + string_unchanged = 1; + gtk_widget_set_sensitive(GTK_WIDGET(state->button_apply), + FALSE); + return; + } +#endif + if (entry_text && strcasecmp(state->comment, entry_text) == 0) { + string_unchanged = 1; + gtk_widget_set_sensitive(GTK_WIDGET(state->button_apply), + FALSE); + return; + } + + gtk_widget_set_sensitive(GTK_WIDGET(state->button_apply), TRUE); + SAFE_FREE(state->comment_new); + state->comment_new = strdup(entry_text); + +} + + +static void callback_enter_workgroup_and_unlock(GtkWidget *widget, + gpointer data) +{ + const gchar *entry_text = NULL; + struct join_state *state = (struct join_state *)data; + + entry_text = gtk_entry_get_text(GTK_ENTRY(widget)); + debug("callback_enter_workgroup_and_unlock: %s\n", entry_text); + if (!entry_text || entry_text[0] == 0) { + gtk_widget_set_sensitive(GTK_WIDGET(state->button_ok), FALSE); + return; + } + if (strcasecmp(state->name_buffer_initial, entry_text) == 0) { + gtk_widget_set_sensitive(GTK_WIDGET(state->button_ok), FALSE); + return; + } + gtk_widget_set_sensitive(GTK_WIDGET(state->button_ok), TRUE); + SAFE_FREE(state->name_buffer_new); + state->name_buffer_new = strdup(entry_text); + state->name_type_new = NetSetupWorkgroupName; +} + +static void callback_enter_domain_and_unlock(GtkWidget *widget, + gpointer data) +{ + const gchar *entry_text = NULL; + struct join_state *state = (struct join_state *)data; + + entry_text = gtk_entry_get_text(GTK_ENTRY(widget)); + debug("callback_enter_domain_and_unlock: %s\n", entry_text); + if (!entry_text || entry_text[0] == 0) { + gtk_widget_set_sensitive(GTK_WIDGET(state->button_ok), FALSE); + return; + } + if (strcasecmp(state->name_buffer_initial, entry_text) == 0) { + gtk_widget_set_sensitive(GTK_WIDGET(state->button_ok), FALSE); + return; + } + gtk_widget_set_sensitive(GTK_WIDGET(state->button_ok), TRUE); + SAFE_FREE(state->name_buffer_new); + state->name_buffer_new = strdup(entry_text); + state->name_type_new = NetSetupDomainName; +} + +static void callback_continue(GtkWidget *widget, + gpointer data) +{ + struct join_state *state = (struct join_state *)data; + + gtk_widget_grab_focus(GTK_WIDGET(state->button_ok)); + g_signal_emit_by_name(state->button_ok, "clicked"); +} + +static void callback_apply_continue(GtkWidget *widget, + gpointer data) +{ + struct join_state *state = (struct join_state *)data; + + gtk_widget_grab_focus(GTK_WIDGET(state->button_apply)); + g_signal_emit_by_name(state->button_apply, "clicked"); +} + +static void callback_do_join_workgroup(GtkWidget *widget, + gpointer data) +{ + struct join_state *state = (struct join_state *)data; + debug("callback_do_join_workgroup choosen\n"); + gtk_widget_set_sensitive(GTK_WIDGET(state->entry_workgroup), TRUE); + gtk_widget_grab_focus(GTK_WIDGET(state->entry_workgroup)); + gtk_widget_set_sensitive(GTK_WIDGET(state->entry_domain), FALSE); + callback_enter_workgroup_and_unlock(state->entry_workgroup, state); /* TEST */ +} + +static void callback_do_join_domain(GtkWidget *widget, + gpointer data) +{ + struct join_state *state = (struct join_state *)data; + debug("callback_do_join_domain choosen\n"); + gtk_widget_set_sensitive(GTK_WIDGET(state->entry_domain), TRUE); + gtk_widget_grab_focus(GTK_WIDGET(state->entry_domain)); + gtk_widget_set_sensitive(GTK_WIDGET(state->entry_workgroup), FALSE); + callback_enter_domain_and_unlock(state->entry_domain, state); /* TEST */ +} + +static void callback_do_change(GtkWidget *widget, + gpointer data) +{ + GtkWidget *window; + GtkWidget *box1; + GtkWidget *bbox; + GtkWidget *button_workgroup; + GtkWidget *button_domain; + GtkWidget *button; + GtkWidget *label; + GtkWidget *frame_horz; + GtkWidget *vbox; + GtkWidget *entry; + GSList *group; + + struct join_state *state = (struct join_state *)data; + + debug("callback_do_change called\n"); + +#if 0 + /* FIXME: add proper warnings for Samba as a DC */ + if (state->server_role == 3) { + GtkWidget *dialog; + dialog = gtk_message_dialog_new(GTK_WINDOW(state->window_main), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + "Domain controller cannot be moved from one domain to another, they must first be demoted. Renaming this domain controller may cause it to become temporarily unavailable to users and computers. For information on renaming domain controllers, including alternate renaming methods, see Help and Support. To continue renaming this domain controller, click OK."); + g_signal_connect_swapped(dialog, "response", + G_CALLBACK(gtk_widget_destroy), + dialog); + + gtk_widget_show(dialog); + return; + } +#endif + + state->button_ok = gtk_button_new_from_stock(GTK_STOCK_OK); + window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + + gtk_window_set_title(GTK_WINDOW(window), "Computer Name Changes"); + gtk_window_set_resizable(GTK_WINDOW(window), FALSE); + gtk_widget_set_size_request(GTK_WIDGET(window), 480, 500); + gtk_window_set_icon_from_file(GTK_WINDOW(window), SAMBA_ICON_PATH, NULL); + + g_signal_connect(G_OBJECT(window), "delete_event", + G_CALLBACK(callback_do_close), window); + + gtk_container_set_border_width(GTK_CONTAINER(window), 10); + + box1 = gtk_vbox_new(FALSE, 0); + gtk_container_add(GTK_CONTAINER(window), box1); + + label = gtk_label_new("You can change the name and membership of this computer. Changes may affect access to network ressources."); + gtk_label_set_line_wrap(GTK_LABEL(label), TRUE); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0); + gtk_box_pack_start(GTK_BOX(box1), label, TRUE, TRUE, 0); + gtk_widget_show(label); + + /* COMPUTER NAME */ + label = gtk_label_new("Computer name:"); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0); + gtk_box_pack_start(GTK_BOX(box1), label, TRUE, TRUE, 0); + gtk_widget_show(label); + + state->label_full_computer_name = gtk_label_new(NULL); + { + entry = gtk_entry_new(); + gtk_entry_set_max_length(GTK_ENTRY(entry), MAX_NETBIOS_NAME_LEN); + g_signal_connect(G_OBJECT(entry), "changed", + G_CALLBACK(callback_enter_hostname_and_unlock), + (gpointer)state); + gtk_entry_set_text(GTK_ENTRY(entry), state->my_hostname); + gtk_editable_select_region(GTK_EDITABLE(entry), + 0, GTK_ENTRY(entry)->text_length); + + gtk_editable_set_editable(GTK_EDITABLE(entry), TRUE); /* ! */ + gtk_box_pack_start(GTK_BOX(box1), entry, TRUE, TRUE, 0); + gtk_widget_show(entry); + } + + /* FULL COMPUTER NAME */ + label = gtk_label_new("Full computer name:"); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0); + gtk_box_pack_start(GTK_BOX(box1), label, TRUE, TRUE, 0); + gtk_widget_show(label); + + { + const gchar *entry_text; + char *str = NULL; + entry_text = gtk_entry_get_text(GTK_ENTRY(entry)); + if (state->name_type_initial == NetSetupDomainName) { + asprintf(&str, "%s.%s", entry_text, + state->my_dnsdomain); + } else { + asprintf(&str, "%s.", entry_text); + } + gtk_label_set_text(GTK_LABEL(state->label_full_computer_name), + str); + free(str); + gtk_misc_set_alignment(GTK_MISC(state->label_full_computer_name), 0, 0); + gtk_box_pack_start(GTK_BOX(box1), + state->label_full_computer_name, TRUE, TRUE, 0); + gtk_widget_show(state->label_full_computer_name); + } + + /* BOX */ + frame_horz = gtk_frame_new ("Member Of"); + gtk_box_pack_start(GTK_BOX(box1), frame_horz, TRUE, TRUE, 10); + + vbox = gtk_vbox_new(FALSE, 0); + gtk_container_set_border_width(GTK_CONTAINER(vbox), 10); + gtk_container_add(GTK_CONTAINER(frame_horz), vbox); + + /* TWO ENTRIES */ + state->entry_workgroup = gtk_entry_new(); + state->entry_domain = gtk_entry_new(); + + /* DOMAIN */ + button_domain = gtk_radio_button_new_with_label(NULL, "Domain"); + if (state->name_type_initial == NetSetupDomainName) { + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button_domain), TRUE); + } + gtk_box_pack_start(GTK_BOX(vbox), button_domain, TRUE, TRUE, 0); + g_signal_connect(G_OBJECT(button_domain), "clicked", + G_CALLBACK(callback_do_join_domain), + (gpointer)state); + + { + gtk_entry_set_max_length(GTK_ENTRY(state->entry_domain), 50); + g_signal_connect(G_OBJECT(state->entry_domain), "changed", + G_CALLBACK(callback_enter_domain_and_unlock), + (gpointer)state); + g_signal_connect(G_OBJECT(state->entry_domain), "activate", + G_CALLBACK(callback_continue), + (gpointer)state); + if (state->name_type_initial == NetSetupDomainName) { + gtk_entry_set_text(GTK_ENTRY(state->entry_domain), + state->name_buffer_initial); + gtk_widget_set_sensitive(state->entry_workgroup, FALSE); + gtk_widget_set_sensitive(state->entry_domain, TRUE); + } + gtk_editable_set_editable(GTK_EDITABLE(state->entry_domain), TRUE); + gtk_box_pack_start(GTK_BOX(vbox), state->entry_domain, TRUE, TRUE, 0); + gtk_widget_show(state->entry_domain); + } + gtk_widget_show(button_domain); + + /* WORKGROUP */ + group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(button_domain)); + button_workgroup = gtk_radio_button_new_with_label(group, "Workgroup"); + if (state->name_type_initial == NetSetupWorkgroupName) { + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button_workgroup), TRUE); + } + gtk_box_pack_start(GTK_BOX(vbox), button_workgroup, TRUE, TRUE, 0); + g_signal_connect(G_OBJECT(button_workgroup), "clicked", + G_CALLBACK(callback_do_join_workgroup), + (gpointer)state); + { + gtk_entry_set_max_length(GTK_ENTRY(state->entry_workgroup), + MAX_NETBIOS_NAME_LEN); + g_signal_connect(G_OBJECT(state->entry_workgroup), "changed", + G_CALLBACK(callback_enter_workgroup_and_unlock), + (gpointer)state); + g_signal_connect(G_OBJECT(state->entry_workgroup), "activate", + G_CALLBACK(callback_continue), + (gpointer)state); + + if (state->name_type_initial == NetSetupWorkgroupName) { + gtk_entry_set_text(GTK_ENTRY(state->entry_workgroup), + state->name_buffer_initial); + gtk_widget_set_sensitive(GTK_WIDGET(state->entry_domain), FALSE); + gtk_widget_set_sensitive(GTK_WIDGET(state->entry_workgroup), TRUE); + } + gtk_box_pack_start(GTK_BOX(vbox), state->entry_workgroup, TRUE, TRUE, 0); + gtk_widget_show(state->entry_workgroup); + } + gtk_widget_show(button_workgroup); + + /* BUTTONS */ + bbox = gtk_hbutton_box_new(); + gtk_container_set_border_width(GTK_CONTAINER(bbox), 5); + gtk_container_add(GTK_CONTAINER(box1), bbox); + gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END); + gtk_box_set_spacing(GTK_BOX(bbox), 10); + + state->window_do_change = window; + gtk_widget_set_sensitive(GTK_WIDGET(state->button_ok), FALSE); /* !!! */ + gtk_container_add(GTK_CONTAINER(bbox), state->button_ok); + g_signal_connect(G_OBJECT(state->button_ok), "clicked", + G_CALLBACK(callback_creds_prompt), + (gpointer)state); + + button = gtk_button_new_from_stock(GTK_STOCK_CANCEL); + gtk_container_add(GTK_CONTAINER(bbox), button); + g_signal_connect(G_OBJECT(button), "clicked", + G_CALLBACK(callback_do_close), + (gpointer)window); + + gtk_widget_show_all(window); + +} + +static void callback_do_about(GtkWidget *widget, + gpointer data) +{ + GdkPixbuf *logo; + GError *error = NULL; + + debug("callback_do_about called\n"); + + logo = gdk_pixbuf_new_from_file(SAMBA_IMAGE_PATH, + &error); + if (logo == NULL) { + g_print("failed to load logo from %s: %s\n", + SAMBA_IMAGE_PATH, error->message); + } + + gtk_show_about_dialog(data, + "name", "Samba", + "version", "3.2.0pre2-GIT-904a90-test", + "copyright", "Copyright Andrew Tridgell and the Samba Team 1992-2007", + "website", "http://www.samba.org", + "license", "GPLv3", + "logo", logo, + "comments", "Samba gtk domain join utility", + NULL); +} + +static int draw_main_window(struct join_state *state) +{ + GtkWidget *window; + GtkWidget *button; + GtkWidget *label; + GtkWidget *main_vbox; + GtkWidget *vbox; + GtkWidget *hbox; + GtkWidget *bbox; + GtkWidget *image; + GtkWidget *table; + GtkWidget *entry; + GdkPixbuf *icon; + GError *error = NULL; + + icon = gdk_pixbuf_new_from_file(SAMBA_ICON_PATH, + &error); + if (icon == NULL) { + g_print("failed to load icon from %s : %s\n", + SAMBA_ICON_PATH, error->message); + } + +#if 1 + image = gtk_image_new_from_file(SAMBA_IMAGE_PATH_SMALL); +#else + image = gtk_image_new_from_file("/usr/share/pixmaps/redhat-system_settings.png"); +#endif + if (image == NULL) { + g_print("failed to load logo from %s : %s\n", + SAMBA_IMAGE_PATH_SMALL, error->message); + } + + window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + state->window_main = window; + + gtk_window_set_title(GTK_WINDOW(window), "Samba - Join Domain dialogue"); + gtk_widget_set_size_request(GTK_WIDGET(window), 600, 600); + gtk_window_set_resizable(GTK_WINDOW(window), FALSE); + gtk_window_set_icon_from_file(GTK_WINDOW(window), SAMBA_ICON_PATH, NULL); + + g_signal_connect(G_OBJECT(window), "delete_event", + G_CALLBACK(callback_delete_event), NULL); + + gtk_container_set_border_width(GTK_CONTAINER(window), 10); + + main_vbox = gtk_vbox_new(FALSE, 10); + gtk_container_add(GTK_CONTAINER(window), main_vbox); + +#if 0 + gtk_box_pack_start(GTK_BOX(main_vbox), image, TRUE, TRUE, 10); + gtk_widget_show(image); +#endif + /* Hbox */ + hbox = gtk_hbox_new(FALSE, 10); + gtk_container_add(GTK_CONTAINER(main_vbox), hbox); + + { +/* gtk_box_pack_start(GTK_BOX(main_vbox), image, TRUE, TRUE, 10); */ +/* gtk_misc_set_alignment(GTK_MISC(image), 0, 0); */ + gtk_widget_set_size_request(GTK_WIDGET(image), 150, 40); + gtk_box_pack_start(GTK_BOX(hbox), image, FALSE, FALSE, 10); + gtk_widget_show(image); + + /* Label */ + label = gtk_label_new("Samba uses the following information to identify your computer on the network."); +/* gtk_misc_set_alignment(GTK_MISC(label), 0, 0); */ + gtk_widget_set_size_request(GTK_WIDGET(label), 400, 40); + gtk_label_set_line_wrap(GTK_LABEL(label), TRUE); + gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); + gtk_widget_show(label); + } + + gtk_widget_show(hbox); + + vbox = gtk_vbox_new(FALSE, 0); + gtk_container_set_border_width(GTK_CONTAINER(vbox), 10); + gtk_container_add(GTK_CONTAINER(main_vbox), vbox); + + /* Table */ + table = gtk_table_new(6, 3, TRUE); + gtk_table_set_row_spacings(GTK_TABLE(table), 5); + gtk_table_set_col_spacings(GTK_TABLE(table), 5); + gtk_container_add(GTK_CONTAINER(vbox), table); + + { + /* Label */ + label = gtk_label_new("Computer description:"); +/* gtk_misc_set_alignment(GTK_MISC(label), 0, 0); */ + gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 0, 1); + gtk_widget_show(label); + + state->button_apply = gtk_button_new_from_stock(GTK_STOCK_APPLY); + + /* Entry */ + entry = gtk_entry_new(); + gtk_entry_set_max_length(GTK_ENTRY(entry), 256); + g_signal_connect(G_OBJECT(entry), "changed", + G_CALLBACK(callback_enter_computer_description_and_unlock), + state); + g_signal_connect(G_OBJECT(entry), "activate", + G_CALLBACK(callback_apply_continue), + (gpointer)state); + + gtk_entry_set_text(GTK_ENTRY(entry), (char *)state->comment); + gtk_editable_set_editable(GTK_EDITABLE(entry), TRUE); /* ! */ + gtk_table_attach_defaults(GTK_TABLE(table), entry, 1, 3, 0, 1); + gtk_widget_show(entry); + } + + /* Label */ + label = gtk_label_new("For example: \"Samba \%v\"."); + gtk_label_set_line_wrap(GTK_LABEL(label), TRUE); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0); + gtk_table_attach_defaults(GTK_TABLE(table), label, 1, 3, 1, 2); + gtk_widget_show(label); + + /* Label */ + label = gtk_label_new("Full computer name:"); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0); + gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 2, 3); + gtk_widget_show(label); + + { + /* Label */ + char *str = NULL; + if (state->name_type_initial == NetSetupDomainName) { + asprintf(&str, "%s.%s", state->my_hostname, + state->my_dnsdomain); + } else { + asprintf(&str, "%s.", state->my_hostname); + } + + label = gtk_label_new(str); + SAFE_FREE(str); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0); + gtk_table_attach_defaults(GTK_TABLE(table), label, 1, 3, 2, 3); + gtk_widget_show(label); + } + + /* Label */ + if (state->name_type_initial == NetSetupDomainName) { + label = gtk_label_new("Domain:"); + } else { + label = gtk_label_new("Workgroup:"); + } + gtk_misc_set_alignment(GTK_MISC(label), 0, 0); + gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 3, 4); + gtk_widget_show(label); + state->label_current_name_type = label; + + /* Label */ + label = gtk_label_new(state->name_buffer_initial); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0); + gtk_table_attach_defaults(GTK_TABLE(table), label, 1, 3, 3, 4); + gtk_widget_show(label); + state->label_current_name_buffer = label; + + { + hbox = gtk_hbox_new(FALSE, 0); + gtk_container_add(GTK_CONTAINER(vbox), hbox); + label = gtk_label_new("To rename this computer or join a domain, click Change."); + gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); + + + } + + /* bbox */ + bbox = gtk_hbutton_box_new(); + gtk_container_set_border_width(GTK_CONTAINER(bbox), 5); + gtk_container_add(GTK_CONTAINER(hbox), bbox); + gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END); + gtk_box_set_spacing(GTK_BOX(bbox), 10); + + button = gtk_button_new_with_mnemonic("Ch_ange"); + g_signal_connect(G_OBJECT(button), "clicked", + G_CALLBACK(callback_do_change), + (gpointer)state); + gtk_box_pack_start(GTK_BOX(bbox), button, TRUE, TRUE, 0); + gtk_widget_show(button); + + /* Label (hidden) */ + state->label_reboot = gtk_label_new(NULL); + gtk_label_set_line_wrap(GTK_LABEL(state->label_reboot), TRUE); + gtk_misc_set_alignment(GTK_MISC(state->label_reboot), 0, 0); + gtk_box_pack_start(GTK_BOX(vbox), state->label_reboot, TRUE, TRUE, 0); + gtk_widget_show(state->label_reboot); + +#if 0 + gtk_box_pack_start(GTK_BOX(vbox), + create_bbox(window, TRUE, NULL, 10, 85, 20, GTK_BUTTONBOX_END), + TRUE, TRUE, 5); +#endif + { + + GtkWidget *frame; + GtkWidget *bbox2; + GtkWidget *button2; + + frame = gtk_frame_new(NULL); + bbox2 = gtk_hbutton_box_new(); + + gtk_container_set_border_width(GTK_CONTAINER(bbox2), 5); + gtk_container_add(GTK_CONTAINER(frame), bbox2); + + /* Set the appearance of the Button Box */ + gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox2), GTK_BUTTONBOX_END); + gtk_box_set_spacing(GTK_BOX(bbox2), 10); + /*gtk_button_box_set_child_size(GTK_BUTTON_BOX(bbox2), child_w, child_h);*/ + + button2 = gtk_button_new_from_stock(GTK_STOCK_OK); + gtk_container_add(GTK_CONTAINER(bbox2), button2); + g_signal_connect(G_OBJECT(button2), "clicked", G_CALLBACK(callback_do_exit), state); + + button2 = gtk_button_new_from_stock(GTK_STOCK_CANCEL); + gtk_container_add(GTK_CONTAINER(bbox2), button2); + g_signal_connect(G_OBJECT(button2), "clicked", + G_CALLBACK(callback_delete_event), + window); + + gtk_container_add(GTK_CONTAINER(bbox2), state->button_apply); + g_signal_connect(G_OBJECT(state->button_apply), "clicked", + G_CALLBACK(callback_apply_description_change), + state); + gtk_widget_set_sensitive(GTK_WIDGET(state->button_apply), FALSE); + + button2 = gtk_button_new_from_stock(GTK_STOCK_ABOUT); + gtk_container_add(GTK_CONTAINER(bbox2), button2); + g_signal_connect(G_OBJECT(button2), "clicked", + G_CALLBACK(callback_do_about), + window); + + gtk_box_pack_start(GTK_BOX(vbox), frame, TRUE, TRUE, 5); + } + + gtk_widget_show_all(window); + + return 0; +} + +static int init_join_state(struct join_state **state) +{ + struct join_state *s; + + s = malloc(sizeof(struct join_state)); + if (!s) { + return -1; + } + + memset(s, '\0', sizeof(struct join_state)); + + *state = s; + + return 0; +} + +static int initialize_join_state(struct join_state *state, + const char *debug_level) +{ + struct libnetapi_ctx *ctx = NULL; + NET_API_STATUS status = 0; + + status = libnetapi_init(&ctx); + if (status) { + return status; + } + + if (debug_level) { + libnetapi_set_debuglevel(ctx, debug_level); + } + + { + char my_hostname[HOST_NAME_MAX]; + const char *p = NULL; + struct hostent *hp = NULL; + + if (gethostname(my_hostname, sizeof(my_hostname)) == -1) { + return -1; + } + + p = strchr(my_hostname, '.'); + if (p) { + my_hostname[strlen(my_hostname)-strlen(p)] = '\0'; + } + state->my_hostname = strdup(my_hostname); + if (!state->my_hostname) { + return -1; + } + debug("state->my_hostname: %s\n", state->my_hostname); + + hp = gethostbyname(my_hostname); + if (!hp || !hp->h_name || !*hp->h_name) { + return -1; + } + + state->my_fqdn = strdup(hp->h_name); + if (!state->my_fqdn) { + return -1; + } + debug("state->my_fqdn: %s\n", state->my_fqdn); + + p = strchr(state->my_fqdn, '.'); + if (p) { + p++; + state->my_dnsdomain = strdup(p); + } else { + state->my_dnsdomain = strdup(""); + } + if (!state->my_dnsdomain) { + return -1; + } + debug("state->my_dnsdomain: %s\n", state->my_dnsdomain); + } + + { + const char *buffer = NULL; + uint16_t type = 0; + status = NetGetJoinInformation(NULL, &buffer, &type); + if (status != 0) { + printf("NetGetJoinInformation failed with: %s\n", + libnetapi_get_error_string(state->ctx, status)); + return status; + } + debug("NetGetJoinInformation gave: %s and %d\n", buffer, type); + state->name_buffer_initial = strdup(buffer); + if (!state->name_buffer_initial) { + return -1; + } + state->name_type_initial = type; + NetApiBufferFree((void *)buffer); + } + + { + struct srvsvc_NetSrvInfo1005 *info1005 = NULL; + uint8_t *buffer = NULL; + + status = NetServerGetInfo(NULL, 1005, &buffer); + if (status != 0) { + printf("NetServerGetInfo failed with: %s\n", + libnetapi_get_error_string(state->ctx, status)); + return status; + } + + info1005 = (struct srvsvc_NetSrvInfo1005 *)buffer; + + state->comment = strdup(info1005->comment); + if (!state->comment) { + return -1; + } + NetApiBufferFree(buffer); + } +#if 0 + { + struct srvsvc_NetSrvInfo100 *info100 = NULL; + uint8_t *buffer = NULL; + + status = NetServerGetInfo(NULL, 100, &buffer); + if (status) { + return status; + } + + info100 = (struct srvsvc_NetSrvInfo100 *)buffer; + + state->comment = strdup(info100->comment); + if (!state->comment) { + return -1; + } + } +#endif + + state->ctx = ctx; + + return 0; +} + +int main(int argc, char **argv) +{ + GOptionContext *context = NULL; + static const char *debug_level = NULL; + struct join_state *state = NULL; + GError *error = NULL; + int ret = 0; + + static GOptionEntry entries[] = { + { "debug", 'd', 0, G_OPTION_ARG_STRING, &debug_level, "Debug level (for samba)", "N" }, + { "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose, "Verbose output", 0 }, + { NULL } + }; + + context = g_option_context_new("- Samba domain join utility"); + g_option_context_add_main_entries(context, entries, NULL); +/* g_option_context_add_main_entries(context, entries, GETTEXT_PACKAGE); */ + g_option_context_add_group(context, gtk_get_option_group(TRUE)); + g_option_context_parse(context, &argc, &argv, &error); + + gtk_init(&argc, &argv); + g_set_application_name("Samba"); + + ret = init_join_state(&state); + if (ret) { + return ret; + } + + ret = initialize_join_state(state, debug_level); + if (ret) { + return ret; + } + + draw_main_window(state); + + gtk_main(); + + do_cleanup(state); + + return 0; +} diff --git a/source3/lib/netapi/examples/netdomjoin-gui/samba.ico b/source3/lib/netapi/examples/netdomjoin-gui/samba.ico Binary files differnew file mode 100755 index 0000000000..b70c9590de --- /dev/null +++ b/source3/lib/netapi/examples/netdomjoin-gui/samba.ico diff --git a/source3/lib/netapi/examples/netdomjoin/netdomjoin.c b/source3/lib/netapi/examples/netdomjoin/netdomjoin.c new file mode 100644 index 0000000000..29f66a17a2 --- /dev/null +++ b/source3/lib/netapi/examples/netdomjoin/netdomjoin.c @@ -0,0 +1,120 @@ +/* + * Unix SMB/CIFS implementation. + * Join Support (cmdline + netapi) + * Copyright (C) Guenther Deschner 2007-2008 + * + * 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 + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <sys/types.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <netapi.h> + +char *get_string_param(const char *param) +{ + char *p; + + p = strchr(param, '='); + if (!p) { + return NULL; + } + + return (p+1); +} + +int main(int argc, char **argv) +{ + NET_API_STATUS status; + const char *server_name = NULL; + const char *domain_name = NULL; + const char *account_ou = NULL; + const char *Account = NULL; + const char *password = NULL; + uint32_t join_flags = 3; + struct libnetapi_ctx *ctx = NULL; + int i; + + status = libnetapi_init(&ctx); + if (status != 0) { + return status; + } + + if (argc < 2) { + printf("usage: netdomjoin\n"); + printf("\t[hostname] [domain=DOMAIN] <ou=OU> " + "<usero=USERO> <passwordo=PASSWORDO> " + "<userd=USERD> <passwordd=PASSWORDD> " + "<debug=DEBUGLEVEL>\n"); + return 0; + } + + if (argc > 2) { + server_name = argv[1]; + } + + for (i=0; i<argc; i++) { + if (strncasecmp(argv[i], "ou", strlen("ou")) == 0) { + account_ou = get_string_param(argv[i]); + } + if (strncasecmp(argv[i], "domain", strlen("domain"))== 0) { + domain_name = get_string_param(argv[i]); + } + if (strncasecmp(argv[i], "userd", strlen("userd"))== 0) { + Account = get_string_param(argv[i]); + } + if (strncasecmp(argv[i], "passwordd", strlen("passwordd"))== 0) { + password = get_string_param(argv[i]); + } + if (strncasecmp(argv[i], "usero", strlen("usero"))== 0) { + const char *str = NULL; + str = get_string_param(argv[i]); + libnetapi_set_username(ctx, str); + } + if (strncasecmp(argv[i], "passwordo", strlen("passwordo"))== 0) { + const char *str = NULL; + str = get_string_param(argv[i]); + libnetapi_set_password(ctx, str); + } + if (strncasecmp(argv[i], "debug", strlen("debug"))== 0) { + const char *str = NULL; + str = get_string_param(argv[i]); + libnetapi_set_debuglevel(ctx, str); + } + } + + status = NetJoinDomain(server_name, + domain_name, + account_ou, + Account, + password, + join_flags); + if (status != 0) { + const char *errstr = NULL; + errstr = libnetapi_get_error_string(ctx, status); + if (!errstr) { + errstr = libnetapi_errstr(status); + } + printf("Join failed with: %s\n", errstr); + } else { + printf("Successfully joined\n"); + } + + libnetapi_free(ctx); + + return status; +} diff --git a/source3/lib/netapi/getdc.c b/source3/lib/netapi/getdc.c new file mode 100644 index 0000000000..2626eb0af4 --- /dev/null +++ b/source3/lib/netapi/getdc.c @@ -0,0 +1,239 @@ +/* + * Unix SMB/CIFS implementation. + * NetApi GetDC Support + * Copyright (C) Guenther Deschner 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 + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "includes.h" + +#include "lib/netapi/netapi.h" +#include "libnet/libnet.h" + +/******************************************************************** +********************************************************************/ + +static WERROR NetGetDCNameLocal(struct libnetapi_ctx *ctx, + const char *server_name, + const char *domain_name, + uint8_t **buffer) +{ + return WERR_NOT_SUPPORTED; +} + +/******************************************************************** +********************************************************************/ + +static WERROR NetGetDCNameRemote(struct libnetapi_ctx *ctx, + const char *server_name, + const char *domain_name, + uint8_t **buffer) +{ + struct cli_state *cli = NULL; + struct rpc_pipe_client *pipe_cli = NULL; + NTSTATUS status; + WERROR werr; + + status = cli_full_connection(&cli, NULL, server_name, + NULL, 0, + "IPC$", "IPC", + ctx->username, + ctx->workgroup, + ctx->password, + 0, Undefined, NULL); + + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + pipe_cli = cli_rpc_pipe_open_noauth(cli, PI_NETLOGON, + &status); + if (!pipe_cli) { + werr = ntstatus_to_werror(status); + goto done; + }; + + status = rpccli_netr_GetDcName(pipe_cli, ctx, + server_name, + domain_name, + (const char **)buffer, + &werr); + done: + if (cli) { + cli_shutdown(cli); + } + + return werr; +} + +/******************************************************************** +********************************************************************/ + +static WERROR libnetapi_NetGetDCName(struct libnetapi_ctx *ctx, + const char *server_name, + const char *domain_name, + uint8_t **buffer) +{ + if (!server_name || is_myname_or_ipaddr(server_name)) { + return NetGetDCNameLocal(ctx, + server_name, + domain_name, + buffer); + } + + return NetGetDCNameRemote(ctx, + server_name, + domain_name, + buffer); +} + +/**************************************************************** + NetGetDCName +****************************************************************/ + +NET_API_STATUS NetGetDCName(const char *server_name, + const char *domain_name, + uint8_t **buffer) +{ + struct libnetapi_ctx *ctx = NULL; + NET_API_STATUS status; + WERROR werr; + + status = libnetapi_getctx(&ctx); + if (status != 0) { + return status; + } + + werr = libnetapi_NetGetDCName(ctx, + server_name, + domain_name, + buffer); + if (!W_ERROR_IS_OK(werr)) { + return W_ERROR_V(werr); + } + + return NET_API_STATUS_SUCCESS; +} + +/******************************************************************** +********************************************************************/ + +static WERROR NetGetAnyDCNameLocal(struct libnetapi_ctx *ctx, + const char *server_name, + const char *domain_name, + uint8_t **buffer) +{ + return WERR_NOT_SUPPORTED; +} + +/******************************************************************** +********************************************************************/ + +static WERROR NetGetAnyDCNameRemote(struct libnetapi_ctx *ctx, + const char *server_name, + const char *domain_name, + uint8_t **buffer) +{ + struct cli_state *cli = NULL; + struct rpc_pipe_client *pipe_cli = NULL; + NTSTATUS status; + WERROR werr; + + status = cli_full_connection(&cli, NULL, server_name, + NULL, 0, + "IPC$", "IPC", + ctx->username, + ctx->workgroup, + ctx->password, + 0, Undefined, NULL); + + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + pipe_cli = cli_rpc_pipe_open_noauth(cli, PI_NETLOGON, + &status); + if (!pipe_cli) { + werr = ntstatus_to_werror(status); + goto done; + }; + + status = rpccli_netr_GetAnyDCName(pipe_cli, ctx, + server_name, + domain_name, + (const char **)buffer, + &werr); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + done: + if (cli) { + cli_shutdown(cli); + } + + return werr; + +} + +/******************************************************************** +********************************************************************/ + +static WERROR libnetapi_NetGetAnyDCName(struct libnetapi_ctx *ctx, + const char *server_name, + const char *domain_name, + uint8_t **buffer) +{ + if (!server_name || is_myname_or_ipaddr(server_name)) { + return NetGetAnyDCNameLocal(ctx, + server_name, + domain_name, + buffer); + } + + return NetGetAnyDCNameRemote(ctx, + server_name, + domain_name, + buffer); +} + +/**************************************************************** + NetGetAnyDCName +****************************************************************/ + +NET_API_STATUS NetGetAnyDCName(const char *server_name, + const char *domain_name, + uint8_t **buffer) +{ + struct libnetapi_ctx *ctx = NULL; + NET_API_STATUS status; + WERROR werr; + + status = libnetapi_getctx(&ctx); + if (status != 0) { + return status; + } + + werr = libnetapi_NetGetAnyDCName(ctx, + server_name, + domain_name, + buffer); + if (!W_ERROR_IS_OK(werr)) { + return W_ERROR_V(werr); + } + + return NET_API_STATUS_SUCCESS; +} diff --git a/source3/lib/netapi/joindomain.c b/source3/lib/netapi/joindomain.c index 210763174e..55f334b5e1 100644 --- a/source3/lib/netapi/joindomain.c +++ b/source3/lib/netapi/joindomain.c @@ -1,7 +1,7 @@ /* * Unix SMB/CIFS implementation. * NetApi Join Support - * Copyright (C) Guenther Deschner 2007 + * Copyright (C) Guenther Deschner 2007-2008 * * 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 @@ -19,65 +19,323 @@ #include "includes.h" -extern const char *opt_user_name; -extern const char *opt_workgroup; -extern const char *opt_password; +#include "lib/netapi/netapi.h" +#include "libnet/libnet.h" -WERROR NetJoinDomain(const char *server_name, - const char *domain_name, - const char *account_ou, - const char *Account, - const char *password, - uint32_t join_flags) +/**************************************************************** +****************************************************************/ + +static WERROR NetJoinDomainLocal(struct libnetapi_ctx *mem_ctx, + const char *server_name, + const char *domain_name, + const char *account_ou, + const char *Account, + const char *password, + uint32_t join_flags) +{ + struct libnet_JoinCtx *r = NULL; + WERROR werr; + + if (!domain_name) { + return WERR_INVALID_PARAM; + } + + werr = libnet_init_JoinCtx(mem_ctx, &r); + W_ERROR_NOT_OK_RETURN(werr); + + r->in.domain_name = talloc_strdup(mem_ctx, domain_name); + W_ERROR_HAVE_NO_MEMORY(r->in.domain_name); + + if (join_flags & WKSSVC_JOIN_FLAGS_JOIN_TYPE) { + NTSTATUS status; + struct DS_DOMAIN_CONTROLLER_INFO *info = NULL; + uint32_t flags = DS_DIRECTORY_SERVICE_REQUIRED | + DS_WRITABLE_REQUIRED | + DS_RETURN_DNS_NAME; + status = dsgetdcname(mem_ctx, domain_name, + NULL, NULL, flags, &info); + if (!NT_STATUS_IS_OK(status)) { + libnetapi_set_error_string(mem_ctx, + "%s", get_friendly_nt_error_msg(status)); + return ntstatus_to_werror(status); + } + r->in.dc_name = talloc_strdup(mem_ctx, + info->domain_controller_name); + W_ERROR_HAVE_NO_MEMORY(r->in.dc_name); + } + + if (account_ou) { + r->in.account_ou = talloc_strdup(mem_ctx, account_ou); + W_ERROR_HAVE_NO_MEMORY(r->in.account_ou); + } + + if (Account) { + r->in.admin_account = talloc_strdup(mem_ctx, Account); + W_ERROR_HAVE_NO_MEMORY(r->in.admin_account); + } + + if (password) { + r->in.admin_password = talloc_strdup(mem_ctx, password); + W_ERROR_HAVE_NO_MEMORY(r->in.admin_password); + } + + r->in.join_flags = join_flags; + r->in.modify_config = true; + + werr = libnet_Join(mem_ctx, r); + if (!W_ERROR_IS_OK(werr) && r->out.error_string) { + libnetapi_set_error_string(mem_ctx, "%s", r->out.error_string); + } + TALLOC_FREE(r); + + return werr; +} + +/**************************************************************** +****************************************************************/ + +static WERROR NetJoinDomainRemote(struct libnetapi_ctx *ctx, + const char *server_name, + const char *domain_name, + const char *account_ou, + const char *Account, + const char *password, + uint32_t join_flags) { - TALLOC_CTX *mem_ctx = NULL; struct cli_state *cli = NULL; struct rpc_pipe_client *pipe_cli = NULL; - struct wkssvc_PasswordBuffer encrypted_password; + struct wkssvc_PasswordBuffer *encrypted_password = NULL; NTSTATUS status; WERROR werr; unsigned int old_timeout = 0; - ZERO_STRUCT(encrypted_password); + status = cli_full_connection(&cli, NULL, server_name, + NULL, 0, + "IPC$", "IPC", + ctx->username, + ctx->workgroup, + ctx->password, + 0, Undefined, NULL); - mem_ctx = talloc_init("NetJoinDomain"); - if (!mem_ctx) { - werr = WERR_NOMEM; + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); goto done; } - if (!server_name || is_myname_or_ipaddr(server_name)) { - werr = WERR_NOT_SUPPORTED; + pipe_cli = cli_rpc_pipe_open_noauth(cli, PI_WKSSVC, + &status); + if (!pipe_cli) { + werr = ntstatus_to_werror(status); goto done; } - if (!domain_name) { - werr = WERR_INVALID_PARAM; + if (password) { + encode_wkssvc_join_password_buffer(ctx, + password, + &cli->user_session_key, + &encrypted_password); + } + + old_timeout = cli_set_timeout(cli, 60000); + + status = rpccli_wkssvc_NetrJoinDomain2(pipe_cli, ctx, + server_name, domain_name, + account_ou, Account, + encrypted_password, + join_flags, &werr); + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); goto done; } + done: + if (cli) { + cli_set_timeout(cli, old_timeout); + cli_shutdown(cli); + } + + return werr; +} + +/**************************************************************** +****************************************************************/ + +static WERROR libnetapi_NetJoinDomain(struct libnetapi_ctx *ctx, + const char *server_name, + const char *domain_name, + const char *account_ou, + const char *Account, + const char *password, + uint32_t join_flags) +{ + if (!domain_name) { + return WERR_INVALID_PARAM; + } + + if (!server_name || is_myname_or_ipaddr(server_name)) { + + return NetJoinDomainLocal(ctx, + server_name, + domain_name, + account_ou, + Account, + password, + join_flags); + } + + return NetJoinDomainRemote(ctx, + server_name, + domain_name, + account_ou, + Account, + password, + join_flags); +} + +/**************************************************************** + NetJoinDomain +****************************************************************/ + +NET_API_STATUS NetJoinDomain(const char *server_name, + const char *domain_name, + const char *account_ou, + const char *Account, + const char *password, + uint32_t join_flags) +{ + struct libnetapi_ctx *ctx = NULL; + NET_API_STATUS status; + WERROR werr; + + status = libnetapi_getctx(&ctx); + if (status != 0) { + return status; + } + + werr = libnetapi_NetJoinDomain(ctx, + server_name, + domain_name, + account_ou, + Account, + password, + join_flags); + if (!W_ERROR_IS_OK(werr)) { + return W_ERROR_V(werr); + } + + return NET_API_STATUS_SUCCESS; +} + +/**************************************************************** +****************************************************************/ + +static WERROR NetUnjoinDomainLocal(struct libnetapi_ctx *mem_ctx, + const char *server_name, + const char *account, + const char *password, + uint32_t unjoin_flags) +{ + struct libnet_UnjoinCtx *r = NULL; + struct dom_sid domain_sid; + WERROR werr; + + if (!secrets_fetch_domain_sid(lp_workgroup(), &domain_sid)) { + return WERR_SETUP_NOT_JOINED; + } + + werr = libnet_init_UnjoinCtx(mem_ctx, &r); + W_ERROR_NOT_OK_RETURN(werr); + + if (server_name) { + r->in.dc_name = talloc_strdup(mem_ctx, server_name); + W_ERROR_HAVE_NO_MEMORY(r->in.dc_name); + } else { + NTSTATUS status; + const char *domain = NULL; + struct DS_DOMAIN_CONTROLLER_INFO *info = NULL; + uint32_t flags = DS_DIRECTORY_SERVICE_REQUIRED | + DS_WRITABLE_REQUIRED | + DS_RETURN_DNS_NAME; + if (lp_realm()) { + domain = lp_realm(); + } else { + domain = lp_workgroup(); + } + status = dsgetdcname(mem_ctx, domain, + NULL, NULL, flags, &info); + if (!NT_STATUS_IS_OK(status)) { + libnetapi_set_error_string(mem_ctx, + "%s", get_friendly_nt_error_msg(status)); + return ntstatus_to_werror(status); + } + r->in.dc_name = talloc_strdup(mem_ctx, + info->domain_controller_name); + W_ERROR_HAVE_NO_MEMORY(r->in.dc_name); + } + + if (account) { + r->in.admin_account = talloc_strdup(mem_ctx, account); + W_ERROR_HAVE_NO_MEMORY(r->in.admin_account); + } + + if (password) { + r->in.admin_password = talloc_strdup(mem_ctx, password); + W_ERROR_HAVE_NO_MEMORY(r->in.admin_password); + } + + r->in.unjoin_flags = unjoin_flags; + r->in.modify_config = true; + r->in.debug = true; + + r->in.domain_sid = &domain_sid; + + werr = libnet_Unjoin(mem_ctx, r); + if (!W_ERROR_IS_OK(werr) && r->out.error_string) { + libnetapi_set_error_string(mem_ctx, "%s", r->out.error_string); + } + TALLOC_FREE(r); + + return werr; +} + +/**************************************************************** +****************************************************************/ + +static WERROR NetUnjoinDomainRemote(struct libnetapi_ctx *ctx, + const char *server_name, + const char *account, + const char *password, + uint32_t unjoin_flags) +{ + struct cli_state *cli = NULL; + struct rpc_pipe_client *pipe_cli = NULL; + struct wkssvc_PasswordBuffer *encrypted_password = NULL; + NTSTATUS status; + WERROR werr; + unsigned int old_timeout = 0; + status = cli_full_connection(&cli, NULL, server_name, NULL, 0, "IPC$", "IPC", - opt_user_name, opt_workgroup, - opt_password, 0, Undefined, NULL); + ctx->username, + ctx->workgroup, + ctx->password, + 0, Undefined, NULL); if (!NT_STATUS_IS_OK(status)) { werr = ntstatus_to_werror(status); goto done; } - old_timeout = cli_set_timeout(cli, 60000); - pipe_cli = cli_rpc_pipe_open_noauth(cli, PI_WKSSVC, &status); if (!pipe_cli) { werr = ntstatus_to_werror(status); goto done; - }; + } if (password) { - encode_wkssvc_join_password_buffer(mem_ctx, + encode_wkssvc_join_password_buffer(ctx, password, &cli->user_session_key, &encrypted_password); @@ -85,11 +343,12 @@ WERROR NetJoinDomain(const char *server_name, old_timeout = cli_set_timeout(cli, 60000); - status = rpccli_wkssvc_NetrJoinDomain2(pipe_cli, mem_ctx, - server_name, domain_name, - account_ou, Account, - &encrypted_password, - join_flags, &werr); + status = rpccli_wkssvc_NetrUnjoinDomain2(pipe_cli, ctx, + server_name, + account, + encrypted_password, + unjoin_flags, + &werr); if (!NT_STATUS_IS_OK(status)) { werr = ntstatus_to_werror(status); goto done; @@ -100,72 +359,313 @@ WERROR NetJoinDomain(const char *server_name, cli_set_timeout(cli, old_timeout); cli_shutdown(cli); } - TALLOC_FREE(mem_ctx); return werr; } -WERROR NetUnjoinDomain(const char *server_name, - const char *account, - const char *password, - uint32_t unjoin_flags) +/**************************************************************** +****************************************************************/ + +static WERROR libnetapi_NetUnjoinDomain(struct libnetapi_ctx *ctx, + const char *server_name, + const char *account, + const char *password, + uint32_t unjoin_flags) +{ + if (!server_name || is_myname_or_ipaddr(server_name)) { + + return NetUnjoinDomainLocal(ctx, + server_name, + account, + password, + unjoin_flags); + } + + return NetUnjoinDomainRemote(ctx, + server_name, + account, + password, + unjoin_flags); +} + +/**************************************************************** + NetUnjoinDomain +****************************************************************/ + +NET_API_STATUS NetUnjoinDomain(const char *server_name, + const char *account, + const char *password, + uint32_t unjoin_flags) +{ + struct libnetapi_ctx *ctx = NULL; + NET_API_STATUS status; + WERROR werr; + + status = libnetapi_getctx(&ctx); + if (status != 0) { + return status; + } + + werr = libnetapi_NetUnjoinDomain(ctx, + server_name, + account, + password, + unjoin_flags); + if (!W_ERROR_IS_OK(werr)) { + return W_ERROR_V(werr); + } + + return NET_API_STATUS_SUCCESS; +} + +/**************************************************************** +****************************************************************/ + +static WERROR NetGetJoinInformationRemote(struct libnetapi_ctx *ctx, + const char *server_name, + const char **name_buffer, + uint16_t *name_type) { - TALLOC_CTX *mem_ctx = NULL; struct cli_state *cli = NULL; struct rpc_pipe_client *pipe_cli = NULL; - struct wkssvc_PasswordBuffer encrypted_password; NTSTATUS status; WERROR werr; - unsigned int old_timeout = 0; - ZERO_STRUCT(encrypted_password); + status = cli_full_connection(&cli, NULL, server_name, + NULL, 0, + "IPC$", "IPC", + ctx->username, + ctx->workgroup, + ctx->password, + 0, Undefined, NULL); - mem_ctx = talloc_init("NetUnjoinDomain"); - if (!mem_ctx) { - werr = WERR_NOMEM; + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); goto done; } - if (!server_name || is_myname_or_ipaddr(server_name)) { - werr = WERR_NOT_SUPPORTED; + pipe_cli = cli_rpc_pipe_open_noauth(cli, PI_WKSSVC, + &status); + if (!pipe_cli) { + werr = ntstatus_to_werror(status); + goto done; + } + + status = rpccli_wkssvc_NetrGetJoinInformation(pipe_cli, ctx, + server_name, + name_buffer, + (enum wkssvc_NetJoinStatus *)name_type, + &werr); + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); goto done; } + done: + if (cli) { + cli_shutdown(cli); + } + + return werr; +} + +/**************************************************************** +****************************************************************/ + +static WERROR NetGetJoinInformationLocal(struct libnetapi_ctx *ctx, + const char *server_name, + const char **name_buffer, + uint16_t *name_type) +{ + if ((lp_security() == SEC_ADS) && lp_realm()) { + *name_buffer = talloc_strdup(ctx, lp_realm()); + } else { + *name_buffer = talloc_strdup(ctx, lp_workgroup()); + } + if (!*name_buffer) { + return WERR_NOMEM; + } + + switch (lp_server_role()) { + case ROLE_DOMAIN_MEMBER: + case ROLE_DOMAIN_PDC: + case ROLE_DOMAIN_BDC: + *name_type = NetSetupDomainName; + break; + case ROLE_STANDALONE: + default: + *name_type = NetSetupWorkgroupName; + break; + } + + return WERR_OK; +} + +static WERROR libnetapi_NetGetJoinInformation(struct libnetapi_ctx *ctx, + const char *server_name, + const char **name_buffer, + uint16_t *name_type) +{ + if (!server_name || is_myname_or_ipaddr(server_name)) { + return NetGetJoinInformationLocal(ctx, + server_name, + name_buffer, + name_type); + } + + return NetGetJoinInformationRemote(ctx, + server_name, + name_buffer, + name_type); +} + +/**************************************************************** + NetGetJoinInformation +****************************************************************/ + +NET_API_STATUS NetGetJoinInformation(const char *server_name, + const char **name_buffer, + uint16_t *name_type) +{ + struct libnetapi_ctx *ctx = NULL; + NET_API_STATUS status; + WERROR werr; + + status = libnetapi_getctx(&ctx); + if (status != 0) { + return status; + } + + werr = libnetapi_NetGetJoinInformation(ctx, + server_name, + name_buffer, + name_type); + if (!W_ERROR_IS_OK(werr)) { + return W_ERROR_V(werr); + } + + return NET_API_STATUS_SUCCESS; +} + +/**************************************************************** +****************************************************************/ + +static WERROR NetGetJoinableOUsLocal(struct libnetapi_ctx *ctx, + const char *server_name, + const char *domain, + const char *account, + const char *password, + uint32_t *ou_count, + const char ***ous) +{ +#ifdef WITH_ADS + NTSTATUS status; + ADS_STATUS ads_status; + ADS_STRUCT *ads = NULL; + struct DS_DOMAIN_CONTROLLER_INFO *info = NULL; + uint32_t flags = DS_DIRECTORY_SERVICE_REQUIRED | + DS_RETURN_DNS_NAME; + + status = dsgetdcname(ctx, domain, + NULL, NULL, flags, &info); + if (!NT_STATUS_IS_OK(status)) { + libnetapi_set_error_string(ctx, "%s", + get_friendly_nt_error_msg(status)); + return ntstatus_to_werror(status); + } + + ads = ads_init(domain, domain, info->domain_controller_name); + if (!ads) { + return WERR_GENERAL_FAILURE; + } + + SAFE_FREE(ads->auth.user_name); + if (account) { + ads->auth.user_name = SMB_STRDUP(account); + } else if (ctx->username) { + ads->auth.user_name = SMB_STRDUP(ctx->username); + } + + SAFE_FREE(ads->auth.password); + if (password) { + ads->auth.password = SMB_STRDUP(password); + } else if (ctx->password) { + ads->auth.password = SMB_STRDUP(ctx->password); + } + + ads_status = ads_connect(ads); + if (!ADS_ERR_OK(ads_status)) { + ads_destroy(&ads); + return WERR_DEFAULT_JOIN_REQUIRED; + } + + ads_status = ads_get_joinable_ous(ads, ctx, + (char ***)ous, + (size_t *)ou_count); + if (!ADS_ERR_OK(ads_status)) { + ads_destroy(&ads); + return WERR_DEFAULT_JOIN_REQUIRED; + } + + ads_destroy(&ads); + return WERR_OK; +#else + return WERR_NOT_SUPPORTED; +#endif +} + +/**************************************************************** +****************************************************************/ + +static WERROR NetGetJoinableOUsRemote(struct libnetapi_ctx *ctx, + const char *server_name, + const char *domain, + const char *account, + const char *password, + uint32_t *ou_count, + const char ***ous) +{ + struct cli_state *cli = NULL; + struct rpc_pipe_client *pipe_cli = NULL; + struct wkssvc_PasswordBuffer *encrypted_password = NULL; + NTSTATUS status; + WERROR werr; + status = cli_full_connection(&cli, NULL, server_name, NULL, 0, "IPC$", "IPC", - opt_user_name, opt_workgroup, - opt_password, 0, Undefined, NULL); + ctx->username, + ctx->workgroup, + ctx->password, + 0, Undefined, NULL); if (!NT_STATUS_IS_OK(status)) { werr = ntstatus_to_werror(status); goto done; } - old_timeout = cli_set_timeout(cli, 60000); - pipe_cli = cli_rpc_pipe_open_noauth(cli, PI_WKSSVC, &status); if (!pipe_cli) { werr = ntstatus_to_werror(status); goto done; - }; + } if (password) { - encode_wkssvc_join_password_buffer(mem_ctx, + encode_wkssvc_join_password_buffer(ctx, password, &cli->user_session_key, &encrypted_password); } - old_timeout = cli_set_timeout(cli, 60000); - - status = rpccli_wkssvc_NetrUnjoinDomain2(pipe_cli, mem_ctx, - server_name, - account, - &encrypted_password, - unjoin_flags, - &werr); + status = rpccli_wkssvc_NetrGetJoinableOus2(pipe_cli, ctx, + server_name, + domain, + account, + encrypted_password, + ou_count, + ous, + &werr); if (!NT_STATUS_IS_OK(status)) { werr = ntstatus_to_werror(status); goto done; @@ -173,10 +673,72 @@ WERROR NetUnjoinDomain(const char *server_name, done: if (cli) { - cli_set_timeout(cli, old_timeout); cli_shutdown(cli); } - TALLOC_FREE(mem_ctx); return werr; } + +/**************************************************************** +****************************************************************/ + +static WERROR libnetapi_NetGetJoinableOUs(struct libnetapi_ctx *ctx, + const char *server_name, + const char *domain, + const char *account, + const char *password, + uint32_t *ou_count, + const char ***ous) +{ + if (!server_name || is_myname_or_ipaddr(server_name)) { + return NetGetJoinableOUsLocal(ctx, + server_name, + domain, + account, + password, + ou_count, + ous); + } + + return NetGetJoinableOUsRemote(ctx, + server_name, + domain, + account, + password, + ou_count, + ous); +} + +/**************************************************************** + NetGetJoinableOUs +****************************************************************/ + +NET_API_STATUS NetGetJoinableOUs(const char *server_name, + const char *domain, + const char *account, + const char *password, + uint32_t *ou_count, + const char ***ous) +{ + struct libnetapi_ctx *ctx = NULL; + NET_API_STATUS status; + WERROR werr; + + status = libnetapi_getctx(&ctx); + if (status != 0) { + return status; + } + + werr = libnetapi_NetGetJoinableOUs(ctx, + server_name, + domain, + account, + password, + ou_count, + ous); + if (!W_ERROR_IS_OK(werr)) { + return W_ERROR_V(werr); + } + + return NET_API_STATUS_SUCCESS; +} diff --git a/source3/lib/netapi/joindomain.h b/source3/lib/netapi/joindomain.h deleted file mode 100644 index 2c71702db7..0000000000 --- a/source3/lib/netapi/joindomain.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Unix SMB/CIFS implementation. - * NetApi Support - * Copyright (C) Guenther Deschner 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 - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see <http://www.gnu.org/licenses/>. - */ - -WERROR NetJoinDomain(const char *server, - const char *domain, - const char *account_ou, - const char *account, - const char *password, - uint32_t join_options); -WERROR NetUnjoinDomain(const char *server_name, - const char *account, - const char *password, - uint32_t unjoin_flags); diff --git a/source3/lib/netapi/netapi.c b/source3/lib/netapi/netapi.c new file mode 100644 index 0000000000..47b3ba93cf --- /dev/null +++ b/source3/lib/netapi/netapi.c @@ -0,0 +1,257 @@ +/* + * Unix SMB/CIFS implementation. + * NetApi Support + * Copyright (C) Guenther Deschner 2007-2008 + * + * 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 + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "includes.h" +#include "lib/netapi/netapi.h" + +extern bool AllowDebugChange; + +struct libnetapi_ctx *stat_ctx = NULL; +TALLOC_CTX *frame = NULL; +static bool libnetapi_initialized = false; + +/**************************************************************** +****************************************************************/ + +NET_API_STATUS libnetapi_init(struct libnetapi_ctx **context) +{ + struct libnetapi_ctx *ctx = NULL; + char *krb5_cc_env = NULL; + + if (stat_ctx && libnetapi_initialized) { + *context = stat_ctx; + return NET_API_STATUS_SUCCESS; + } + +#ifdef DEVELOPER + talloc_enable_leak_report(); +#endif + frame = talloc_stackframe(); + + ctx = talloc_zero(frame, struct libnetapi_ctx); + if (!ctx) { + TALLOC_FREE(frame); + return W_ERROR_V(WERR_NOMEM); + } + + DEBUGLEVEL = 0; + setup_logging("libnetapi", true); + + dbf = x_stderr; + x_setbuf(x_stderr, NULL); + AllowDebugChange = false; + + load_case_tables(); + + if (!lp_load(get_dyn_CONFIGFILE(), true, false, false, false)) { + TALLOC_FREE(frame); + return W_ERROR_V(WERR_GENERAL_FAILURE); + } + + AllowDebugChange = true; + + init_names(); + load_interfaces(); + reopen_logs(); + + BlockSignals(True, SIGPIPE); + + krb5_cc_env = getenv(KRB5_ENV_CCNAME); + if (!krb5_cc_env || (strlen(krb5_cc_env) == 0)) { + ctx->krb5_cc_env = talloc_strdup(frame, "MEMORY:libnetapi"); + setenv(KRB5_ENV_CCNAME, ctx->krb5_cc_env, 1); + } + + libnetapi_initialized = true; + + *context = stat_ctx = ctx; + + return NET_API_STATUS_SUCCESS; +} + +/**************************************************************** +****************************************************************/ + +NET_API_STATUS libnetapi_getctx(struct libnetapi_ctx **ctx) +{ + if (stat_ctx) { + *ctx = stat_ctx; + return NET_API_STATUS_SUCCESS; + } + + return libnetapi_init(ctx); +} + +/**************************************************************** +****************************************************************/ + +NET_API_STATUS libnetapi_free(struct libnetapi_ctx *ctx) +{ + + if (ctx->krb5_cc_env) { + char *env = getenv(KRB5_ENV_CCNAME); + if (env && (strequal(ctx->krb5_cc_env, env))) { + unsetenv(KRB5_ENV_CCNAME); + } + } + + gfree_names(); + gfree_loadparm(); + gfree_case_tables(); + gfree_charcnv(); + gfree_interfaces(); + + gencache_shutdown(); + secrets_shutdown(); + + TALLOC_FREE(ctx); + TALLOC_FREE(frame); + + gfree_debugsyms(); + + return NET_API_STATUS_SUCCESS; +} + +/**************************************************************** +****************************************************************/ + +NET_API_STATUS libnetapi_set_debuglevel(struct libnetapi_ctx *ctx, + const char *debuglevel) +{ + AllowDebugChange = true; + ctx->debuglevel = talloc_strdup(ctx, debuglevel); + if (!debug_parse_levels(debuglevel)) { + return W_ERROR_V(WERR_GENERAL_FAILURE); + } + return NET_API_STATUS_SUCCESS; +} + +/**************************************************************** +****************************************************************/ + +NET_API_STATUS libnetapi_get_debuglevel(struct libnetapi_ctx *ctx, + char **debuglevel) +{ + *debuglevel = ctx->debuglevel; + return NET_API_STATUS_SUCCESS; +} + +/**************************************************************** +****************************************************************/ + +NET_API_STATUS libnetapi_set_username(struct libnetapi_ctx *ctx, + const char *username) +{ + TALLOC_FREE(ctx->username); + ctx->username = talloc_strdup(ctx, username); + if (!ctx->username) { + return W_ERROR_V(WERR_NOMEM); + } + return NET_API_STATUS_SUCCESS; +} + +NET_API_STATUS libnetapi_set_password(struct libnetapi_ctx *ctx, + const char *password) +{ + TALLOC_FREE(ctx->password); + ctx->password = talloc_strdup(ctx, password); + if (!ctx->password) { + return W_ERROR_V(WERR_NOMEM); + } + return NET_API_STATUS_SUCCESS; +} + +NET_API_STATUS libnetapi_set_workgroup(struct libnetapi_ctx *ctx, + const char *workgroup) +{ + TALLOC_FREE(ctx->workgroup); + ctx->workgroup = talloc_strdup(ctx, workgroup); + if (!ctx->workgroup) { + return W_ERROR_V(WERR_NOMEM); + } + return NET_API_STATUS_SUCCESS; +} + +/**************************************************************** +****************************************************************/ + +const char *libnetapi_errstr(NET_API_STATUS status) +{ + if (status & 0xc0000000) { + return get_friendly_nt_error_msg(NT_STATUS(status)); + } + + return get_friendly_werror_msg(W_ERROR(status)); +} + +/**************************************************************** +****************************************************************/ + +NET_API_STATUS libnetapi_set_error_string(struct libnetapi_ctx *ctx, + const char *format, ...) +{ + va_list args; + + TALLOC_FREE(ctx->error_string); + + va_start(args, format); + ctx->error_string = talloc_vasprintf(ctx, format, args); + va_end(args); + + if (!ctx->error_string) { + return W_ERROR_V(WERR_NOMEM); + } + return NET_API_STATUS_SUCCESS; +} + +/**************************************************************** +****************************************************************/ + +const char *libnetapi_get_error_string(struct libnetapi_ctx *ctx, + NET_API_STATUS status) +{ + struct libnetapi_ctx *tmp_ctx = ctx; + + if (!tmp_ctx) { + status = libnetapi_getctx(&tmp_ctx); + if (status != 0) { + return NULL; + } + } + + if (tmp_ctx->error_string) { + return tmp_ctx->error_string; + } + + return libnetapi_errstr(status); +} + +/**************************************************************** +****************************************************************/ + +NET_API_STATUS NetApiBufferFree(void *buffer) +{ + if (!buffer) { + return W_ERROR_V(WERR_INSUFFICIENT_BUFFER); + } + + talloc_free(buffer); + + return NET_API_STATUS_SUCCESS; +} diff --git a/source3/lib/netapi/netapi.h b/source3/lib/netapi/netapi.h new file mode 100644 index 0000000000..002fc37762 --- /dev/null +++ b/source3/lib/netapi/netapi.h @@ -0,0 +1,147 @@ +/* + * Unix SMB/CIFS implementation. + * NetApi Support + * Copyright (C) Guenther Deschner 2007-2008 + * + * 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 + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __LIB_NETAPI_H__ +#define __LIB_NETAPI_H__ + +/**************************************************************** + include some basic headers +****************************************************************/ + +#include <inttypes.h> + +/**************************************************************** + NET_API_STATUS +****************************************************************/ + +#define NET_API_STATUS uint32_t +#define NET_API_STATUS_SUCCESS 0 + +/**************************************************************** +****************************************************************/ + +#define LIBNETAPI_LOCAL_SERVER(x) (!x || is_myname_or_ipaddr(x)) + +/**************************************************************** +****************************************************************/ + +struct libnetapi_ctx { + char *debuglevel; + char *error_string; + char *username; + char *workgroup; + char *password; + char *krb5_cc_env; +}; + +/**************************************************************** +****************************************************************/ + +NET_API_STATUS libnetapi_init(struct libnetapi_ctx **ctx); +NET_API_STATUS libnetapi_getctx(struct libnetapi_ctx **ctx); +NET_API_STATUS libnetapi_free(struct libnetapi_ctx *ctx); +NET_API_STATUS libnetapi_set_debuglevel(struct libnetapi_ctx *ctx, const char *debuglevel); +NET_API_STATUS libnetapi_get_debuglevel(struct libnetapi_ctx *ctx, char **debuglevel); +NET_API_STATUS libnetapi_set_username(struct libnetapi_ctx *ctx, const char *username); +NET_API_STATUS libnetapi_set_password(struct libnetapi_ctx *ctx, const char *password); +NET_API_STATUS libnetapi_set_workgroup(struct libnetapi_ctx *ctx, const char *workgroup); +const char *libnetapi_errstr(NET_API_STATUS status); +NET_API_STATUS libnetapi_set_error_string(struct libnetapi_ctx *ctx, const char *format, ...); +const char *libnetapi_get_error_string(struct libnetapi_ctx *ctx, NET_API_STATUS status); + + +/**************************************************************** + NetApiBufferFree +****************************************************************/ + +NET_API_STATUS NetApiBufferFree(void *buffer); + +/**************************************************************** + NetJoinDomain +****************************************************************/ + +NET_API_STATUS NetJoinDomain(const char *server, + const char *domain, + const char *account_ou, + const char *account, + const char *password, + uint32_t join_options); + +/**************************************************************** + NetUnjoinDomain +****************************************************************/ + +NET_API_STATUS NetUnjoinDomain(const char *server_name, + const char *account, + const char *password, + uint32_t unjoin_flags); + +/**************************************************************** + NetGetJoinInformation +****************************************************************/ + +NET_API_STATUS NetGetJoinInformation(const char *server_name, + const char **name_buffer, + uint16_t *name_type); + +/**************************************************************** + NetGetJoinableOUs +****************************************************************/ + +NET_API_STATUS NetGetJoinableOUs(const char *server_name, + const char *domain, + const char *account, + const char *password, + uint32_t *ou_count, + const char ***ous); + +/**************************************************************** + NetServerGetInfo +****************************************************************/ + +NET_API_STATUS NetServerGetInfo(const char *server_name, + uint32_t level, + uint8_t **buffer); + +/**************************************************************** + NetServerSetInfo +****************************************************************/ + +NET_API_STATUS NetServerSetInfo(const char *server_name, + uint32_t level, + uint8_t *buffer, + uint32_t *parm_error); + +/**************************************************************** + NetGetDCName +****************************************************************/ + +NET_API_STATUS NetGetDCName(const char *server_name, + const char *domain_name, + uint8_t **buffer); + +/**************************************************************** + NetGetAnyDCName +****************************************************************/ + +NET_API_STATUS NetGetAnyDCName(const char *server_name, + const char *domain_name, + uint8_t **buffer); + +#endif diff --git a/source3/lib/netapi/serverinfo.c b/source3/lib/netapi/serverinfo.c new file mode 100644 index 0000000000..7fa166e411 --- /dev/null +++ b/source3/lib/netapi/serverinfo.c @@ -0,0 +1,339 @@ +/* + * Unix SMB/CIFS implementation. + * NetApi Server Support + * Copyright (C) Guenther Deschner 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 + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "includes.h" + +#include "lib/netapi/netapi.h" +#include "libnet/libnet.h" + +/**************************************************************** +****************************************************************/ + +static WERROR NetServerGetInfoLocal_1005(struct libnetapi_ctx *ctx, + uint8_t **buffer) +{ + struct srvsvc_NetSrvInfo1005 info1005; + + info1005.comment = lp_serverstring(); + *buffer = (uint8_t *)talloc_memdup(ctx, &info1005, sizeof(info1005)); + if (!*buffer) { + return WERR_NOMEM; + } + + return WERR_OK; +} + +/**************************************************************** +****************************************************************/ + +static WERROR NetServerGetInfoLocal(struct libnetapi_ctx *ctx, + const char *server_name, + uint32_t level, + uint8_t **buffer) +{ + switch (level) { + case 1005: + return NetServerGetInfoLocal_1005(ctx, buffer); + default: + return WERR_UNKNOWN_LEVEL; + } + + return WERR_UNKNOWN_LEVEL; +} + +/**************************************************************** +****************************************************************/ + +static WERROR NetServerGetInfoRemote(struct libnetapi_ctx *ctx, + const char *server_name, + uint32_t level, + uint8_t **buffer) +{ + struct cli_state *cli = NULL; + struct rpc_pipe_client *pipe_cli = NULL; + NTSTATUS status; + WERROR werr; + union srvsvc_NetSrvInfo info; + + status = cli_full_connection(&cli, NULL, server_name, + NULL, 0, + "IPC$", "IPC", + ctx->username, + ctx->workgroup, + ctx->password, + 0, Undefined, NULL); + + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + pipe_cli = cli_rpc_pipe_open_noauth(cli, PI_SRVSVC, + &status); + if (!pipe_cli) { + werr = ntstatus_to_werror(status); + goto done; + }; + + status = rpccli_srvsvc_NetSrvGetInfo(pipe_cli, ctx, + server_name, + level, + &info, + &werr); + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + *buffer = (uint8_t *)&info; + + done: + if (cli) { + cli_shutdown(cli); + } + + return werr; +} + +/**************************************************************** +****************************************************************/ + +static WERROR libnetapi_NetServerGetInfo(struct libnetapi_ctx *ctx, + const char *server_name, + uint32_t level, + uint8_t **buffer) +{ + if (!server_name || is_myname_or_ipaddr(server_name)) { + return NetServerGetInfoLocal(ctx, + server_name, + level, + buffer); + } + + return NetServerGetInfoRemote(ctx, + server_name, + level, + buffer); + +} + +/**************************************************************** + NetServerGetInfo +****************************************************************/ + +NET_API_STATUS NetServerGetInfo(const char *server_name, + uint32_t level, + uint8_t **buffer) +{ + struct libnetapi_ctx *ctx = NULL; + NET_API_STATUS status; + WERROR werr; + + status = libnetapi_getctx(&ctx); + if (status != 0) { + return status; + } + + werr = libnetapi_NetServerGetInfo(ctx, + server_name, + level, + buffer); + if (!W_ERROR_IS_OK(werr)) { + return W_ERROR_V(werr); + } + + return NET_API_STATUS_SUCCESS; +} + +/**************************************************************** +****************************************************************/ + +static WERROR NetServerSetInfoLocal_1005(struct libnetapi_ctx *ctx, + uint8_t *buffer, + uint32_t *parm_error) +{ + WERROR werr; + struct libnet_conf_ctx *conf_ctx; + struct srvsvc_NetSrvInfo1005 *info1005; + + if (!buffer) { + *parm_error = 1005; /* sure here ? */ + return WERR_INVALID_PARAM; + } + + info1005 = (struct srvsvc_NetSrvInfo1005 *)buffer; + + if (!info1005->comment) { + *parm_error = 1005; + return WERR_INVALID_PARAM; + } + + if (!lp_config_backend_is_registry()) { + return WERR_NOT_SUPPORTED; + } + + werr = libnet_conf_open(ctx, &conf_ctx); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + werr = libnet_conf_set_global_parameter(conf_ctx, + "server string", + info1005->comment); + + done: + libnet_conf_close(conf_ctx); + return werr; +} + +/**************************************************************** +****************************************************************/ + +static WERROR NetServerSetInfoLocal(struct libnetapi_ctx *ctx, + const char *server_name, + uint32_t level, + uint8_t *buffer, + uint32_t *parm_error) +{ + switch (level) { + case 1005: + return NetServerSetInfoLocal_1005(ctx, buffer, parm_error); + default: + return WERR_UNKNOWN_LEVEL; + } + + return WERR_UNKNOWN_LEVEL; +} + +/**************************************************************** +****************************************************************/ + +static WERROR NetServerSetInfoRemote(struct libnetapi_ctx *ctx, + const char *server_name, + uint32_t level, + uint8_t *buffer, + uint32_t *parm_error) +{ + struct cli_state *cli = NULL; + struct rpc_pipe_client *pipe_cli = NULL; + NTSTATUS status; + WERROR werr; + union srvsvc_NetSrvInfo info; + + status = cli_full_connection(&cli, NULL, server_name, + NULL, 0, + "IPC$", "IPC", + ctx->username, + ctx->workgroup, + ctx->password, + 0, Undefined, NULL); + + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + pipe_cli = cli_rpc_pipe_open_noauth(cli, PI_SRVSVC, + &status); + if (!pipe_cli) { + werr = ntstatus_to_werror(status); + goto done; + }; + + switch (level) { + case 1005: + info.info1005 = (struct srvsvc_NetSrvInfo1005 *)buffer; + break; + default: + werr = WERR_NOT_SUPPORTED; + goto done; + } + + status = rpccli_srvsvc_NetSrvSetInfo(pipe_cli, ctx, + server_name, + level, + info, + parm_error, + &werr); + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); + goto done; + } + + done: + if (cli) { + cli_shutdown(cli); + } + + return werr; +} + +/**************************************************************** +****************************************************************/ + +static WERROR libnetapi_NetServerSetInfo(struct libnetapi_ctx *ctx, + const char *server_name, + uint32_t level, + uint8_t *buffer, + uint32_t *parm_error) +{ + if (!server_name || is_myname_or_ipaddr(server_name)) { + return NetServerSetInfoLocal(ctx, + server_name, + level, + buffer, + parm_error); + } + + return NetServerSetInfoRemote(ctx, + server_name, + level, + buffer, + parm_error); +} + +/**************************************************************** + NetServerSetInfo +****************************************************************/ + +NET_API_STATUS NetServerSetInfo(const char *server_name, + uint32_t level, + uint8_t *buffer, + uint32_t *parm_error) +{ + struct libnetapi_ctx *ctx = NULL; + NET_API_STATUS status; + WERROR werr; + + status = libnetapi_getctx(&ctx); + if (status != 0) { + return status; + } + + werr = libnetapi_NetServerSetInfo(ctx, + server_name, + level, + buffer, + parm_error); + if (!W_ERROR_IS_OK(werr)) { + return W_ERROR_V(werr); + } + + return NET_API_STATUS_SUCCESS; +} diff --git a/source3/lib/popt_common.c b/source3/lib/popt_common.c index b3a84a6f7c..5a9d39d181 100644 --- a/source3/lib/popt_common.c +++ b/source3/lib/popt_common.c @@ -414,6 +414,7 @@ static void get_credentials_file(const char *file) * -N,--no-pass * -S,--signing * -P --machine-pass + * -e --encrypt */ @@ -532,6 +533,10 @@ static void popt_common_credentials_callback(poptContext con, case 'N': set_cmdline_auth_info_password(""); break; + case 'e': + set_cmdline_auth_info_smb_encrypt(); + break; + } } @@ -543,5 +548,6 @@ struct poptOption popt_common_credentials[] = { { "authentication-file", 'A', POPT_ARG_STRING, NULL, 'A', "Get the credentials from a file", "FILE" }, { "signing", 'S', POPT_ARG_STRING, NULL, 'S', "Set the client signing state", "on|off|required" }, {"machine-pass", 'P', POPT_ARG_NONE, NULL, 'P', "Use stored machine account password" }, + {"encrypt", 'e', POPT_ARG_NONE, NULL, 'e', "Encrypt SMB transport (UNIX extended servers only)" }, POPT_TABLEEND }; diff --git a/source3/lib/privileges.c b/source3/lib/privileges.c index 63fb462e32..509da80785 100644 --- a/source3/lib/privileges.c +++ b/source3/lib/privileges.c @@ -184,8 +184,10 @@ static int priv_traverse_fn(TDB_CONTEXT *t, TDB_DATA key, TDB_DATA data, void *s return 0; } - if (!add_sid_to_array( priv->mem_ctx, &sid, &priv->sids.list, - &priv->sids.count )) { + if (!NT_STATUS_IS_OK(add_sid_to_array(priv->mem_ctx, &sid, + &priv->sids.list, + &priv->sids.count))) + { return 0; } diff --git a/source3/lib/replace/libreplace.m4 b/source3/lib/replace/libreplace.m4 index c10a4b2381..f866b3648f 100644 --- a/source3/lib/replace/libreplace.m4 +++ b/source3/lib/replace/libreplace.m4 @@ -100,6 +100,7 @@ AC_CHECK_HEADERS(sys/socket.h netinet/in.h netdb.h arpa/inet.h) AC_CHECK_HEADERS(netinet/ip.h netinet/tcp.h netinet/in_systm.h netinet/in_ip.h) AC_CHECK_HEADERS(sys/sockio.h sys/un.h) AC_CHECK_HEADERS(sys/mount.h mntent.h) +AC_CHECK_HEADERS(stropts.h) dnl we need to check that net/if.h really can be used, to cope with hpux dnl where including it always fails @@ -152,6 +153,26 @@ AC_HAVE_TYPE([struct sockaddr_in6], [ #include <netinet/in.h> ]) +if test x"$ac_cv_type_struct_sockaddr_storage" = x"yes"; then +AC_CHECK_MEMBER(struct sockaddr_storage.ss_family, + AC_DEFINE(HAVE_SS_FAMILY, 1, [Defined if struct sockaddr_storage has ss_family field]),, + [ +#include <sys/socket.h> +#include <sys/types.h> +#include <netinet/in.h> + ]) + +if test x"$ac_cv_member_struct_sockaddr_storage_ss_family" != x"yes"; then +AC_CHECK_MEMBER(struct sockaddr_storage.__ss_family, + AC_DEFINE(HAVE___SS_FAMILY, 1, [Defined if struct sockaddr_storage has __ss_family field]),, + [ +#include <sys/socket.h> +#include <sys/types.h> +#include <netinet/in.h> + ]) +fi +fi + AC_CHECK_FUNCS(seteuid setresuid setegid setresgid chroot bzero strerror) AC_CHECK_FUNCS(vsyslog setlinebuf mktime ftruncate chsize rename) AC_CHECK_FUNCS(waitpid strlcpy strlcat initgroups memmove strdup) diff --git a/source3/lib/replace/libreplace_cc.m4 b/source3/lib/replace/libreplace_cc.m4 index a01bf1b290..bf5056838d 100644 --- a/source3/lib/replace/libreplace_cc.m4 +++ b/source3/lib/replace/libreplace_cc.m4 @@ -48,8 +48,7 @@ LIBREPLACE_C99_STRUCT_INIT([],[AC_MSG_WARN([c99 structure initializer are not su AC_PROG_INSTALL AC_ISC_POSIX -AC_EXTENSION_FLAG(_XOPEN_SOURCE_EXTENDED) -AC_EXTENSION_FLAG(_OSF_SOURCE) +AC_N_DEFINE(_XOPEN_SOURCE_EXTENDED) AC_SYS_LARGEFILE @@ -77,6 +76,11 @@ case "$host_os" in CFLAGS="$CFLAGS -D_LINUX_SOURCE_COMPAT -qmaxmem=32000" fi ;; + *osf*) + # this brings in socklen_t + AC_N_DEFINE(_XOPEN_SOURCE,600) + AC_N_DEFINE(_OSF_SOURCE) + ;; # # VOS may need to have POSIX support and System V compatibility enabled. # diff --git a/source3/lib/replace/libreplace_ld.m4 b/source3/lib/replace/libreplace_ld.m4 index fd85ef9fc4..2aec698967 100644 --- a/source3/lib/replace/libreplace_ld.m4 +++ b/source3/lib/replace/libreplace_ld.m4 @@ -1,3 +1,23 @@ +# +# This offers a nice overview how to build shared libraries on all platforms +# http://www.fortran-2000.com/ArnaudRecipes/sharedlib.html +# + +AC_DEFUN([AC_LIBREPLACE_STLD], +[ + AC_PATH_PROG(PROG_AR, ar) + + STLD=${PROG_AR} + + AC_SUBST(STLD) +]) + +AC_DEFUN([AC_LIBREPLACE_STLD_FLAGS], +[ + STLD_FLAGS="-rcs" + AC_SUBST(STLD_FLAGS) +]) + AC_DEFUN([AC_LD_EXPORT_DYNAMIC], [ saved_LDFLAGS="$LDFLAGS" @@ -67,48 +87,102 @@ case "$host_os" in PICFLAG="-KPIC" ;; *darwin*) + PICFLAG="-fno-common" ;; esac AC_SUBST(PICFLAG) ]) -AC_DEFUN([AC_LD_SHLDFLAGS], +AC_DEFUN([AC_LIBREPLACE_LD_SHLIB_LINKER], +[ + LD_SHLIB_LINKER="${CC}" + + case "$host_os" in + *irix*) + LD_SHLIB_LINKER="${PROG_LD}" + ;; + esac + + AC_SUBST(LD_SHLIB_LINKER) +]) + +AC_DEFUN([AC_LIBREPLACE_LD_SHLIB_FLAGS], [ - SHLD_FLAGS="-shared" + LD_SHLIB_FLAGS="-shared" case "$host_os" in *linux*) - SHLD_FLAGS="-shared -Wl,-Bsymbolic" + LD_SHLIB_FLAGS="-shared -Wl,-Bsymbolic" ;; *solaris*) - SHLD_FLAGS="-G" + LD_SHLIB_FLAGS="-G" if test "${GCC}" = "no"; then ## ${CFLAGS} added for building 64-bit shared ## libs using Sun's Compiler - SHLD_FLAGS="-G \${CFLAGS}" + LD_SHLIB_FLAGS="-G \${CFLAGS}" fi ;; *sunos*) - SHLD_FLAGS="-G" + LD_SHLIB_FLAGS="-G" ;; *irix*) - SHLD_FLAGS="-shared" + LD_SHLIB_FLAGS="-shared" ;; *aix*) - SHLD_FLAGS="-Wl,-G,-bexpall,-bbigtoc" + LD_SHLIB_FLAGS="-Wl,-G,-bexpall,-bbigtoc" ;; *hpux*) if test "${GCC}" = "yes"; then - SHLD_FLAGS="-shared" + LD_SHLIB_FLAGS="-shared" else - SHLD_FLAGS="-b" + LD_SHLIB_FLAGS="-b" fi ;; + *osf*) + LD_SHLIB_FLAGS="-shared" + ;; *darwin*) - SHLD_FLAGS="-dynamiclib" + LD_SHLIB_FLAGS="-dynamiclib -Wl,-search_paths_first" ;; esac + AC_SUBST(LD_SHLIB_FLAGS) +]) + +AC_DEFUN([AC_LIBREPLACE_LD_SHLIB_DISALLOW_UNDEF_FLAG], +[ + LD_SHLIB_DISALLOW_UNDEF_FLAG="" + + # + # TODO: enforce error not only warnings + # + # NOTE: -Wl,--no-allow-shlib-undefined isn't what we want... + # as it bails out on broken system libraries + # + case "$host_os" in + *osf*) + LD_SHLIB_DISALLOW_UNDEF_FLAG="-warning_unresolved" + ;; + *darwin*) + LD_SHLIB_DISALLOW_UNDEF_FLAG="-undefined error" + ;; + esac + + AC_SUBST(LD_SHLIB_DISALLOW_UNDEF_FLAG) +]) + +AC_DEFUN([AC_LIBREPLACE_SHLD], +[ + AC_REQUIRE([AC_LIBREPLACE_LD_SHLIB_LINKER]) + SHLD="$LD_SHLIB_LINKER" + AC_SUBST(SHLD) +]) + +AC_DEFUN([AC_LIBREPLACE_SHLD_FLAGS], +[ + AC_REQUIRE([AC_LIBREPLACE_LD_SHLIB_FLAGS]) + AC_REQUIRE([AC_LIBREPLACE_LD_SHLIB_DISALLOW_UNDEF_FLAG]) + SHLD_FLAGS="$LD_SHLIB_FLAGS $LD_SHLIB_DISALLOW_UNDEF_FLAG" AC_SUBST(SHLD_FLAGS) ]) @@ -157,7 +231,7 @@ AC_DEFUN([AC_LD_SONAMEFLAG], SONAMEFLAG="-Wl,-soname," ;; *hpux*) - SONAMEFLAG="-Wl,+h " + SONAMEFLAG="-Wl,+h," ;; *osf*) SONAMEFLAG="-Wl,-soname," @@ -166,7 +240,74 @@ AC_DEFUN([AC_LD_SONAMEFLAG], SONAMEFLAG="-Wl,-soname," ;; *darwin*) - SONAMEFLAG="-install_name " + SONAMEFLAG="#" + ;; + *aix*) + # Not supported + SONAMEFLAG="#" ;; esac ]) + +AC_DEFUN([AC_LIBREPLACE_MDLD], +[ + AC_REQUIRE([AC_LIBREPLACE_LD_SHLIB_LINKER]) + MDLD="$LD_SHLIB_LINKER" + AC_SUBST(MDLD) +]) + +AC_DEFUN([AC_LIBREPLACE_LD_SHLIB_ALLOW_UNDEF_FLAG], +[ + LD_ALLOW_SHLIB_UNDEF_FLAG="" + + case "$host_os" in + *linux*) + LD_SHLIB_ALLOW_UNDEF_FLAG="-Wl,--allow-shlib-undefined" + ;; + *osf*) + LD_SHLIB_ALLOW_UNDEF_FLAG="-Wl,-expect_unresolved,\"*\"" + ;; + *darwin*) + LD_SHLIB_ALLOW_UNDEF_FLAG="-undefined dynamic_lookup" + ;; + esac + + AC_SUBST(LD_SHLIB_ALLOW_UNDEF_FLAG) +]) + +AC_DEFUN([AC_LIBREPLACE_MDLD_FLAGS], +[ + AC_REQUIRE([AC_LIBREPLACE_LD_SHLIB_FLAGS]) + AC_REQUIRE([AC_LIBREPLACE_LD_SHLIB_ALLOW_UNDEF_FLAG]) + MDLD_FLAGS="$LD_SHLIB_FLAGS $LD_SHLIB_ALLOW_UNDEF_FLAG" + AC_SUBST(MDLD_FLAGS) +]) + +AC_DEFUN([AC_LIBREPLACE_RUNTIME_LIB_PATH_VAR], +[ + case "$host_os" in + *linux*) + LIB_PATH_VAR=LD_LIBRARY_PATH + ;; + *solaris*) + LIB_PATH_VAR=LD_LIBRARY_PATH + ;; + *hpux*) + LIB_PATH_VAR=SHLIB_PATH + ;; + *osf*) + LIB_PATH_VAR=LD_LIBRARY_PATH + ;; + *aix*) + LIB_PATH_VAR=LIB_PATH + ;; + *irix*) + LIB_PATH_VAR=LD_LIBRARY_PATH + ;; + *darwin*) + LIB_PATH_VAR=DYLD_LIBRARY_PATH + ;; + esac + + AC_SUBST(LIB_PATH_VAR) +]) diff --git a/source3/lib/replace/libreplace_macros.m4 b/source3/lib/replace/libreplace_macros.m4 index 92fecd3db8..1856eacf66 100644 --- a/source3/lib/replace/libreplace_macros.m4 +++ b/source3/lib/replace/libreplace_macros.m4 @@ -87,19 +87,6 @@ fi rm -f conftest* ])]) -AC_DEFUN([AC_EXTENSION_FLAG], -[ - cat >>confdefs.h <<\EOF -#ifndef $1 -# define $1 1 -#endif -EOF -AH_VERBATIM([$1], [#ifndef $1 -# define $1 1 -#endif]) -]) - - dnl see if a declaration exists for a function or variable dnl defines HAVE_function_DECL if it exists dnl AC_HAVE_DECL(var, includes) @@ -248,11 +235,18 @@ m4_define([AH_CHECK_FUNC_EXT], dnl Define an AC_DEFINE with ifndef guard. dnl AC_N_DEFINE(VARIABLE [, VALUE]) -define(AC_N_DEFINE, -[cat >> confdefs.h <<\EOF -[#ifndef] $1 -[#define] $1 ifelse($#, 2, [$2], $#, 3, [$2], 1) -[#endif] +AC_DEFUN([AC_N_DEFINE], +[ +AH_VERBATIM([$1], [ +#ifndef $1 +# undef $1 +#endif +]) + + cat >>confdefs.h <<\EOF +#ifndef $1 +[#define] $1 m4_if($#, 1, 1, [$2]) +#endif EOF ]) diff --git a/source3/lib/replace/system/config.m4 b/source3/lib/replace/system/config.m4 index 799187af7d..1c05733126 100644 --- a/source3/lib/replace/system/config.m4 +++ b/source3/lib/replace/system/config.m4 @@ -73,6 +73,18 @@ AC_VERIFY_C_PROTOTYPE([struct passwd *getpwent_r(struct passwd *src, char *buf, #include <unistd.h> #include <pwd.h> ]) +AC_VERIFY_C_PROTOTYPE([struct passwd *getpwent_r(struct passwd *src, char *buf, size_t buflen)], + [ + #ifndef HAVE_GETPWENT_R_DECL + #error missing getpwent_r prototype + #endif + return NULL; + ],[ + AC_DEFINE(SOLARIS_GETPWENT_R, 1, [getpwent_r irix (similar to solaris) function prototype]) + ],[],[ + #include <unistd.h> + #include <pwd.h> + ]) AC_CHECK_FUNCS(getgrnam_r getgrgid_r getgrent_r) AC_HAVE_DECL(getgrent_r, [ #include <unistd.h> @@ -91,6 +103,19 @@ AC_VERIFY_C_PROTOTYPE([struct group *getgrent_r(struct group *src, char *buf, in #include <grp.h> ]) +AC_VERIFY_C_PROTOTYPE([struct group *getgrent_r(struct group *src, char *buf, size_t buflen)], + [ + #ifndef HAVE_GETGRENT_R_DECL + #error missing getgrent_r prototype + #endif + return NULL; + ],[ + AC_DEFINE(SOLARIS_GETGRENT_R, 1, [getgrent_r irix (similar to solaris) function prototype]) + ],[],[ + #include <unistd.h> + #include <grp.h> + ]) + # locale AC_CHECK_HEADERS(ctype.h locale.h) diff --git a/source3/lib/replace/system/network.h b/source3/lib/replace/system/network.h index d3ae2bf398..aff8a841da 100644 --- a/source3/lib/replace/system/network.h +++ b/source3/lib/replace/system/network.h @@ -78,6 +78,10 @@ #include <sys/ioctl.h> #endif +#ifdef HAVE_STROPTS_H +#include <stropts.h> +#endif + #ifdef REPLACE_INET_NTOA /* define is in "replace.h" */ char *rep_inet_ntoa(struct in_addr ip); @@ -133,8 +137,15 @@ const char *rep_inet_ntop(int af, const void *src, char *dst, socklen_t size); #endif #ifndef AI_ADDRCONFIG +/* + * logic copied from AI_NUMERICHOST + */ +#if defined(HAVE_STRUCT_ADDRINFO) && defined(HAVE_GETADDRINFO) +#define AI_ADDRCONFIG 0 +#else #define AI_ADDRCONFIG 0x0020 #endif +#endif #ifndef AI_NUMERICSERV /* @@ -223,9 +234,18 @@ typedef unsigned short int sa_family_t; #ifdef HAVE_STRUCT_SOCKADDR_IN6 #define sockaddr_storage sockaddr_in6 #define ss_family sin6_family +#define HAVE_SS_FAMILY 1 #else #define sockaddr_storage sockaddr_in #define ss_family sin_family +#define HAVE_SS_FAMILY 1 +#endif +#endif + +#ifndef HAVE_SS_FAMILY +#ifdef HAVE___SS_FAMILY +#define ss_family __ss_family +#define HAVE_SS_FAMILY 1 #endif #endif diff --git a/source3/lib/replace/system/printing.h b/source3/lib/replace/system/printing.h deleted file mode 100644 index 7eb02d004a..0000000000 --- a/source3/lib/replace/system/printing.h +++ /dev/null @@ -1,50 +0,0 @@ -#ifndef _system_printing_h -#define _system_printing_h - -/* - Unix SMB/CIFS implementation. - - printing system include wrappers - - Copyright (C) Andrew Tridgell 2004 - - ** NOTE! The following LGPL license applies to the replace - ** library. This does NOT imply that all of Samba is released - ** under the LGPL - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 3 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, see <http://www.gnu.org/licenses/>. - -*/ - -#ifdef AIX -#define DEFAULT_PRINTING PRINT_AIX -#define PRINTCAP_NAME "/etc/qconfig" -#endif - -#ifdef HPUX -#define DEFAULT_PRINTING PRINT_HPUX -#endif - -#ifdef QNX -#define DEFAULT_PRINTING PRINT_QNX -#endif - -#ifndef DEFAULT_PRINTING -#define DEFAULT_PRINTING PRINT_BSD -#endif -#ifndef PRINTCAP_NAME -#define PRINTCAP_NAME "/etc/printcap" -#endif - -#endif diff --git a/source3/lib/replace/test/testsuite.c b/source3/lib/replace/test/testsuite.c index 269a2ff5d6..5b95ae395c 100644 --- a/source3/lib/replace/test/testsuite.c +++ b/source3/lib/replace/test/testsuite.c @@ -37,7 +37,6 @@ #include "system/locale.h" #include "system/network.h" #include "system/passwd.h" -#include "system/printing.h" #include "system/readline.h" #include "system/select.h" #include "system/shmem.h" diff --git a/source3/lib/secace.c b/source3/lib/secace.c index 9e533a5a28..8760a6109a 100644 --- a/source3/lib/secace.c +++ b/source3/lib/secace.c @@ -54,11 +54,12 @@ void sec_ace_copy(SEC_ACE *ace_dest, SEC_ACE *ace_src) Sets up a SEC_ACE structure. ********************************************************************/ -void init_sec_ace(SEC_ACE *t, const DOM_SID *sid, uint8 type, uint32 mask, uint8 flag) +void init_sec_ace(SEC_ACE *t, const DOM_SID *sid, enum security_ace_type type, + uint32 mask, uint8 flag) { t->type = type; t->flags = flag; - t->size = sid_size(sid) + 8; + t->size = ndr_size_dom_sid(sid, 0) + 8; t->access_mask = mask; ZERO_STRUCTP(&t->trustee); @@ -83,9 +84,9 @@ NTSTATUS sec_ace_add_sid(TALLOC_CTX *ctx, SEC_ACE **pp_new, SEC_ACE *old, unsign for (i = 0; i < *num - 1; i ++) sec_ace_copy(&(*pp_new)[i], &old[i]); - (*pp_new)[i].type = 0; + (*pp_new)[i].type = SEC_ACE_TYPE_ACCESS_ALLOWED; (*pp_new)[i].flags = 0; - (*pp_new)[i].size = SEC_ACE_HEADER_SIZE + sid_size(sid); + (*pp_new)[i].size = SEC_ACE_HEADER_SIZE + ndr_size_dom_sid(sid, 0); (*pp_new)[i].access_mask = mask; sid_copy(&(*pp_new)[i].trustee, sid); return NT_STATUS_OK; diff --git a/source3/lib/secacl.c b/source3/lib/secacl.c index 328bc1b4b4..5e82242e1b 100644 --- a/source3/lib/secacl.c +++ b/source3/lib/secacl.c @@ -26,7 +26,8 @@ Create a SEC_ACL structure. ********************************************************************/ -SEC_ACL *make_sec_acl(TALLOC_CTX *ctx, uint16 revision, int num_aces, SEC_ACE *ace_list) +SEC_ACL *make_sec_acl(TALLOC_CTX *ctx, enum security_acl_revision revision, + int num_aces, SEC_ACE *ace_list) { SEC_ACL *dst; int i; diff --git a/source3/lib/secdesc.c b/source3/lib/secdesc.c index 4a9785009b..44ae23271e 100644 --- a/source3/lib/secdesc.c +++ b/source3/lib/secdesc.c @@ -32,35 +32,6 @@ const struct generic_mapping file_generic_mapping = { }; /******************************************************************* - Works out the linearization size of a SEC_DESC. -********************************************************************/ - -size_t sec_desc_size(SEC_DESC *psd) -{ - size_t offset; - - if (!psd) return 0; - - offset = SEC_DESC_HEADER_SIZE; - - /* don't align */ - - if (psd->owner_sid != NULL) - offset += sid_size(psd->owner_sid); - - if (psd->group_sid != NULL) - offset += sid_size(psd->group_sid); - - if (psd->sacl != NULL) - offset += psd->sacl->size; - - if (psd->dacl != NULL) - offset += psd->dacl->size; - - return offset; -} - -/******************************************************************* Compares two SEC_DESC structures ********************************************************************/ @@ -182,7 +153,9 @@ SEC_DESC_BUF *sec_desc_merge(TALLOC_CTX *ctx, SEC_DESC_BUF *new_sdb, SEC_DESC_BU Creates a SEC_DESC structure ********************************************************************/ -SEC_DESC *make_sec_desc(TALLOC_CTX *ctx, uint16 revision, uint16 type, +SEC_DESC *make_sec_desc(TALLOC_CTX *ctx, + enum security_descriptor_revision revision, + uint16 type, const DOM_SID *owner_sid, const DOM_SID *grp_sid, SEC_ACL *sacl, SEC_ACL *dacl, size_t *sd_size) { @@ -233,11 +206,11 @@ SEC_DESC *make_sec_desc(TALLOC_CTX *ctx, uint16 revision, uint16 type, } if (dst->owner_sid != NULL) { - offset += sid_size(dst->owner_sid); + offset += ndr_size_dom_sid(dst->owner_sid, 0); } if (dst->group_sid != NULL) { - offset += sid_size(dst->group_sid); + offset += ndr_size_dom_sid(dst->group_sid, 0); } *sd_size = (size_t)offset; @@ -272,25 +245,21 @@ NTSTATUS marshall_sec_desc(TALLOC_CTX *mem_ctx, struct security_descriptor *secdesc, uint8 **data, size_t *len) { - prs_struct ps; - - if (!prs_init(&ps, sec_desc_size(secdesc), mem_ctx, MARSHALL)) { - return NT_STATUS_NO_MEMORY; - } + DATA_BLOB blob; + enum ndr_err_code ndr_err; - if (!sec_io_desc("security_descriptor", &secdesc, &ps, 1)) { - prs_mem_free(&ps); - return NT_STATUS_INVALID_PARAMETER; - } + ndr_err = ndr_push_struct_blob( + &blob, mem_ctx, secdesc, + (ndr_push_flags_fn_t)ndr_push_security_descriptor); - if (!(*data = (uint8 *)talloc_memdup(mem_ctx, ps.data_p, - prs_offset(&ps)))) { - prs_mem_free(&ps); - return NT_STATUS_NO_MEMORY; + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + DEBUG(0, ("ndr_push_security_descriptor failed: %s\n", + ndr_errstr(ndr_err))); + return ndr_map_error2ntstatus(ndr_err);; } - *len = prs_offset(&ps); - prs_mem_free(&ps); + *data = blob.data; + *len = blob.length; return NT_STATUS_OK; } @@ -300,25 +269,33 @@ NTSTATUS marshall_sec_desc(TALLOC_CTX *mem_ctx, NTSTATUS unmarshall_sec_desc(TALLOC_CTX *mem_ctx, uint8 *data, size_t len, struct security_descriptor **psecdesc) { - prs_struct ps; - struct security_descriptor *secdesc = NULL; + DATA_BLOB blob; + enum ndr_err_code ndr_err; + struct security_descriptor *result; - if (!(secdesc = TALLOC_ZERO_P(mem_ctx, struct security_descriptor))) { - return NT_STATUS_NO_MEMORY; + if ((data == NULL) || (len == 0)) { + return NT_STATUS_INVALID_PARAMETER; } - if (!prs_init(&ps, 0, secdesc, UNMARSHALL)) { + result = TALLOC_ZERO_P(mem_ctx, struct security_descriptor); + if (result == NULL) { return NT_STATUS_NO_MEMORY; } - prs_give_memory(&ps, (char *)data, len, False); + blob = data_blob_const(data, len); - if (!sec_io_desc("security_descriptor", &secdesc, &ps, 1)) { - return NT_STATUS_INVALID_PARAMETER; + ndr_err = ndr_pull_struct_blob( + &blob, result, result, + (ndr_pull_flags_fn_t)ndr_pull_security_descriptor); + + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + DEBUG(0, ("ndr_pull_security_descriptor failed: %s\n", + ndr_errstr(ndr_err))); + TALLOC_FREE(result); + return ndr_map_error2ntstatus(ndr_err);; } - prs_mem_free(&ps); - *psecdesc = secdesc; + *psecdesc = result; return NT_STATUS_OK; } @@ -329,8 +306,9 @@ NTSTATUS unmarshall_sec_desc(TALLOC_CTX *mem_ctx, uint8 *data, size_t len, SEC_DESC *make_standard_sec_desc(TALLOC_CTX *ctx, const DOM_SID *owner_sid, const DOM_SID *grp_sid, SEC_ACL *dacl, size_t *sd_size) { - return make_sec_desc(ctx, SEC_DESC_REVISION, SEC_DESC_SELF_RELATIVE, - owner_sid, grp_sid, NULL, dacl, sd_size); + return make_sec_desc(ctx, SECURITY_DESCRIPTOR_REVISION_1, + SEC_DESC_SELF_RELATIVE, owner_sid, grp_sid, NULL, + dacl, sd_size); } /******************************************************************* @@ -557,7 +535,8 @@ SEC_DESC_BUF *se_create_child_secdesc(TALLOC_CTX *ctx, SEC_DESC *parent_ctr, correct. Perhaps the user and group should be passed in as parameters by the caller? */ - sd = make_sec_desc(ctx, SEC_DESC_REVISION, SEC_DESC_SELF_RELATIVE, + sd = make_sec_desc(ctx, SECURITY_DESCRIPTOR_REVISION_1, + SEC_DESC_SELF_RELATIVE, parent_ctr->owner_sid, parent_ctr->group_sid, parent_ctr->sacl, diff --git a/source3/lib/sharesec.c b/source3/lib/sharesec.c index b3b000579f..f6ff701d5b 100644 --- a/source3/lib/sharesec.c +++ b/source3/lib/sharesec.c @@ -92,7 +92,9 @@ SEC_DESC *get_share_security_default( TALLOC_CTX *ctx, size_t *psize, uint32 def init_sec_ace(&ace, &global_sid_World, SEC_ACE_TYPE_ACCESS_ALLOWED, sa, 0); if ((psa = make_sec_acl(ctx, NT4_ACL_REVISION, 1, &ace)) != NULL) { - psd = make_sec_desc(ctx, SEC_DESC_REVISION, SEC_DESC_SELF_RELATIVE, NULL, NULL, NULL, psa, psize); + psd = make_sec_desc(ctx, SECURITY_DESCRIPTOR_REVISION_1, + SEC_DESC_SELF_RELATIVE, NULL, NULL, NULL, + psa, psize); } if (!psd) { @@ -142,7 +144,7 @@ SEC_DESC *get_share_security( TALLOC_CTX *ctx, const char *servicename, } if (psd) - *psize = sec_desc_size(psd); + *psize = ndr_size_security_descriptor(psd, 0); return psd; } @@ -226,25 +228,20 @@ bool share_access_check(const NT_USER_TOKEN *token, const char *sharename, { uint32 granted; NTSTATUS status; - TALLOC_CTX *mem_ctx = NULL; SEC_DESC *psd = NULL; size_t sd_size; bool ret = True; - if (!(mem_ctx = talloc_init("share_access_check"))) { - return False; - } - - psd = get_share_security(mem_ctx, sharename, &sd_size); + psd = get_share_security(talloc_tos(), sharename, &sd_size); if (!psd) { - TALLOC_FREE(mem_ctx); return True; } ret = se_access_check(psd, token, desired_access, &granted, &status); - talloc_destroy(mem_ctx); + TALLOC_FREE(psd); + return ret; } @@ -291,7 +288,7 @@ bool parse_usershare_acl(TALLOC_CTX *ctx, const char *acl_str, SEC_DESC **ppsd) uint32 s_access; DOM_SID sid; char *sidstr; - uint8 type = SEC_ACE_TYPE_ACCESS_ALLOWED; + enum security_ace_type type = SEC_ACE_TYPE_ACCESS_ALLOWED; if (!next_token_talloc(ctx, &pacl, &sidstr, ":")) { DEBUG(0,("parse_usershare_acl: malformed usershare acl looking " @@ -339,7 +336,9 @@ bool parse_usershare_acl(TALLOC_CTX *ctx, const char *acl_str, SEC_DESC **ppsd) } if ((psa = make_sec_acl(ctx, NT4_ACL_REVISION, num_aces, ace_list)) != NULL) { - psd = make_sec_desc(ctx, SEC_DESC_REVISION, SEC_DESC_SELF_RELATIVE, NULL, NULL, NULL, psa, &sd_size); + psd = make_sec_desc(ctx, SECURITY_DESCRIPTOR_REVISION_1, + SEC_DESC_SELF_RELATIVE, NULL, NULL, NULL, + psa, &sd_size); } if (!psd) { diff --git a/source3/lib/socket_wrapper/socket_wrapper.c b/source3/lib/socket_wrapper/socket_wrapper.c index 3a72c5a74a..5b8052e5ed 100644 --- a/source3/lib/socket_wrapper/socket_wrapper.c +++ b/source3/lib/socket_wrapper/socket_wrapper.c @@ -571,8 +571,8 @@ static const char *socket_wrapper_pcap_file(void) { static int initialized = 0; static const char *s = NULL; - static const struct swrap_file_hdr h; - static const struct swrap_packet p; + static const struct swrap_file_hdr h = { 0, }; + static const struct swrap_packet p = { { 0, }, { { 0, }, { { 0, } } } }; if (initialized == 1) { return s; diff --git a/source3/lib/substitute.c b/source3/lib/substitute.c index e06917c8fb..59b54c4dff 100644 --- a/source3/lib/substitute.c +++ b/source3/lib/substitute.c @@ -34,6 +34,11 @@ fstring remote_proto="UNKNOWN"; static char *local_machine; +void free_local_machine_name(void) +{ + SAFE_FREE(local_machine); +} + bool set_local_machine_name(const char *local_name, bool perm) { static bool already_perm = false; @@ -408,7 +413,7 @@ static const char *automount_path(const char *user_name) /* use the passwd entry as the default */ /* this will be the default if WITH_AUTOMOUNT is not used or fails */ - server_path = talloc_strdup(ctx, get_user_home_dir(user_name)); + server_path = talloc_strdup(ctx, get_user_home_dir(ctx, user_name)); if (!server_path) { return ""; } @@ -541,7 +546,6 @@ char *alloc_sub_basic(const char *smb_name, const char *domain_name, { char *b, *p, *s, *r, *a_string; fstring pidstr, vnnstr; - struct passwd *pass; char addr[INET6_ADDRSTRLEN]; const char *local_machine_name = get_local_machine_name(); @@ -571,15 +575,21 @@ char *alloc_sub_basic(const char *smb_name, const char *domain_name, } a_string = realloc_string_sub(a_string, "%U", r); break; - case 'G' : + case 'G' : { + struct passwd *pass; r = SMB_STRDUP(smb_name); if (r == NULL) { goto error; } - if ((pass = Get_Pwnam(r))!=NULL) { - a_string = realloc_string_sub(a_string, "%G", gidtoname(pass->pw_gid)); - } + pass = Get_Pwnam_alloc(talloc_tos(), r); + if (pass != NULL) { + a_string = realloc_string_sub( + a_string, "%G", + gidtoname(pass->pw_gid)); + } + TALLOC_FREE(pass); break; + } case 'D' : r = strdup_upper(domain_name); if (r == NULL) { @@ -766,7 +776,7 @@ static char *alloc_sub_advanced(const char *servicename, const char *user, const char *str) { char *a_string, *ret_string; - char *b, *p, *s, *h; + char *b, *p, *s; a_string = SMB_STRDUP(str); if (a_string == NULL) { @@ -782,10 +792,13 @@ static char *alloc_sub_advanced(const char *servicename, const char *user, case 'N' : a_string = realloc_string_sub(a_string, "%N", automount_server(user)); break; - case 'H': - if ((h = get_user_home_dir(user))) + case 'H': { + char *h; + if ((h = get_user_home_dir(talloc_tos(), user))) a_string = realloc_string_sub(a_string, "%H", h); + TALLOC_FREE(h); break; + } case 'P': a_string = realloc_string_sub(a_string, "%P", connectpath); break; diff --git a/source3/lib/sysacls.c b/source3/lib/sysacls.c index 3e61f42aa5..9c1256ed65 100644 --- a/source3/lib/sysacls.c +++ b/source3/lib/sysacls.c @@ -3,7 +3,7 @@ Samba system utilities for ACL support. Copyright (C) Jeremy Allison 2000. Copyright (C) Volker Lendecke 2006 - Copyright (C) Michael Adam 2006 + Copyright (C) Michael Adam 2006,2008 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 @@ -364,9 +364,9 @@ SMB_ACL_T sys_acl_get_file(vfs_handle_struct *handle, return posixacl_sys_acl_get_file(handle, path_p, type); } -SMB_ACL_T sys_acl_get_fd(vfs_handle_struct *handle, files_struct *fsp, int fd) +SMB_ACL_T sys_acl_get_fd(vfs_handle_struct *handle, files_struct *fsp) { - return posixacl_sys_acl_get_fd(handle, fsp, fd); + return posixacl_sys_acl_get_fd(handle, fsp); } int sys_acl_set_file(vfs_handle_struct *handle, @@ -376,9 +376,9 @@ int sys_acl_set_file(vfs_handle_struct *handle, } int sys_acl_set_fd(vfs_handle_struct *handle, files_struct *fsp, - int fd, SMB_ACL_T acl_d) + SMB_ACL_T acl_d) { - return posixacl_sys_acl_set_fd(handle, fsp, fd, acl_d); + return posixacl_sys_acl_set_fd(handle, fsp, acl_d); } int sys_acl_delete_def_file(vfs_handle_struct *handle, @@ -395,9 +395,9 @@ SMB_ACL_T sys_acl_get_file(vfs_handle_struct *handle, return aixacl_sys_acl_get_file(handle, path_p, type); } -SMB_ACL_T sys_acl_get_fd(vfs_handle_struct *handle, files_struct *fsp, int fd) +SMB_ACL_T sys_acl_get_fd(vfs_handle_struct *handle, files_struct *fsp) { - return aixacl_sys_acl_get_fd(handle, fsp, fd); + return aixacl_sys_acl_get_fd(handle, fsp); } int sys_acl_set_file(vfs_handle_struct *handle, @@ -407,9 +407,9 @@ int sys_acl_set_file(vfs_handle_struct *handle, } int sys_acl_set_fd(vfs_handle_struct *handle, files_struct *fsp, - int fd, SMB_ACL_T acl_d) + SMB_ACL_T acl_d) { - return aixacl_sys_acl_set_fd(handle, fsp, fd, acl_d); + return aixacl_sys_acl_set_fd(handle, fsp, acl_d); } int sys_acl_delete_def_file(vfs_handle_struct *handle, @@ -426,9 +426,9 @@ SMB_ACL_T sys_acl_get_file(vfs_handle_struct *handle, return tru64acl_sys_acl_get_file(handle, path_p, type); } -SMB_ACL_T sys_acl_get_fd(vfs_handle_struct *handle, files_struct *fsp, int fd) +SMB_ACL_T sys_acl_get_fd(vfs_handle_struct *handle, files_struct *fsp) { - return tru64acl_sys_acl_get_fd(handle, fsp, fd); + return tru64acl_sys_acl_get_fd(handle, fsp); } int sys_acl_set_file(vfs_handle_struct *handle, @@ -438,9 +438,9 @@ int sys_acl_set_file(vfs_handle_struct *handle, } int sys_acl_set_fd(vfs_handle_struct *handle, files_struct *fsp, - int fd, SMB_ACL_T acl_d) + SMB_ACL_T acl_d) { - return tru64acl_sys_acl_set_fd(handle, fsp, fd, acl_d); + return tru64acl_sys_acl_set_fd(handle, fsp, acl_d); } int sys_acl_delete_def_file(vfs_handle_struct *handle, @@ -457,9 +457,9 @@ SMB_ACL_T sys_acl_get_file(vfs_handle_struct *handle, return solarisacl_sys_acl_get_file(handle, path_p, type); } -SMB_ACL_T sys_acl_get_fd(vfs_handle_struct *handle, files_struct *fsp, int fd) +SMB_ACL_T sys_acl_get_fd(vfs_handle_struct *handle, files_struct *fsp) { - return solarisacl_sys_acl_get_fd(handle, fsp, fd); + return solarisacl_sys_acl_get_fd(handle, fsp); } int sys_acl_set_file(vfs_handle_struct *handle, @@ -469,9 +469,9 @@ int sys_acl_set_file(vfs_handle_struct *handle, } int sys_acl_set_fd(vfs_handle_struct *handle, files_struct *fsp, - int fd, SMB_ACL_T acl_d) + SMB_ACL_T acl_d) { - return solarisacl_sys_acl_set_fd(handle, fsp, fd, acl_d); + return solarisacl_sys_acl_set_fd(handle, fsp, acl_d); } int sys_acl_delete_def_file(vfs_handle_struct *handle, @@ -488,9 +488,9 @@ SMB_ACL_T sys_acl_get_file(vfs_handle_struct *handle, return hpuxacl_sys_acl_get_file(handle, path_p, type); } -SMB_ACL_T sys_acl_get_fd(vfs_handle_struct *handle, files_struct *fsp, int fd) +SMB_ACL_T sys_acl_get_fd(vfs_handle_struct *handle, files_struct *fsp) { - return hpuxacl_sys_acl_get_fd(handle, fsp, fd); + return hpuxacl_sys_acl_get_fd(handle, fsp); } int sys_acl_set_file(vfs_handle_struct *handle, @@ -500,9 +500,9 @@ int sys_acl_set_file(vfs_handle_struct *handle, } int sys_acl_set_fd(vfs_handle_struct *handle, files_struct *fsp, - int fd, SMB_ACL_T acl_d) + SMB_ACL_T acl_d) { - return hpuxacl_sys_acl_set_fd(handle, fsp, fd, acl_d); + return hpuxacl_sys_acl_set_fd(handle, fsp, acl_d); } int sys_acl_delete_def_file(vfs_handle_struct *handle, @@ -519,9 +519,9 @@ SMB_ACL_T sys_acl_get_file(vfs_handle_struct *handle, return irixacl_sys_acl_get_file(handle, path_p, type); } -SMB_ACL_T sys_acl_get_fd(vfs_handle_struct *handle, files_struct *fsp, int fd) +SMB_ACL_T sys_acl_get_fd(vfs_handle_struct *handle, files_struct *fsp) { - return irixacl_sys_acl_get_fd(handle, fsp, fd); + return irixacl_sys_acl_get_fd(handle, fsp); } int sys_acl_set_file(vfs_handle_struct *handle, @@ -531,9 +531,9 @@ int sys_acl_set_file(vfs_handle_struct *handle, } int sys_acl_set_fd(vfs_handle_struct *handle, files_struct *fsp, - int fd, SMB_ACL_T acl_d) + SMB_ACL_T acl_d) { - return irixacl_sys_acl_set_fd(handle, fsp, fd, acl_d); + return irixacl_sys_acl_set_fd(handle, fsp, acl_d); } int sys_acl_delete_def_file(vfs_handle_struct *handle, @@ -555,7 +555,7 @@ SMB_ACL_T sys_acl_get_file(vfs_handle_struct *handle, return NULL; } -SMB_ACL_T sys_acl_get_fd(vfs_handle_struct *handle, files_struct *fsp, int fd) +SMB_ACL_T sys_acl_get_fd(vfs_handle_struct *handle, files_struct *fsp) { #ifdef ENOTSUP errno = ENOTSUP; @@ -577,7 +577,7 @@ int sys_acl_set_file(vfs_handle_struct *handle, } int sys_acl_set_fd(vfs_handle_struct *handle, files_struct *fsp, - int fd, SMB_ACL_T acl_d) + SMB_ACL_T acl_d) { #ifdef ENOTSUP errno = ENOTSUP; diff --git a/source3/lib/system.c b/source3/lib/system.c index eb6dcae6fb..fa50955ef6 100644 --- a/source3/lib/system.c +++ b/source3/lib/system.c @@ -1917,11 +1917,6 @@ int sys_fremovexattr (int filedes, const char *name) #endif } -#if !defined(HAVE_SETXATTR) -#define XATTR_CREATE 0x1 /* set value, fail if attr already exists */ -#define XATTR_REPLACE 0x2 /* set value, fail if attr does not exist */ -#endif - int sys_setxattr (const char *path, const char *name, const void *value, size_t size, int flags) { #if defined(HAVE_SETXATTR) diff --git a/source3/lib/talloc/talloc.c b/source3/lib/talloc/talloc.c index 4d72c0e871..9dcd8a2a83 100644 --- a/source3/lib/talloc/talloc.c +++ b/source3/lib/talloc/talloc.c @@ -60,6 +60,8 @@ #define TALLOC_MAGIC 0xe814ec70 #define TALLOC_FLAG_FREE 0x01 #define TALLOC_FLAG_LOOP 0x02 +#define TALLOC_FLAG_POOL 0x04 /* This is a talloc pool */ +#define TALLOC_FLAG_POOLMEM 0x08 /* This is allocated in a pool */ #define TALLOC_MAGIC_REFERENCE ((const char *)1) /* by default we abort when given a bad pointer (such as when talloc_free() is called @@ -109,6 +111,19 @@ struct talloc_chunk { const char *name; size_t size; unsigned flags; + + /* + * "pool" has dual use: + * + * For the talloc pool itself (i.e. TALLOC_FLAG_POOL is set), "pool" + * marks the end of the currently allocated area. + * + * For members of the pool (i.e. TALLOC_FLAG_POOLMEM is set), "pool" + * is a pointer to the struct talloc_chunk of the pool that it was + * allocated from. This way children can quickly find the pool to chew + * from. + */ + void *pool; }; /* 16 byte alignment seems to keep everyone happy */ @@ -200,12 +215,87 @@ const char *talloc_parent_name(const void *ptr) return tc? tc->name : NULL; } +/* + A pool carries an in-pool object count count in the first 16 bytes. + bytes. This is done to support talloc_steal() to a parent outside of the + pool. The count includes the pool itself, so a talloc_free() on a pool will + only destroy the pool if the count has dropped to zero. A talloc_free() of a + pool member will reduce the count, and eventually also call free(3) on the + pool memory. + + The object count is not put into "struct talloc_chunk" because it is only + relevant for talloc pools and the alignment to 16 bytes would increase the + memory footprint of each talloc chunk by those 16 bytes. +*/ + +#define TALLOC_POOL_HDR_SIZE 16 + +static unsigned int *talloc_pool_objectcount(struct talloc_chunk *tc) +{ + return (unsigned int *)((char *)tc + sizeof(struct talloc_chunk)); +} + +/* + Allocate from a pool +*/ + +static struct talloc_chunk *talloc_alloc_pool(struct talloc_chunk *parent, + size_t size) +{ + struct talloc_chunk *pool_ctx = NULL; + size_t space_left; + struct talloc_chunk *result; + size_t chunk_size; + + if (parent == NULL) { + return NULL; + } + + if (parent->flags & TALLOC_FLAG_POOL) { + pool_ctx = parent; + } + else if (parent->flags & TALLOC_FLAG_POOLMEM) { + pool_ctx = (struct talloc_chunk *)parent->pool; + } + + if (pool_ctx == NULL) { + return NULL; + } + + space_left = ((char *)pool_ctx + TC_HDR_SIZE + pool_ctx->size) + - ((char *)pool_ctx->pool); + + /* + * Align size to 16 bytes + */ + chunk_size = ((size + 15) & ~15); + + if (space_left < chunk_size) { + return NULL; + } + + result = (struct talloc_chunk *)pool_ctx->pool; + +#if defined(DEVELOPER) && defined(VALGRIND_MAKE_MEM_UNDEFINED) + VALGRIND_MAKE_MEM_UNDEFINED(result, size); +#endif + + pool_ctx->pool = (void *)((char *)result + chunk_size); + + result->flags = TALLOC_MAGIC | TALLOC_FLAG_POOLMEM; + result->pool = pool_ctx; + + *talloc_pool_objectcount(pool_ctx) += 1; + + return result; +} + /* Allocate a bit of memory as a child of an existing pointer */ static inline void *__talloc(const void *context, size_t size) { - struct talloc_chunk *tc; + struct talloc_chunk *tc = NULL; if (unlikely(context == NULL)) { context = null_context; @@ -215,11 +305,19 @@ static inline void *__talloc(const void *context, size_t size) return NULL; } - tc = (struct talloc_chunk *)malloc(TC_HDR_SIZE+size); - if (unlikely(tc == NULL)) return NULL; + if (context != NULL) { + tc = talloc_alloc_pool(talloc_chunk_from_ptr(context), + TC_HDR_SIZE+size); + } + + if (tc == NULL) { + tc = (struct talloc_chunk *)malloc(TC_HDR_SIZE+size); + if (unlikely(tc == NULL)) return NULL; + tc->flags = TALLOC_MAGIC; + tc->pool = NULL; + } tc->size = size; - tc->flags = TALLOC_MAGIC; tc->destructor = NULL; tc->child = NULL; tc->name = NULL; @@ -246,6 +344,33 @@ static inline void *__talloc(const void *context, size_t size) } /* + * Create a talloc pool + */ + +void *talloc_pool(const void *context, size_t size) +{ + void *result = __talloc(context, size + TALLOC_POOL_HDR_SIZE); + struct talloc_chunk *tc; + + if (unlikely(result == NULL)) { + return NULL; + } + + tc = talloc_chunk_from_ptr(result); + + tc->flags |= TALLOC_FLAG_POOL; + tc->pool = (char *)result + TALLOC_POOL_HDR_SIZE; + + *talloc_pool_objectcount(tc) = 1; + +#if defined(DEVELOPER) && defined(VALGRIND_MAKE_MEM_NOACCESS) + VALGRIND_MAKE_MEM_NOACCESS(tc->pool, size); +#endif + + return result; +} + +/* setup a destructor to be called on free of a pointer the destructor should return 0 on success, or -1 on failure. if the destructor fails then the free is failed, and the memory can @@ -420,7 +545,29 @@ static inline int _talloc_free(void *ptr) } tc->flags |= TALLOC_FLAG_FREE; - free(tc); + + if (tc->flags & (TALLOC_FLAG_POOL|TALLOC_FLAG_POOLMEM)) { + struct talloc_chunk *pool; + unsigned int *pool_object_count; + + pool = (tc->flags & TALLOC_FLAG_POOL) + ? tc : (struct talloc_chunk *)tc->pool; + + pool_object_count = talloc_pool_objectcount(pool); + + if (*pool_object_count == 0) { + TALLOC_ABORT("Pool object count zero!"); + } + + *pool_object_count -= 1; + + if (*pool_object_count == 0) { + free(pool); + } + } + else { + free(tc); + } return 0; } @@ -718,6 +865,15 @@ void talloc_free_children(void *ptr) talloc_steal(new_parent, child); } } + + if ((tc->flags & TALLOC_FLAG_POOL) + && (*talloc_pool_objectcount(tc) == 1)) { + tc->pool = ((char *)tc + TC_HDR_SIZE + TALLOC_POOL_HDR_SIZE); +#if defined(DEVELOPER) && defined(VALGRIND_MAKE_MEM_NOACCESS) + VALGRIND_MAKE_MEM_NOACCESS( + tc->pool, tc->size - TALLOC_POOL_HDR_SIZE); +#endif + } } /* @@ -769,6 +925,7 @@ void *_talloc_realloc(const void *context, void *ptr, size_t size, const char *n { struct talloc_chunk *tc; void *new_ptr; + bool malloced = false; /* size zero is equivalent to free() */ if (unlikely(size == 0)) { @@ -792,6 +949,12 @@ void *_talloc_realloc(const void *context, void *ptr, size_t size, const char *n return NULL; } + /* don't shrink if we have less than 1k to gain */ + if ((size < tc->size) && ((tc->size - size) < 1024)) { + tc->size = size; + return ptr; + } + /* by resetting magic we catch users of the old memory */ tc->flags |= TALLOC_FLAG_FREE; @@ -802,7 +965,24 @@ void *_talloc_realloc(const void *context, void *ptr, size_t size, const char *n free(tc); } #else - new_ptr = realloc(tc, size + TC_HDR_SIZE); + if (tc->flags & TALLOC_FLAG_POOLMEM) { + + new_ptr = talloc_alloc_pool(tc, size + TC_HDR_SIZE); + *talloc_pool_objectcount((struct talloc_chunk *) + (tc->pool)) -= 1; + + if (new_ptr == NULL) { + new_ptr = malloc(TC_HDR_SIZE+size); + malloced = true; + } + + if (new_ptr) { + memcpy(new_ptr, tc, MIN(tc->size,size) + TC_HDR_SIZE); + } + } + else { + new_ptr = realloc(tc, size + TC_HDR_SIZE); + } #endif if (unlikely(!new_ptr)) { tc->flags &= ~TALLOC_FLAG_FREE; @@ -810,7 +990,10 @@ void *_talloc_realloc(const void *context, void *ptr, size_t size, const char *n } tc = (struct talloc_chunk *)new_ptr; - tc->flags &= ~TALLOC_FLAG_FREE; + tc->flags &= ~TALLOC_FLAG_FREE; + if (malloced) { + tc->flags &= ~TALLOC_FLAG_POOLMEM; + } if (tc->parent) { tc->parent->child = tc; } diff --git a/source3/lib/talloc/talloc.h b/source3/lib/talloc/talloc.h index e103391681..5431971655 100644 --- a/source3/lib/talloc/talloc.h +++ b/source3/lib/talloc/talloc.h @@ -116,6 +116,7 @@ typedef void TALLOC_CTX; /* The following definitions come from talloc.c */ void *_talloc(const void *context, size_t size); +void *talloc_pool(const void *context, size_t size); void _talloc_set_destructor(const void *ptr, int (*destructor)(void *)); int talloc_increase_ref_count(const void *ptr); size_t talloc_reference_count(const void *ptr); diff --git a/source3/lib/talloc/testsuite.c b/source3/lib/talloc/testsuite.c index e16c91f8b9..fedbda95aa 100644 --- a/source3/lib/talloc/testsuite.c +++ b/source3/lib/talloc/testsuite.c @@ -813,6 +813,25 @@ static bool test_speed(void) talloc_free(ctx); + ctx = talloc_pool(NULL, 1024); + + tv = timeval_current(); + count = 0; + do { + void *p1, *p2, *p3; + for (i=0;i<loop;i++) { + p1 = talloc_size(ctx, loop % 100); + p2 = talloc_strdup(p1, "foo bar"); + p3 = talloc_size(p1, 300); + talloc_free_children(ctx); + } + count += 3 * loop; + } while (timeval_elapsed(&tv) < 5.0); + + talloc_free(ctx); + + fprintf(stderr, "talloc_pool: %.0f ops/sec\n", count/timeval_elapsed(&tv)); + tv = timeval_current(); count = 0; do { @@ -1066,6 +1085,23 @@ static bool test_autofree(void) return true; } +static bool test_pool(void) +{ + void *pool; + void *p1, *p2, *p3, *p4; + + pool = talloc_pool(NULL, 1024); + + p1 = talloc_size(pool, 80); + p2 = talloc_size(pool, 20); + p3 = talloc_size(p1, 50); + p4 = talloc_size(p3, 1000); + + talloc_free(pool); + + return true; +} + struct torture_context; bool torture_local_talloc(struct torture_context *tctx) { @@ -1094,6 +1130,7 @@ bool torture_local_talloc(struct torture_context *tctx) ret &= test_free_parent_deny_child(); ret &= test_talloc_ptrtype(); ret &= test_talloc_free_in_destructor(); + ret &= test_pool(); if (ret) { ret &= test_speed(); diff --git a/source3/lib/talloc_stack.c b/source3/lib/talloc_stack.c index e6e4ed321a..2722fb9676 100644 --- a/source3/lib/talloc_stack.c +++ b/source3/lib/talloc_stack.c @@ -39,18 +39,21 @@ #include "includes.h" static int talloc_stacksize; +static int talloc_stack_arraysize; static TALLOC_CTX **talloc_stack; -static int talloc_pop(int *ptr) +static int talloc_pop(TALLOC_CTX *frame) { - int tos = *ptr; int i; - for (i=talloc_stacksize-1; i>=tos; i--) { + for (i=talloc_stacksize-1; i>0; i--) { + if (frame == talloc_stack[i]) { + break; + } talloc_free(talloc_stack[i]); } - talloc_stacksize = tos; + talloc_stacksize = i; return 0; } @@ -61,31 +64,40 @@ static int talloc_pop(int *ptr) * not explicitly freed. */ -TALLOC_CTX *talloc_stackframe(void) +static TALLOC_CTX *talloc_stackframe_internal(size_t poolsize) { - TALLOC_CTX **tmp, *top; - int *cleanup; + TALLOC_CTX **tmp, *top, *parent; + + if (talloc_stack_arraysize < talloc_stacksize + 1) { + tmp = TALLOC_REALLOC_ARRAY(NULL, talloc_stack, TALLOC_CTX *, + talloc_stacksize + 1); + if (tmp == NULL) { + goto fail; + } + talloc_stack = tmp; + talloc_stack_arraysize = talloc_stacksize + 1; + } - if (!(tmp = TALLOC_REALLOC_ARRAY(NULL, talloc_stack, TALLOC_CTX *, - talloc_stacksize + 1))) { - goto fail; + if (talloc_stacksize == 0) { + parent = talloc_stack; + } + else { + parent = talloc_stack[talloc_stacksize-1]; } - talloc_stack = tmp; - - if (!(top = talloc_new(talloc_stack))) { - goto fail; + if (poolsize) { + top = talloc_pool(parent, poolsize); + } else { + top = talloc_new(parent); } - if (!(cleanup = talloc(top, int))) { + if (top == NULL) { goto fail; } - *cleanup = talloc_stacksize; - talloc_set_destructor(cleanup, talloc_pop); + talloc_set_destructor(top, talloc_pop); talloc_stack[talloc_stacksize++] = top; - return top; fail: @@ -93,6 +105,16 @@ TALLOC_CTX *talloc_stackframe(void) return NULL; } +TALLOC_CTX *talloc_stackframe(void) +{ + return talloc_stackframe_internal(0); +} + +TALLOC_CTX *talloc_stackframe_pool(size_t poolsize) +{ + return talloc_stackframe_internal(poolsize); +} + /* * Get us the current top of the talloc stack. */ diff --git a/source3/lib/tdb/common/freelist.c b/source3/lib/tdb/common/freelist.c index b109643f23..2f2a4c379b 100644 --- a/source3/lib/tdb/common/freelist.c +++ b/source3/lib/tdb/common/freelist.c @@ -27,6 +27,12 @@ #include "tdb_private.h" +/* 'right' merges can involve O(n^2) cost when combined with a + traverse, so they are disabled until we find a way to do them in + O(1) time +*/ +#define USE_RIGHT_MERGES 0 + /* read a freelist record and check for simple errors */ int tdb_rec_free_read(struct tdb_context *tdb, tdb_off_t off, struct list_struct *rec) { @@ -56,7 +62,7 @@ int tdb_rec_free_read(struct tdb_context *tdb, tdb_off_t off, struct list_struct } - +#if USE_RIGHT_MERGES /* Remove an element from the freelist. Must have alloc lock. */ static int remove_from_freelist(struct tdb_context *tdb, tdb_off_t off, tdb_off_t next) { @@ -75,6 +81,7 @@ static int remove_from_freelist(struct tdb_context *tdb, tdb_off_t off, tdb_off_ TDB_LOG((tdb, TDB_DEBUG_FATAL,"remove_from_freelist: not on list at off=%d\n", off)); return TDB_ERRCODE(TDB_ERR_CORRUPT, -1); } +#endif /* update a record tailer (must hold allocation lock) */ @@ -93,8 +100,6 @@ static int update_tailer(struct tdb_context *tdb, tdb_off_t offset, neccessary. */ int tdb_free(struct tdb_context *tdb, tdb_off_t offset, struct list_struct *rec) { - tdb_off_t right, left; - /* Allocation and tailer lock */ if (tdb_lock(tdb, -1, F_WRLCK) != 0) return -1; @@ -105,9 +110,10 @@ int tdb_free(struct tdb_context *tdb, tdb_off_t offset, struct list_struct *rec) goto fail; } +#if USE_RIGHT_MERGES /* Look right first (I'm an Australian, dammit) */ - right = offset + sizeof(*rec) + rec->rec_len; - if (right + sizeof(*rec) <= tdb->map_size) { + if (offset + sizeof(*rec) + rec->rec_len + sizeof(*rec) <= tdb->map_size) { + tdb_off_t right = offset + sizeof(*rec) + rec->rec_len; struct list_struct r; if (tdb->methods->tdb_read(tdb, right, &r, sizeof(r), DOCONV()) == -1) { @@ -122,13 +128,18 @@ int tdb_free(struct tdb_context *tdb, tdb_off_t offset, struct list_struct *rec) goto left; } rec->rec_len += sizeof(r) + r.rec_len; + if (update_tailer(tdb, offset, rec) == -1) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_free: update_tailer failed at %u\n", offset)); + goto fail; + } } } - left: +#endif + /* Look left */ - left = offset - sizeof(tdb_off_t); - if (left > TDB_DATA_START(tdb->header.hash_size)) { + if (offset - sizeof(tdb_off_t) > TDB_DATA_START(tdb->header.hash_size)) { + tdb_off_t left = offset - sizeof(tdb_off_t); struct list_struct l; tdb_off_t leftsize; @@ -145,7 +156,12 @@ left: left = offset - leftsize; - /* Now read in record */ + if (leftsize > offset || + left < TDB_DATA_START(tdb->header.hash_size)) { + goto update; + } + + /* Now read in the left record */ if (tdb->methods->tdb_read(tdb, left, &l, sizeof(l), DOCONV()) == -1) { TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_free: left read failed at %u (%u)\n", left, leftsize)); goto update; @@ -153,21 +169,24 @@ left: /* If it's free, expand to include it. */ if (l.magic == TDB_FREE_MAGIC) { - if (remove_from_freelist(tdb, left, l.next) == -1) { - TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_free: left free failed at %u\n", left)); - goto update; - } else { - offset = left; - rec->rec_len += leftsize; + /* we now merge the new record into the left record, rather than the other + way around. This makes the operation O(1) instead of O(n). This change + prevents traverse from being O(n^2) after a lot of deletes */ + l.rec_len += sizeof(*rec) + rec->rec_len; + if (tdb_rec_write(tdb, left, &l) == -1) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_free: update_left failed at %u\n", left)); + goto fail; + } + if (update_tailer(tdb, left, &l) == -1) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_free: update_tailer failed at %u\n", offset)); + goto fail; } + tdb_unlock(tdb, -1, F_WRLCK); + return 0; } } update: - if (update_tailer(tdb, offset, rec) == -1) { - TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_free: update_tailer failed at %u\n", offset)); - goto fail; - } /* Now, prepend to free list */ rec->magic = TDB_FREE_MAGIC; @@ -189,62 +208,61 @@ update: } + /* the core of tdb_allocate - called when we have decided which free list entry to use + + Note that we try to allocate by grabbing data from the end of an existing record, + not the beginning. This is so the left merge in a free is more likely to be + able to free up the record without fragmentation */ -static tdb_off_t tdb_allocate_ofs(struct tdb_context *tdb, tdb_len_t length, tdb_off_t rec_ptr, - struct list_struct *rec, tdb_off_t last_ptr) +static tdb_off_t tdb_allocate_ofs(struct tdb_context *tdb, + tdb_len_t length, tdb_off_t rec_ptr, + struct list_struct *rec, tdb_off_t last_ptr) { - struct list_struct newrec; - tdb_off_t newrec_ptr; +#define MIN_REC_SIZE (sizeof(struct list_struct) + sizeof(tdb_off_t) + 8) - memset(&newrec, '\0', sizeof(newrec)); + if (rec->rec_len < length + MIN_REC_SIZE) { + /* we have to grab the whole record */ - /* found it - now possibly split it up */ - if (rec->rec_len > length + MIN_REC_SIZE) { - /* Length of left piece */ - length = TDB_ALIGN(length, TDB_ALIGNMENT); - - /* Right piece to go on free list */ - newrec.rec_len = rec->rec_len - (sizeof(*rec) + length); - newrec_ptr = rec_ptr + sizeof(*rec) + length; - - /* And left record is shortened */ - rec->rec_len = length; - } else { - newrec_ptr = 0; + /* unlink it from the previous record */ + if (tdb_ofs_write(tdb, last_ptr, &rec->next) == -1) { + return 0; + } + + /* mark it not free */ + rec->magic = TDB_MAGIC; + if (tdb_rec_write(tdb, rec_ptr, rec) == -1) { + return 0; + } + return rec_ptr; + } + + /* we're going to just shorten the existing record */ + rec->rec_len -= (length + sizeof(*rec)); + if (tdb_rec_write(tdb, rec_ptr, rec) == -1) { + return 0; } - - /* Remove allocated record from the free list */ - if (tdb_ofs_write(tdb, last_ptr, &rec->next) == -1) { + if (update_tailer(tdb, rec_ptr, rec) == -1) { return 0; } - - /* Update header: do this before we drop alloc - lock, otherwise tdb_free() might try to - merge with us, thinking we're free. - (Thanks Jeremy Allison). */ + + /* and setup the new record */ + rec_ptr += sizeof(*rec) + rec->rec_len; + + memset(rec, '\0', sizeof(*rec)); + rec->rec_len = length; rec->magic = TDB_MAGIC; + if (tdb_rec_write(tdb, rec_ptr, rec) == -1) { return 0; } - - /* Did we create new block? */ - if (newrec_ptr) { - /* Update allocated record tailer (we - shortened it). */ - if (update_tailer(tdb, rec_ptr, rec) == -1) { - return 0; - } - - /* Free new record */ - if (tdb_free(tdb, newrec_ptr, &newrec) == -1) { - return 0; - } + + if (update_tailer(tdb, rec_ptr, rec) == -1) { + return 0; } - - /* all done - return the new record offset */ + return rec_ptr; } @@ -261,12 +279,14 @@ tdb_off_t tdb_allocate(struct tdb_context *tdb, tdb_len_t length, struct list_st tdb_off_t rec_ptr, last_ptr; tdb_len_t rec_len; } bestfit; + float multiplier = 1.0; if (tdb_lock(tdb, -1, F_WRLCK) == -1) return 0; /* Extra bytes required for tailer */ length += sizeof(tdb_off_t); + length = TDB_ALIGN(length, TDB_ALIGNMENT); again: last_ptr = FREELIST_TOP; @@ -295,18 +315,27 @@ tdb_off_t tdb_allocate(struct tdb_context *tdb, tdb_len_t length, struct list_st bestfit.rec_len = rec->rec_len; bestfit.rec_ptr = rec_ptr; bestfit.last_ptr = last_ptr; - /* consider a fit to be good enough if - we aren't wasting more than half - the space */ - if (bestfit.rec_len < 2*length) { - break; - } } } /* move to the next record */ last_ptr = rec_ptr; rec_ptr = rec->next; + + /* if we've found a record that is big enough, then + stop searching if its also not too big. The + definition of 'too big' changes as we scan + through */ + if (bestfit.rec_len > 0 && + bestfit.rec_len < length * multiplier) { + break; + } + + /* this multiplier means we only extremely rarely + search more than 50 or so records. At 50 records we + accept records up to 11 times larger than what we + want */ + multiplier *= 1.05; } if (bestfit.rec_ptr != 0) { @@ -314,7 +343,8 @@ tdb_off_t tdb_allocate(struct tdb_context *tdb, tdb_len_t length, struct list_st goto fail; } - newrec_ptr = tdb_allocate_ofs(tdb, length, bestfit.rec_ptr, rec, bestfit.last_ptr); + newrec_ptr = tdb_allocate_ofs(tdb, length, bestfit.rec_ptr, + rec, bestfit.last_ptr); tdb_unlock(tdb, -1, F_WRLCK); return newrec_ptr; } @@ -328,3 +358,25 @@ tdb_off_t tdb_allocate(struct tdb_context *tdb, tdb_len_t length, struct list_st return 0; } + + +/* + return the size of the freelist - used to decide if we should repack +*/ +int tdb_freelist_size(struct tdb_context *tdb) +{ + tdb_off_t ptr; + int count=0; + + if (tdb_lock(tdb, -1, F_RDLCK) == -1) { + return -1; + } + + ptr = FREELIST_TOP; + while (tdb_ofs_read(tdb, ptr, &ptr) == 0 && ptr != 0) { + count++; + } + + tdb_unlock(tdb, -1, F_RDLCK); + return count; +} diff --git a/source3/lib/tdb/common/io.c b/source3/lib/tdb/common/io.c index 8ab0768883..172ab69d8c 100644 --- a/source3/lib/tdb/common/io.c +++ b/source3/lib/tdb/common/io.c @@ -101,8 +101,8 @@ static int tdb_write(struct tdb_context *tdb, tdb_off_t off, off+written); } if (written == -1) { - /* Ensure ecode is set for log fn. */ - tdb->ecode = TDB_ERR_IO; + /* Ensure ecode is set for log fn. */ + tdb->ecode = TDB_ERR_IO; TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_write failed at %d " "len=%d (%s)\n", off, len, strerror(errno))); return TDB_ERRCODE(TDB_ERR_IO, -1); @@ -111,8 +111,8 @@ static int tdb_write(struct tdb_context *tdb, tdb_off_t off, "write %d bytes at %d in two attempts\n", len, off)); errno = ENOSPC; - return TDB_ERRCODE(TDB_ERR_IO, -1); - } + return TDB_ERRCODE(TDB_ERR_IO, -1); + } } return 0; } @@ -230,7 +230,7 @@ void tdb_mmap(struct tdb_context *tdb) says to use for mmap expansion */ static int tdb_expand_file(struct tdb_context *tdb, tdb_off_t size, tdb_off_t addition) { - char buf[1024]; + char buf[8192]; if (tdb->read_only || tdb->traverse_read) { tdb->ecode = TDB_ERR_RDONLY; @@ -294,7 +294,7 @@ static int tdb_expand_file(struct tdb_context *tdb, tdb_off_t size, tdb_off_t ad int tdb_expand(struct tdb_context *tdb, tdb_off_t size) { struct list_struct rec; - tdb_off_t offset; + tdb_off_t offset, new_size; if (tdb_lock(tdb, -1, F_WRLCK) == -1) { TDB_LOG((tdb, TDB_DEBUG_ERROR, "lock failed in tdb_expand\n")); @@ -304,9 +304,11 @@ int tdb_expand(struct tdb_context *tdb, tdb_off_t size) /* must know about any previous expansions by another process */ tdb->methods->tdb_oob(tdb, tdb->map_size + 1, 1); - /* always make room for at least 10 more records, and round - the database up to a multiple of the page size */ - size = TDB_ALIGN(tdb->map_size + size*10, tdb->page_size) - tdb->map_size; + /* always make room for at least 100 more records, and at + least 25% more space. Round the database up to a multiple + of the page size */ + new_size = MAX(tdb->map_size + size*100, tdb->map_size * 1.25); + size = TDB_ALIGN(new_size, tdb->page_size) - tdb->map_size; if (!(tdb->flags & TDB_INTERNAL)) tdb_munmap(tdb); diff --git a/source3/lib/tdb/common/lock.c b/source3/lib/tdb/common/lock.c index e3fe888c46..f156c0fa7b 100644 --- a/source3/lib/tdb/common/lock.c +++ b/source3/lib/tdb/common/lock.c @@ -505,6 +505,9 @@ int tdb_chainunlock_read(struct tdb_context *tdb, TDB_DATA key) /* record lock stops delete underneath */ int tdb_lock_record(struct tdb_context *tdb, tdb_off_t off) { + if (tdb->global_lock.count) { + return 0; + } return off ? tdb->methods->tdb_brlock(tdb, off, F_RDLCK, F_SETLKW, 0, 1) : 0; } @@ -537,6 +540,10 @@ int tdb_unlock_record(struct tdb_context *tdb, tdb_off_t off) struct tdb_traverse_lock *i; uint32_t count = 0; + if (tdb->global_lock.count) { + return 0; + } + if (off == 0) return 0; for (i = &tdb->travlocks; i; i = i->next) diff --git a/source3/lib/tdb/common/open.c b/source3/lib/tdb/common/open.c index 0bd1c91a5e..b19e4cea29 100644 --- a/source3/lib/tdb/common/open.c +++ b/source3/lib/tdb/common/open.c @@ -35,7 +35,7 @@ static struct tdb_context *tdbs = NULL; static unsigned int default_tdb_hash(TDB_DATA *key) { uint32_t value; /* Used to compute the hash value. */ - uint32_t i; /* Used to cycle through random values. */ + uint32_t i; /* Used to cycle through random values. */ /* Set the initial value from the key size. */ for (value = 0x238F13AF * key->dsize, i=0; i < key->dsize; i++) @@ -90,7 +90,7 @@ static int tdb_new_database(struct tdb_context *tdb, int hash_size) size -= written; written = write(tdb->fd, newdb+written, size); if (written == size) { - ret = 0; + ret = 0; } else if (written >= 0) { /* a second incomplete write - we give up. * guessing the errno... */ @@ -152,6 +152,7 @@ struct tdb_context *tdb_open_ex(const char *name, int hash_size, int tdb_flags, int rev = 0, locked = 0; unsigned char *vp; uint32_t vertest; + unsigned v; if (!(tdb = (struct tdb_context *)calloc(1, sizeof *tdb))) { /* Can't log this */ @@ -178,9 +179,7 @@ struct tdb_context *tdb_open_ex(const char *name, int hash_size, int tdb_flags, tdb->page_size = 0x2000; } - if (open_flags & TDB_VOLATILE) { - tdb->max_dead_records = 5; - } + tdb->max_dead_records = (tdb_flags & TDB_VOLATILE) ? 5 : 0; if ((open_flags & O_ACCMODE) == O_WRONLY) { TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_open_ex: can't open tdb %s write-only\n", @@ -215,6 +214,10 @@ struct tdb_context *tdb_open_ex(const char *name, int hash_size, int tdb_flags, goto fail; /* errno set by open(2) */ } + /* on exec, don't inherit the fd */ + v = fcntl(tdb->fd, F_GETFD, 0); + fcntl(tdb->fd, F_SETFD, v | FD_CLOEXEC); + /* ensure there is only one process initialising at once */ if (tdb->methods->tdb_brlock(tdb, GLOBAL_LOCK, F_WRLCK, F_SETLKW, 0, 1) == -1) { TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_open_ex: failed to get global lock on %s: %s\n", @@ -224,6 +227,7 @@ struct tdb_context *tdb_open_ex(const char *name, int hash_size, int tdb_flags, /* we need to zero database if we are the only one with it open */ if ((tdb_flags & TDB_CLEAR_IF_FIRST) && + (!tdb->read_only) && (locked = (tdb->methods->tdb_brlock(tdb, ACTIVE_LOCK, F_WRLCK, F_SETLK, 0, 1) == 0))) { open_flags |= O_CREAT; if (ftruncate(tdb->fd, 0) == -1) { @@ -242,7 +246,7 @@ struct tdb_context *tdb_open_ex(const char *name, int hash_size, int tdb_flags, /* its not a valid database - possibly initialise it */ if (!(open_flags & O_CREAT) || tdb_new_database(tdb, hash_size) == -1) { if (errno == 0) { - errno = EIO; /* ie bad format or something */ + errno = EIO; /* ie bad format or something */ } goto fail; } @@ -283,7 +287,6 @@ struct tdb_context *tdb_open_ex(const char *name, int hash_size, int tdb_flags, tdb->map_size = st.st_size; tdb->device = st.st_dev; tdb->inode = st.st_ino; - tdb->max_dead_records = 0; tdb_mmap(tdb); if (locked) { if (tdb->methods->tdb_brlock(tdb, ACTIVE_LOCK, F_UNLCK, F_SETLK, 0, 1) == -1) { diff --git a/source3/lib/tdb/common/tdb.c b/source3/lib/tdb/common/tdb.c index 0e9d1dbd74..ea5d9ccc60 100644 --- a/source3/lib/tdb/common/tdb.c +++ b/source3/lib/tdb/common/tdb.c @@ -102,8 +102,7 @@ static tdb_off_t tdb_find(struct tdb_context *tdb, TDB_DATA key, uint32_t hash, } /* As tdb_find, but if you succeed, keep the lock */ -tdb_off_t tdb_find_lock_hash(struct tdb_context *tdb, TDB_DATA key, - uint32_t hash, int locktype, +tdb_off_t tdb_find_lock_hash(struct tdb_context *tdb, TDB_DATA key, uint32_t hash, int locktype, struct list_struct *rec) { uint32_t rec_ptr; @@ -237,14 +236,15 @@ int tdb_exists(struct tdb_context *tdb, TDB_DATA key) } /* actually delete an entry in the database given the offset */ -int tdb_do_delete(struct tdb_context *tdb, tdb_off_t rec_ptr, struct list_struct*rec) +int tdb_do_delete(struct tdb_context *tdb, tdb_off_t rec_ptr, struct list_struct *rec) { tdb_off_t last_ptr, i; struct list_struct lastrec; if (tdb->read_only || tdb->traverse_read) return -1; - if (tdb_write_lock_record(tdb, rec_ptr) == -1) { + if (tdb->traverse_write != 0 || + tdb_write_lock_record(tdb, rec_ptr) == -1) { /* Someone traversing here: mark it as dead */ rec->magic = TDB_DEAD_MAGIC; return tdb_rec_write(tdb, rec_ptr, rec); @@ -666,6 +666,16 @@ int tdb_get_flags(struct tdb_context *tdb) return tdb->flags; } +void tdb_add_flags(struct tdb_context *tdb, unsigned flags) +{ + tdb->flags |= flags; +} + +void tdb_remove_flags(struct tdb_context *tdb, unsigned flags) +{ + tdb->flags &= ~flags; +} + /* enable sequence number handling on an open tdb @@ -674,3 +684,62 @@ void tdb_enable_seqnum(struct tdb_context *tdb) { tdb->flags |= TDB_SEQNUM; } + + +/* + wipe the entire database, deleting all records. This can be done + very fast by using a global lock. The entire data portion of the + file becomes a single entry in the freelist. + */ +int tdb_wipe_all(struct tdb_context *tdb) +{ + int i; + tdb_off_t offset = 0; + ssize_t data_len; + + if (tdb_lockall(tdb) != 0) { + return -1; + } + + /* wipe the hashes */ + for (i=0;i<tdb->header.hash_size;i++) { + if (tdb_ofs_write(tdb, TDB_HASH_TOP(i), &offset) == -1) { + TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_wipe_all: failed to write hash %d\n", i)); + goto failed; + } + } + + /* wipe the freelist */ + if (tdb_ofs_write(tdb, FREELIST_TOP, &offset) == -1) { + TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_wipe_all: failed to write freelist\n")); + goto failed; + } + + if (tdb_ofs_write(tdb, TDB_RECOVERY_HEAD, &offset) == -1) { + TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_wipe_all: failed to write recovery head\n")); + goto failed; + } + + /* add all the rest of the file to the freelist */ + data_len = (tdb->map_size - TDB_DATA_START(tdb->header.hash_size)) - sizeof(struct list_struct); + if (data_len > 0) { + struct list_struct rec; + memset(&rec,'\0',sizeof(rec)); + rec.rec_len = data_len; + if (tdb_free(tdb, TDB_DATA_START(tdb->header.hash_size), &rec) == -1) { + TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_wipe_all: failed to add free record\n")); + goto failed; + } + } + + if (tdb_unlockall(tdb) != 0) { + TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_wipe_all: failed to unlock\n")); + goto failed; + } + + return 0; + +failed: + tdb_unlockall(tdb); + return -1; +} diff --git a/source3/lib/tdb/common/tdb_private.h b/source3/lib/tdb/common/tdb_private.h index 58c30c1706..ffac89ff0e 100644 --- a/source3/lib/tdb/common/tdb_private.h +++ b/source3/lib/tdb/common/tdb_private.h @@ -38,6 +38,10 @@ typedef uint32_t tdb_len_t; typedef uint32_t tdb_off_t; +#ifndef offsetof +#define offsetof(t,f) ((unsigned int)&((t *)0)->f) +#endif + #define TDB_MAGIC_FOOD "TDB file\n" #define TDB_VERSION (0x26011967 + 6) #define TDB_MAGIC (0x26011999U) @@ -45,7 +49,6 @@ typedef uint32_t tdb_off_t; #define TDB_DEAD_MAGIC (0xFEE1DEAD) #define TDB_RECOVERY_MAGIC (0xf53bc0e7U) #define TDB_ALIGNMENT 4 -#define MIN_REC_SIZE (2*sizeof(struct list_struct) + TDB_ALIGNMENT) #define DEFAULT_HASH_SIZE 131 #define FREELIST_TOP (sizeof(struct tdb_header)) #define TDB_ALIGN(x,a) (((x) + (a)-1) & ~((a)-1)) @@ -54,7 +57,7 @@ typedef uint32_t tdb_off_t; #define TDB_BAD_MAGIC(r) ((r)->magic != TDB_MAGIC && !TDB_DEAD(r)) #define TDB_HASH_TOP(hash) (FREELIST_TOP + (BUCKET(hash)+1)*sizeof(tdb_off_t)) #define TDB_HASHTABLE_SIZE(tdb) ((tdb->header.hash_size+1)*sizeof(tdb_off_t)) -#define TDB_DATA_START(hash_size) TDB_HASH_TOP(hash_size-1) +#define TDB_DATA_START(hash_size) (TDB_HASH_TOP(hash_size-1) + sizeof(tdb_off_t)) #define TDB_RECOVERY_HEAD offsetof(struct tdb_header, recovery_start) #define TDB_SEQNUM_OFS offsetof(struct tdb_header, sequence_number) #define TDB_PAD_BYTE 0x42 @@ -144,6 +147,7 @@ struct tdb_context { tdb_len_t map_size; /* how much space has been mapped */ int read_only; /* opened read-only */ int traverse_read; /* read-only traversal */ + int traverse_write; /* read-write traversal */ struct tdb_lock_type global_lock; int num_lockrecs; struct tdb_lock_type *lockrecs; /* only real locks, all with count>0 */ diff --git a/source3/lib/tdb/common/transaction.c b/source3/lib/tdb/common/transaction.c index 7eaacf7a16..4e2127be64 100644 --- a/source3/lib/tdb/common/transaction.c +++ b/source3/lib/tdb/common/transaction.c @@ -87,12 +87,6 @@ */ -struct tdb_transaction_el { - struct tdb_transaction_el *next, *prev; - tdb_off_t offset; - tdb_len_t length; - unsigned char *data; -}; /* hold the context of any current transaction @@ -105,12 +99,12 @@ struct tdb_transaction { /* the original io methods - used to do IOs to the real db */ const struct tdb_methods *io_methods; - /* the list of transaction elements. We use a doubly linked - list with a last pointer to allow us to keep the list - ordered, with first element at the front of the list. It - needs to be doubly linked as the read/write traversals need - to be backwards, while the commit needs to be forwards */ - struct tdb_transaction_el *elements, *elements_last; + /* the list of transaction blocks. When a block is first + written to, it gets created in this list */ + uint8_t **blocks; + uint32_t num_blocks; + uint32_t block_size; /* bytes in each block */ + uint32_t last_block_size; /* number of valid bytes in the last block */ /* non-zero when an internal transaction error has occurred. All write operations will then fail until the @@ -134,52 +128,48 @@ struct tdb_transaction { static int transaction_read(struct tdb_context *tdb, tdb_off_t off, void *buf, tdb_len_t len, int cv) { - struct tdb_transaction_el *el; - - /* we need to walk the list backwards to get the most recent data */ - for (el=tdb->transaction->elements_last;el;el=el->prev) { - tdb_len_t partial; + uint32_t blk; - if (off+len <= el->offset) { - continue; - } - if (off >= el->offset + el->length) { - continue; + /* break it down into block sized ops */ + while (len + (off % tdb->transaction->block_size) > tdb->transaction->block_size) { + tdb_len_t len2 = tdb->transaction->block_size - (off % tdb->transaction->block_size); + if (transaction_read(tdb, off, buf, len2, cv) != 0) { + return -1; } + len -= len2; + off += len2; + buf = (void *)(len2 + (char *)buf); + } - /* an overlapping read - needs to be split into up to - 2 reads and a memcpy */ - if (off < el->offset) { - partial = el->offset - off; - if (transaction_read(tdb, off, buf, partial, cv) != 0) { - goto fail; - } - len -= partial; - off += partial; - buf = (void *)(partial + (char *)buf); - } - if (off + len <= el->offset + el->length) { - partial = len; - } else { - partial = el->offset + el->length - off; - } - memcpy(buf, el->data + (off - el->offset), partial); - if (cv) { - tdb_convert(buf, len); - } - len -= partial; - off += partial; - buf = (void *)(partial + (char *)buf); - - if (len != 0 && transaction_read(tdb, off, buf, len, cv) != 0) { + if (len == 0) { + return 0; + } + + blk = off / tdb->transaction->block_size; + + /* see if we have it in the block list */ + if (tdb->transaction->num_blocks <= blk || + tdb->transaction->blocks[blk] == NULL) { + /* nope, do a real read */ + if (tdb->transaction->io_methods->tdb_read(tdb, off, buf, len, cv) != 0) { goto fail; } - return 0; } - /* its not in the transaction elements - do a real read */ - return tdb->transaction->io_methods->tdb_read(tdb, off, buf, len, cv); + /* it is in the block list. Now check for the last block */ + if (blk == tdb->transaction->num_blocks-1) { + if (len > tdb->transaction->last_block_size) { + goto fail; + } + } + + /* now copy it out of this block */ + memcpy(buf, tdb->transaction->blocks[blk] + (off % tdb->transaction->block_size), len); + if (cv) { + tdb_convert(buf, len); + } + return 0; fail: TDB_LOG((tdb, TDB_DEBUG_FATAL, "transaction_read: failed at off=%d len=%d\n", off, len)); @@ -195,12 +185,8 @@ fail: static int transaction_write(struct tdb_context *tdb, tdb_off_t off, const void *buf, tdb_len_t len) { - struct tdb_transaction_el *el, *best_el=NULL; + uint32_t blk; - if (len == 0) { - return 0; - } - /* if the write is to a hash head, then update the transaction hash heads */ if (len == sizeof(tdb_off_t) && off >= FREELIST_TOP && @@ -209,110 +195,145 @@ static int transaction_write(struct tdb_context *tdb, tdb_off_t off, memcpy(&tdb->transaction->hash_heads[chain], buf, len); } - /* first see if we can replace an existing entry */ - for (el=tdb->transaction->elements_last;el;el=el->prev) { - tdb_len_t partial; - - if (best_el == NULL && off == el->offset+el->length) { - best_el = el; - } - - if (off+len <= el->offset) { - continue; + /* break it up into block sized chunks */ + while (len + (off % tdb->transaction->block_size) > tdb->transaction->block_size) { + tdb_len_t len2 = tdb->transaction->block_size - (off % tdb->transaction->block_size); + if (transaction_write(tdb, off, buf, len2) != 0) { + return -1; } - if (off >= el->offset + el->length) { - continue; + len -= len2; + off += len2; + if (buf != NULL) { + buf = (const void *)(len2 + (const char *)buf); } + } - /* an overlapping write - needs to be split into up to - 2 writes and a memcpy */ - if (off < el->offset) { - partial = el->offset - off; - if (transaction_write(tdb, off, buf, partial) != 0) { - goto fail; - } - len -= partial; - off += partial; - buf = (const void *)(partial + (const char *)buf); - } - if (off + len <= el->offset + el->length) { - partial = len; + if (len == 0) { + return 0; + } + + blk = off / tdb->transaction->block_size; + off = off % tdb->transaction->block_size; + + if (tdb->transaction->num_blocks <= blk) { + uint8_t **new_blocks; + /* expand the blocks array */ + if (tdb->transaction->blocks == NULL) { + new_blocks = (uint8_t **)malloc( + (blk+1)*sizeof(uint8_t *)); } else { - partial = el->offset + el->length - off; + new_blocks = (uint8_t **)realloc( + tdb->transaction->blocks, + (blk+1)*sizeof(uint8_t *)); } - memcpy(el->data + (off - el->offset), buf, partial); - len -= partial; - off += partial; - buf = (const void *)(partial + (const char *)buf); - - if (len != 0 && transaction_write(tdb, off, buf, len) != 0) { + if (new_blocks == NULL) { + tdb->ecode = TDB_ERR_OOM; goto fail; } - - return 0; + memset(&new_blocks[tdb->transaction->num_blocks], 0, + (1+(blk - tdb->transaction->num_blocks))*sizeof(uint8_t *)); + tdb->transaction->blocks = new_blocks; + tdb->transaction->num_blocks = blk+1; + tdb->transaction->last_block_size = 0; } - /* see if we can append the new entry to an existing entry */ - if (best_el && best_el->offset + best_el->length == off && - (off+len < tdb->transaction->old_map_size || - off > tdb->transaction->old_map_size)) { - unsigned char *data = best_el->data; - el = best_el; - el->data = (unsigned char *)realloc(el->data, - el->length + len); - if (el->data == NULL) { + /* allocate and fill a block? */ + if (tdb->transaction->blocks[blk] == NULL) { + tdb->transaction->blocks[blk] = (uint8_t *)calloc(tdb->transaction->block_size, 1); + if (tdb->transaction->blocks[blk] == NULL) { tdb->ecode = TDB_ERR_OOM; tdb->transaction->transaction_error = 1; - el->data = data; - return -1; + return -1; } - if (buf) { - memcpy(el->data + el->length, buf, len); - } else { - memset(el->data + el->length, TDB_PAD_BYTE, len); + if (tdb->transaction->old_map_size > blk * tdb->transaction->block_size) { + tdb_len_t len2 = tdb->transaction->block_size; + if (len2 + (blk * tdb->transaction->block_size) > tdb->transaction->old_map_size) { + len2 = tdb->transaction->old_map_size - (blk * tdb->transaction->block_size); + } + if (tdb->transaction->io_methods->tdb_read(tdb, blk * tdb->transaction->block_size, + tdb->transaction->blocks[blk], + len2, 0) != 0) { + SAFE_FREE(tdb->transaction->blocks[blk]); + tdb->ecode = TDB_ERR_IO; + goto fail; + } + if (blk == tdb->transaction->num_blocks-1) { + tdb->transaction->last_block_size = len2; + } } - el->length += len; - return 0; - } - - /* add a new entry at the end of the list */ - el = (struct tdb_transaction_el *)malloc(sizeof(*el)); - if (el == NULL) { - tdb->ecode = TDB_ERR_OOM; - tdb->transaction->transaction_error = 1; - return -1; - } - el->next = NULL; - el->prev = tdb->transaction->elements_last; - el->offset = off; - el->length = len; - el->data = (unsigned char *)malloc(len); - if (el->data == NULL) { - free(el); - tdb->ecode = TDB_ERR_OOM; - tdb->transaction->transaction_error = 1; - return -1; } - if (buf) { - memcpy(el->data, buf, len); + + /* overwrite part of an existing block */ + if (buf == NULL) { + memset(tdb->transaction->blocks[blk] + off, 0, len); } else { - memset(el->data, TDB_PAD_BYTE, len); + memcpy(tdb->transaction->blocks[blk] + off, buf, len); } - if (el->prev) { - el->prev->next = el; - } else { - tdb->transaction->elements = el; + if (blk == tdb->transaction->num_blocks-1) { + if (len + off > tdb->transaction->last_block_size) { + tdb->transaction->last_block_size = len + off; + } } - tdb->transaction->elements_last = el; + return 0; fail: - TDB_LOG((tdb, TDB_DEBUG_FATAL, "transaction_write: failed at off=%d len=%d\n", off, len)); - tdb->ecode = TDB_ERR_IO; + TDB_LOG((tdb, TDB_DEBUG_FATAL, "transaction_write: failed at off=%d len=%d\n", + (blk*tdb->transaction->block_size) + off, len)); tdb->transaction->transaction_error = 1; return -1; } + +/* + write while in a transaction - this varient never expands the transaction blocks, it only + updates existing blocks. This means it cannot change the recovery size +*/ +static int transaction_write_existing(struct tdb_context *tdb, tdb_off_t off, + const void *buf, tdb_len_t len) +{ + uint32_t blk; + + /* break it up into block sized chunks */ + while (len + (off % tdb->transaction->block_size) > tdb->transaction->block_size) { + tdb_len_t len2 = tdb->transaction->block_size - (off % tdb->transaction->block_size); + if (transaction_write_existing(tdb, off, buf, len2) != 0) { + return -1; + } + len -= len2; + off += len2; + if (buf != NULL) { + buf = (const void *)(len2 + (const char *)buf); + } + } + + if (len == 0) { + return 0; + } + + blk = off / tdb->transaction->block_size; + off = off % tdb->transaction->block_size; + + if (tdb->transaction->num_blocks <= blk || + tdb->transaction->blocks[blk] == NULL) { + return 0; + } + + if (blk == tdb->transaction->num_blocks-1 && + off + len > tdb->transaction->last_block_size) { + if (off >= tdb->transaction->last_block_size) { + return 0; + } + len = tdb->transaction->last_block_size - off; + } + + /* overwrite part of an existing block */ + memcpy(tdb->transaction->blocks[blk] + off, buf, len); + + return 0; +} + + /* accelerated hash chain head search, using the cached hash heads */ @@ -419,10 +440,14 @@ int tdb_transaction_start(struct tdb_context *tdb) return -1; } + /* a page at a time seems like a reasonable compromise between compactness and efficiency */ + tdb->transaction->block_size = tdb->page_size; + /* get the transaction write lock. This is a blocking lock. As discussed with Volker, there are a number of ways we could make this async, which we will probably do in the future */ if (tdb_transaction_lock(tdb, F_WRLCK) == -1) { + SAFE_FREE(tdb->transaction->blocks); SAFE_FREE(tdb->transaction); return -1; } @@ -460,21 +485,12 @@ int tdb_transaction_start(struct tdb_context *tdb) tdb->transaction->io_methods = tdb->methods; tdb->methods = &transaction_methods; - /* by calling this transaction write here, we ensure that we don't grow the - transaction linked list due to hash table updates */ - if (transaction_write(tdb, FREELIST_TOP, tdb->transaction->hash_heads, - TDB_HASHTABLE_SIZE(tdb)) != 0) { - TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_start: failed to prime hash table\n")); - tdb->ecode = TDB_ERR_IO; - tdb->methods = tdb->transaction->io_methods; - goto fail; - } - return 0; fail: tdb_brlock(tdb, FREELIST_TOP, F_UNLCK, F_SETLKW, 0, 0); tdb_transaction_unlock(tdb); + SAFE_FREE(tdb->transaction->blocks); SAFE_FREE(tdb->transaction->hash_heads); SAFE_FREE(tdb->transaction); return -1; @@ -486,6 +502,8 @@ fail: */ int tdb_transaction_cancel(struct tdb_context *tdb) { + int i; + if (tdb->transaction == NULL) { TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_cancel: no transaction\n")); return -1; @@ -499,13 +517,13 @@ int tdb_transaction_cancel(struct tdb_context *tdb) tdb->map_size = tdb->transaction->old_map_size; - /* free all the transaction elements */ - while (tdb->transaction->elements) { - struct tdb_transaction_el *el = tdb->transaction->elements; - tdb->transaction->elements = el->next; - free(el->data); - free(el); + /* free all the transaction blocks */ + for (i=0;i<tdb->transaction->num_blocks;i++) { + if (tdb->transaction->blocks[i] != NULL) { + free(tdb->transaction->blocks[i]); + } } + SAFE_FREE(tdb->transaction->blocks); /* remove any global lock created during the transaction */ if (tdb->global_lock.count != 0) { @@ -515,7 +533,6 @@ int tdb_transaction_cancel(struct tdb_context *tdb) /* remove any locks created during the transaction */ if (tdb->num_locks != 0) { - int i; for (i=0;i<tdb->num_lockrecs;i++) { tdb_brlock(tdb,FREELIST_TOP+4*tdb->lockrecs[i].list, F_UNLCK,F_SETLKW, 0, 1); @@ -567,16 +584,24 @@ static int transaction_sync(struct tdb_context *tdb, tdb_off_t offset, tdb_len_t */ static tdb_len_t tdb_recovery_size(struct tdb_context *tdb) { - struct tdb_transaction_el *el; tdb_len_t recovery_size = 0; + int i; recovery_size = sizeof(uint32_t); - for (el=tdb->transaction->elements;el;el=el->next) { - if (el->offset >= tdb->transaction->old_map_size) { + for (i=0;i<tdb->transaction->num_blocks;i++) { + if (i * tdb->transaction->block_size >= tdb->transaction->old_map_size) { + break; + } + if (tdb->transaction->blocks[i] == NULL) { continue; } - recovery_size += 2*sizeof(tdb_off_t) + el->length; - } + recovery_size += 2*sizeof(tdb_off_t); + if (i == tdb->transaction->num_blocks-1) { + recovery_size += tdb->transaction->last_block_size; + } else { + recovery_size += tdb->transaction->block_size; + } + } return recovery_size; } @@ -658,6 +683,10 @@ static int tdb_recovery_allocate(struct tdb_context *tdb, TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_recovery_allocate: failed to write recovery head\n")); return -1; } + if (transaction_write_existing(tdb, TDB_RECOVERY_HEAD, &recovery_head, sizeof(tdb_off_t)) == -1) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_recovery_allocate: failed to write recovery head\n")); + return -1; + } return 0; } @@ -669,7 +698,6 @@ static int tdb_recovery_allocate(struct tdb_context *tdb, static int transaction_setup_recovery(struct tdb_context *tdb, tdb_off_t *magic_offset) { - struct tdb_transaction_el *el; tdb_len_t recovery_size; unsigned char *data, *p; const struct tdb_methods *methods = tdb->transaction->io_methods; @@ -677,6 +705,7 @@ static int transaction_setup_recovery(struct tdb_context *tdb, tdb_off_t recovery_offset, recovery_max_size; tdb_off_t old_map_size = tdb->transaction->old_map_size; uint32_t magic, tailer; + int i; /* check that the recovery area has enough space @@ -704,30 +733,43 @@ static int transaction_setup_recovery(struct tdb_context *tdb, /* build the recovery data into a single blob to allow us to do a single large write, which should be more efficient */ p = data + sizeof(*rec); - for (el=tdb->transaction->elements;el;el=el->next) { - if (el->offset >= old_map_size) { + for (i=0;i<tdb->transaction->num_blocks;i++) { + tdb_off_t offset; + tdb_len_t length; + + if (tdb->transaction->blocks[i] == NULL) { + continue; + } + + offset = i * tdb->transaction->block_size; + length = tdb->transaction->block_size; + if (i == tdb->transaction->num_blocks-1) { + length = tdb->transaction->last_block_size; + } + + if (offset >= old_map_size) { continue; } - if (el->offset + el->length > tdb->transaction->old_map_size) { + if (offset + length > tdb->transaction->old_map_size) { TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_setup_recovery: transaction data over new region boundary\n")); free(data); tdb->ecode = TDB_ERR_CORRUPT; return -1; } - memcpy(p, &el->offset, 4); - memcpy(p+4, &el->length, 4); + memcpy(p, &offset, 4); + memcpy(p+4, &length, 4); if (DOCONV()) { tdb_convert(p, 8); } /* the recovery area contains the old data, not the new data, so we have to call the original tdb_read method to get it */ - if (methods->tdb_read(tdb, el->offset, p + 8, el->length, 0) != 0) { + if (methods->tdb_read(tdb, offset, p + 8, length, 0) != 0) { free(data); tdb->ecode = TDB_ERR_IO; return -1; } - p += 8 + el->length; + p += 8 + length; } /* and the tailer */ @@ -742,6 +784,12 @@ static int transaction_setup_recovery(struct tdb_context *tdb, tdb->ecode = TDB_ERR_IO; return -1; } + if (transaction_write_existing(tdb, recovery_offset, data, sizeof(*rec) + recovery_size) == -1) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_setup_recovery: failed to write secondary recovery data\n")); + free(data); + tdb->ecode = TDB_ERR_IO; + return -1; + } /* as we don't have ordered writes, we have to sync the recovery data before we update the magic to indicate that the recovery @@ -763,6 +811,11 @@ static int transaction_setup_recovery(struct tdb_context *tdb, tdb->ecode = TDB_ERR_IO; return -1; } + if (transaction_write_existing(tdb, *magic_offset, &magic, sizeof(magic)) == -1) { + TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_setup_recovery: failed to write secondary recovery magic\n")); + tdb->ecode = TDB_ERR_IO; + return -1; + } /* ensure the recovery magic marker is on disk */ if (transaction_sync(tdb, *magic_offset, sizeof(magic)) == -1) { @@ -780,6 +833,7 @@ int tdb_transaction_commit(struct tdb_context *tdb) const struct tdb_methods *methods; tdb_off_t magic_offset = 0; uint32_t zero = 0; + int i; if (tdb->transaction == NULL) { TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_commit: no transaction\n")); @@ -793,13 +847,14 @@ int tdb_transaction_commit(struct tdb_context *tdb) return -1; } + if (tdb->transaction->nesting != 0) { tdb->transaction->nesting--; return 0; } /* check for a null transaction */ - if (tdb->transaction->elements == NULL) { + if (tdb->transaction->blocks == NULL) { tdb_transaction_cancel(tdb); return 0; } @@ -858,10 +913,21 @@ int tdb_transaction_commit(struct tdb_context *tdb) } /* perform all the writes */ - while (tdb->transaction->elements) { - struct tdb_transaction_el *el = tdb->transaction->elements; + for (i=0;i<tdb->transaction->num_blocks;i++) { + tdb_off_t offset; + tdb_len_t length; + + if (tdb->transaction->blocks[i] == NULL) { + continue; + } - if (methods->tdb_write(tdb, el->offset, el->data, el->length) == -1) { + offset = i * tdb->transaction->block_size; + length = tdb->transaction->block_size; + if (i == tdb->transaction->num_blocks-1) { + length = tdb->transaction->last_block_size; + } + + if (methods->tdb_write(tdb, offset, tdb->transaction->blocks[i], length) == -1) { TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_commit: write failed during commit\n")); /* we've overwritten part of the data and @@ -876,11 +942,12 @@ int tdb_transaction_commit(struct tdb_context *tdb) TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_commit: write failed\n")); return -1; } - tdb->transaction->elements = el->next; - free(el->data); - free(el); + SAFE_FREE(tdb->transaction->blocks[i]); } + SAFE_FREE(tdb->transaction->blocks); + tdb->transaction->num_blocks = 0; + if (!(tdb->flags & TDB_NOSYNC)) { /* ensure the new data is on disk */ if (transaction_sync(tdb, 0, tdb->map_size) == -1) { @@ -919,6 +986,7 @@ int tdb_transaction_commit(struct tdb_context *tdb) /* use a transaction cancel to free memory and remove the transaction locks */ tdb_transaction_cancel(tdb); + return 0; } diff --git a/source3/lib/tdb/common/traverse.c b/source3/lib/tdb/common/traverse.c index 27b2cfc54a..07b0c23858 100644 --- a/source3/lib/tdb/common/traverse.c +++ b/source3/lib/tdb/common/traverse.c @@ -241,7 +241,9 @@ int tdb_traverse(struct tdb_context *tdb, return -1; } + tdb->traverse_write++; ret = tdb_traverse_internal(tdb, fn, private_data, &tl); + tdb->traverse_write--; tdb_transaction_unlock(tdb); @@ -333,3 +335,4 @@ TDB_DATA tdb_nextkey(struct tdb_context *tdb, TDB_DATA oldkey) TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_nextkey: WARNING tdb_unlock failed!\n")); return key; } + diff --git a/source3/lib/tdb/configure.ac b/source3/lib/tdb/configure.ac index de8fc9c2b5..1085055bc9 100644 --- a/source3/lib/tdb/configure.ac +++ b/source3/lib/tdb/configure.ac @@ -2,7 +2,7 @@ AC_PREREQ(2.50) AC_DEFUN([SMB_MODULE_DEFAULT], [echo -n ""]) AC_DEFUN([SMB_LIBRARY_ENABLE], [echo -n ""]) AC_DEFUN([SMB_ENABLE], [echo -n ""]) -AC_INIT(tdb, 1.1.0) +AC_INIT(tdb, 1.1.1) AC_CONFIG_SRCDIR([common/tdb.c]) AC_CONFIG_HEADER(include/config.h) AC_LIBREPLACE_ALL_CHECKS diff --git a/source3/lib/tdb/include/tdb.h b/source3/lib/tdb/include/tdb.h index fce6628b84..0008085de5 100644 --- a/source3/lib/tdb/include/tdb.h +++ b/source3/lib/tdb/include/tdb.h @@ -135,6 +135,8 @@ int tdb_get_seqnum(struct tdb_context *tdb); int tdb_hash_size(struct tdb_context *tdb); size_t tdb_map_size(struct tdb_context *tdb); int tdb_get_flags(struct tdb_context *tdb); +void tdb_add_flags(struct tdb_context *tdb, unsigned flag); +void tdb_remove_flags(struct tdb_context *tdb, unsigned flag); void tdb_enable_seqnum(struct tdb_context *tdb); void tdb_increment_seqnum_nonblock(struct tdb_context *tdb); @@ -153,6 +155,8 @@ void tdb_setalarm_sigptr(struct tdb_context *tdb, volatile sig_atomic_t *sigptr) void tdb_dump_all(struct tdb_context *tdb); int tdb_printfreelist(struct tdb_context *tdb); int tdb_validate_freelist(struct tdb_context *tdb, int *pnum_entries); +int tdb_wipe_all(struct tdb_context *tdb); +int tdb_freelist_size(struct tdb_context *tdb); extern TDB_DATA tdb_null; diff --git a/source3/lib/tdb/tools/tdbtool.c b/source3/lib/tdb/tools/tdbtool.c index 79435a3571..d104ccd7c4 100644 --- a/source3/lib/tdb/tools/tdbtool.c +++ b/source3/lib/tdb/tools/tdbtool.c @@ -135,7 +135,7 @@ static void print_data(const char *buf,int len) if (len<=0) return; printf("[%03X] ",i); for (i=0;i<len;) { - printf("%02X ",(int)buf[i]); + printf("%02X ",(int)((unsigned char)buf[i])); i++; if (i%8 == 0) printf(" "); if (i%16 == 0) { diff --git a/source3/lib/time.c b/source3/lib/time.c index 5301e3a55a..f98e03197f 100644 --- a/source3/lib/time.c +++ b/source3/lib/time.c @@ -1382,7 +1382,7 @@ void unix_to_nt_time_abs(NTTIME *nt, time_t t) d = (double)(t); d *= 1.0e7; - *nt = d; + *nt = (NTTIME)d; /* convert to a negative value */ *nt=~*nt; @@ -1443,7 +1443,7 @@ const char *display_time(NTTIME nttime) low = ~(nttime & 0xFFFFFFFF); low = low/(1000*1000*10); - sec=high+low; + sec=(int)(high+low); days=sec/(60*60*24); hours=(sec - (days*60*60*24)) / (60*60); diff --git a/source3/lib/username.c b/source3/lib/username.c index 21eed9f5fc..3087bac0f4 100644 --- a/source3/lib/username.c +++ b/source3/lib/username.c @@ -32,19 +32,24 @@ static struct passwd *uname_string_combinations2(char *s, TALLOC_CTX *mem_ctx, i Get a users home directory. ****************************************************************************/ -char *get_user_home_dir(const char *user) +char *get_user_home_dir(TALLOC_CTX *mem_ctx, const char *user) { - static struct passwd *pass; + struct passwd *pass; + char *result; /* Ensure the user exists. */ - pass = Get_Pwnam(user); + pass = Get_Pwnam_alloc(mem_ctx, user); if (!pass) return(NULL); + /* Return home directory from struct passwd. */ - return(pass->pw_dir); + result = talloc_move(mem_ctx, &pass->pw_dir); + + TALLOC_FREE(pass); + return result; } /**************************************************************************** @@ -55,8 +60,6 @@ char *get_user_home_dir(const char *user) * - using lp_usernamelevel() for permutations. ****************************************************************************/ -static struct passwd *Get_Pwnam_ret = NULL; - static struct passwd *Get_Pwnam_internals(TALLOC_CTX *mem_ctx, const char *user, char *user2) { @@ -134,40 +137,6 @@ struct passwd *Get_Pwnam_alloc(TALLOC_CTX *mem_ctx, const char *user) return ret; } -/**************************************************************************** - Get_Pwnam wrapper without modification. - NOTE: This with NOT modify 'user'! -****************************************************************************/ - -struct passwd *Get_Pwnam(const char *user) -{ - struct passwd *ret; - - ret = Get_Pwnam_alloc(NULL, user); - - /* This call used to just return the 'passwd' static buffer. - This could then have accidental reuse implications, so - we now malloc a copy, and free it in the next use. - - This should cause the (ab)user to segfault if it - uses an old struct. - - This is better than useing the wrong data in security - critical operations. - - The real fix is to make the callers free the returned - malloc'ed data. - */ - - if (Get_Pwnam_ret) { - TALLOC_FREE(Get_Pwnam_ret); - } - - Get_Pwnam_ret = ret; - - return ret; -} - /* The functions below have been taken from password.c and slightly modified */ /**************************************************************************** Apply a function to upper/lower case combinations diff --git a/source3/lib/util.c b/source3/lib/util.c index 73b035b22b..dba7142bad 100644 --- a/source3/lib/util.c +++ b/source3/lib/util.c @@ -183,6 +183,7 @@ void gfree_names(void) SAFE_FREE( smb_myworkgroup ); SAFE_FREE( smb_scope ); free_netbios_names_array(); + free_local_machine_name(); } void gfree_all( void ) @@ -289,7 +290,8 @@ static struct user_auth_info cmdline_auth_info = { NULL, /* password */ false, /* got_pass */ false, /* use_kerberos */ - Undefined /* signing state */ + Undefined, /* signing state */ + false /* smb_encrypt */ }; const char *get_cmdline_auth_info_username(void) @@ -362,11 +364,22 @@ void set_cmdline_auth_info_use_krb5_ticket(void) cmdline_auth_info.got_pass = true; } +/* This should only be used by lib/popt_common.c JRA */ +void set_cmdline_auth_info_smb_encrypt(void) +{ + cmdline_auth_info.smb_encrypt = true; +} + bool get_cmdline_auth_info_got_pass(void) { return cmdline_auth_info.got_pass; } +bool get_cmdline_auth_info_smb_encrypt(void) +{ + return cmdline_auth_info.smb_encrypt; +} + bool get_cmdline_auth_info_copy(struct user_auth_info *info) { *info = cmdline_auth_info; @@ -493,6 +506,19 @@ bool file_exist(const char *fname,SMB_STRUCT_STAT *sbuf) } /******************************************************************* + Check if a unix domain socket exists - call vfs_file_exist for samba files. +********************************************************************/ + +bool socket_exist(const char *fname) +{ + SMB_STRUCT_STAT st; + if (sys_stat(fname,&st) != 0) + return(False); + + return S_ISSOCK(st.st_mode); +} + +/******************************************************************* Check a files mod time. ********************************************************************/ @@ -605,32 +631,30 @@ void show_msg(char *buf) } /******************************************************************* - Set the length and marker of an smb packet. + Set the length and marker of an encrypted smb packet. ********************************************************************/ -void smb_setlen(char *buf,int len) +void smb_set_enclen(char *buf,int len,uint16 enc_ctx_num) { _smb_setlen(buf,len); SCVAL(buf,4,0xFF); - SCVAL(buf,5,'S'); - SCVAL(buf,6,'M'); - SCVAL(buf,7,'B'); + SCVAL(buf,5,'E'); + SSVAL(buf,6,enc_ctx_num); } /******************************************************************* - Setup the word count and byte count for a smb message. + Set the length and marker of an smb packet. ********************************************************************/ -int set_message(char *buf,int num_words,int num_bytes,bool zero) +void smb_setlen(char *buf,int len) { - if (zero && (num_words || num_bytes)) { - memset(buf + smb_size,'\0',num_words*2 + num_bytes); - } - SCVAL(buf,smb_wct,num_words); - SSVAL(buf,smb_vwv + num_words*SIZEOFWORD,num_bytes); - smb_setlen(buf,smb_size + num_words*2 + num_bytes - 4); - return (smb_size + num_words*2 + num_bytes); + _smb_setlen(buf,len); + + SCVAL(buf,4,0xFF); + SCVAL(buf,5,'S'); + SCVAL(buf,6,'M'); + SCVAL(buf,7,'B'); } /******************************************************************* @@ -641,21 +665,11 @@ int set_message_bcc(char *buf,int num_bytes) { int num_words = CVAL(buf,smb_wct); SSVAL(buf,smb_vwv + num_words*SIZEOFWORD,num_bytes); - smb_setlen(buf,smb_size + num_words*2 + num_bytes - 4); + _smb_setlen(buf,smb_size + num_words*2 + num_bytes - 4); return (smb_size + num_words*2 + num_bytes); } /******************************************************************* - Setup only the byte count for a smb message, using the end of the - message as a marker. -********************************************************************/ - -int set_message_end(void *outbuf,void *end_ptr) -{ - return set_message_bcc((char *)outbuf,PTR_DIFF(end_ptr,smb_buf((char *)outbuf))); -} - -/******************************************************************* Add a data blob to the end of a smb_buf, adjusting bcc and smb_len. Return the bytes added ********************************************************************/ @@ -896,67 +910,6 @@ int set_blocking(int fd, bool set) #undef FLAG_TO_SET } -/**************************************************************************** - Transfer some data between two fd's. -****************************************************************************/ - -#ifndef TRANSFER_BUF_SIZE -#define TRANSFER_BUF_SIZE 65536 -#endif - -ssize_t transfer_file_internal(int infd, int outfd, size_t n, ssize_t (*read_fn)(int, void *, size_t), - ssize_t (*write_fn)(int, const void *, size_t)) -{ - char *buf; - size_t total = 0; - ssize_t read_ret; - ssize_t write_ret; - size_t num_to_read_thistime; - size_t num_written = 0; - - if ((buf = SMB_MALLOC_ARRAY(char, TRANSFER_BUF_SIZE)) == NULL) - return -1; - - while (total < n) { - num_to_read_thistime = MIN((n - total), TRANSFER_BUF_SIZE); - - read_ret = (*read_fn)(infd, buf, num_to_read_thistime); - if (read_ret == -1) { - DEBUG(0,("transfer_file_internal: read failure. Error = %s\n", strerror(errno) )); - SAFE_FREE(buf); - return -1; - } - if (read_ret == 0) - break; - - num_written = 0; - - while (num_written < read_ret) { - write_ret = (*write_fn)(outfd,buf + num_written, read_ret - num_written); - - if (write_ret == -1) { - DEBUG(0,("transfer_file_internal: write failure. Error = %s\n", strerror(errno) )); - SAFE_FREE(buf); - return -1; - } - if (write_ret == 0) - return (ssize_t)total; - - num_written += (size_t)write_ret; - } - - total += (size_t)read_ret; - } - - SAFE_FREE(buf); - return (ssize_t)total; -} - -SMB_OFF_T transfer_file(int infd,int outfd,SMB_OFF_T n) -{ - return (SMB_OFF_T)transfer_file_internal(infd, outfd, (size_t)n, sys_read, sys_write); -} - /******************************************************************* Sleep for a specified number of milliseconds. ********************************************************************/ @@ -2227,17 +2180,12 @@ void dump_data_pw(const char *msg, const uchar * data, size_t len) #endif } -char *tab_depth(int depth) +const char *tab_depth(int level, int depth) { - static fstring spaces; - size_t len = depth * 4; - if (len > sizeof(fstring)-1) { - len = sizeof(fstring)-1; + if( CHECK_DEBUGLVL(level) ) { + dbgtext("%*s", depth*4, ""); } - - memset(spaces, ' ', len); - spaces[len] = 0; - return spaces; + return ""; } /***************************************************************************** @@ -2483,6 +2431,7 @@ char *smb_xstrndup(const char *s, size_t n) if (n == -1 || ! *ptr) { smb_panic("smb_xvasprintf: out of memory"); } + va_end(ap2); return n; } @@ -3325,3 +3274,93 @@ void *talloc_zeronull(const void *context, size_t size, const char *name) return talloc_named_const(context, size, name); } #endif + +/* Split a path name into filename and stream name components. Canonicalise + * such that an implicit $DATA token is always explicit. + * + * The "specification" of this function can be found in the + * run_local_stream_name() function in torture.c, I've tried those + * combinations against a W2k3 server. + */ + +NTSTATUS split_ntfs_stream_name(TALLOC_CTX *mem_ctx, const char *fname, + char **pbase, char **pstream) +{ + char *base = NULL; + char *stream = NULL; + char *sname; /* stream name */ + const char *stype; /* stream type */ + + DEBUG(10, ("split_ntfs_stream_name called for [%s]\n", fname)); + + sname = strchr_m(fname, ':'); + + if (lp_posix_pathnames() || (sname == NULL)) { + if (pbase != NULL) { + base = talloc_strdup(mem_ctx, fname); + NT_STATUS_HAVE_NO_MEMORY(base); + } + goto done; + } + + if (pbase != NULL) { + base = talloc_strndup(mem_ctx, fname, PTR_DIFF(sname, fname)); + NT_STATUS_HAVE_NO_MEMORY(base); + } + + sname += 1; + + stype = strchr_m(sname, ':'); + + if (stype == NULL) { + sname = talloc_strdup(mem_ctx, sname); + stype = "$DATA"; + } + else { + if (StrCaseCmp(stype, ":$DATA") != 0) { + /* + * If there is an explicit stream type, so far we only + * allow $DATA. Is there anything else allowed? -- vl + */ + DEBUG(10, ("[%s] is an invalid stream type\n", stype)); + TALLOC_FREE(base); + return NT_STATUS_OBJECT_NAME_INVALID; + } + sname = talloc_strndup(mem_ctx, sname, PTR_DIFF(stype, sname)); + stype += 1; + } + + if (sname == NULL) { + TALLOC_FREE(base); + return NT_STATUS_NO_MEMORY; + } + + if (sname[0] == '\0') { + /* + * no stream name, so no stream + */ + goto done; + } + + if (pstream != NULL) { + stream = talloc_asprintf(mem_ctx, "%s:%s", sname, stype); + if (stream == NULL) { + TALLOC_FREE(sname); + TALLOC_FREE(base); + return NT_STATUS_NO_MEMORY; + } + /* + * upper-case the type field + */ + strupper_m(strchr_m(stream, ':')+1); + } + + done: + if (pbase != NULL) { + *pbase = base; + } + if (pstream != NULL) { + *pstream = stream; + } + return NT_STATUS_OK; +} diff --git a/source3/lib/util_pw.c b/source3/lib/util_pw.c index 1973626d84..428378505f 100644 --- a/source3/lib/util_pw.c +++ b/source3/lib/util_pw.c @@ -37,74 +37,36 @@ struct passwd *tcopy_passwd(TALLOC_CTX *mem_ctx, const struct passwd *from) return ret; } -#define PWNAMCACHE_SIZE 4 -static struct passwd **pwnam_cache = NULL; - -static void init_pwnam_cache(void) -{ - if (pwnam_cache != NULL) - return; - - pwnam_cache = TALLOC_ZERO_ARRAY(NULL, struct passwd *, - PWNAMCACHE_SIZE); - if (pwnam_cache == NULL) { - smb_panic("Could not init pwnam_cache"); - } - - return; -} - void flush_pwnam_cache(void) { - TALLOC_FREE(pwnam_cache); - pwnam_cache = NULL; - init_pwnam_cache(); + memcache_flush(NULL, GETPWNAM_CACHE); } struct passwd *getpwnam_alloc(TALLOC_CTX *mem_ctx, const char *name) { - int i; - - struct passwd *temp; + struct passwd *temp, *cached; - init_pwnam_cache(); - - for (i=0; i<PWNAMCACHE_SIZE; i++) { - if ((pwnam_cache[i] != NULL) && - (strcmp(name, pwnam_cache[i]->pw_name) == 0)) { - DEBUG(10, ("Got %s from pwnam_cache\n", name)); - return tcopy_passwd(mem_ctx, pwnam_cache[i]); - } + temp = (struct passwd *)memcache_lookup_talloc( + NULL, GETPWNAM_CACHE, data_blob_string_const(name)); + if (temp != NULL) { + return tcopy_passwd(mem_ctx, temp); } temp = sys_getpwnam(name); - - if (!temp) { -#if 0 - if (errno == ENOMEM) { - /* what now? */ - } -#endif + if (temp == NULL) { return NULL; } - for (i=0; i<PWNAMCACHE_SIZE; i++) { - if (pwnam_cache[i] == NULL) - break; + cached = tcopy_passwd(NULL, temp); + if (cached == NULL) { + /* + * Just don't add this into the cache, ignore the failure + */ + return temp; } - if (i == PWNAMCACHE_SIZE) - i = rand() % PWNAMCACHE_SIZE; - - if (pwnam_cache[i] != NULL) { - /* Remove this old cache entry, from the cache. We - * use talloc_unlink here because we want to be very - * clear which referece we are removing */ - talloc_unlink(pwnam_cache, pwnam_cache[i]); - } - - pwnam_cache[i] = tcopy_passwd(pwnam_cache, temp); - + memcache_add_talloc(NULL, GETPWNAM_CACHE, data_blob_string_const(name), + cached); return tcopy_passwd(mem_ctx, temp); } diff --git a/source3/lib/util_reg.c b/source3/lib/util_reg.c index 956f0475a5..2d7d70c63f 100644 --- a/source3/lib/util_reg.c +++ b/source3/lib/util_reg.c @@ -130,6 +130,7 @@ bool registry_smbconf_valname_forbidden(const char *valname) "include", "lock directory", "lock dir", + "config backend", NULL }; const char **forbidden = NULL; diff --git a/source3/lib/util_reg_smbconf.c b/source3/lib/util_reg_smbconf.c deleted file mode 100644 index 154c67ab8f..0000000000 --- a/source3/lib/util_reg_smbconf.c +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Unix SMB/CIFS implementation. - * Registry helper routines - * Copyright (C) Michael Adam 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 the Free - * Software Foundation; either version 3 of the License, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, see <http://www.gnu.org/licenses/>. - */ - -#include "includes.h" - -#undef DBGC_CLASS -#define DBGC_CLASS DBGC_REGISTRY - -extern REGISTRY_OPS smbconf_reg_ops; - -/* - * create a fake token just with enough rights to - * locally access the registry. - */ -NT_USER_TOKEN *registry_create_admin_token(TALLOC_CTX *mem_ctx) -{ - NT_USER_TOKEN *token = NULL; - - /* fake a user token: builtin administrators sid and the - * disk operators privilege is all we need to access the - * registry... */ - if (!(token = TALLOC_ZERO_P(mem_ctx, NT_USER_TOKEN))) { - DEBUG(1, ("talloc failed\n")); - goto done; - } - token->privileges = se_disk_operators; - if (!add_sid_to_array(token, &global_sid_Builtin_Administrators, - &token->user_sids, &token->num_sids)) { - DEBUG(1, ("Error adding builtin administrators sid " - "to fake token.\n")); - goto done; - } -done: - return token; -} - -/* - * init the smbconf portion of the registry. - * for use in places where not the whole registry is needed, - * e.g. utils/net_conf.c and loadparm.c - */ -bool registry_init_regdb(void) -{ - bool ret = False; - int saved_errno = 0; - static REGISTRY_HOOK smbconf_reg_hook = {KEY_SMBCONF, &smbconf_reg_ops}; - - DEBUG(10, ("registry_init_regdb called\n")); - - if (!regdb_init()) { - saved_errno = errno; - DEBUG(1, ("Can't open the registry")); - if (saved_errno) { - DEBUGADD(1, (": %s", strerror(saved_errno))); - } - DEBUGADD(1, (".\n")); - goto done; - } - reghook_cache_init(); - if (!reghook_cache_add(&smbconf_reg_hook)) { - DEBUG(1, ("Error adding smbconf reghooks to reghook cache.\n")); - goto done; - } - - ret = True; - -done: - return ret; -} diff --git a/source3/lib/util_seaccess.c b/source3/lib/util_seaccess.c index 0481eea5f0..87e70bb95b 100644 --- a/source3/lib/util_seaccess.c +++ b/source3/lib/util_seaccess.c @@ -350,7 +350,9 @@ NTSTATUS samr_make_sam_obj_sd(TALLOC_CTX *ctx, SEC_DESC **psd, size_t *sd_size) if ((psa = make_sec_acl(ctx, NT4_ACL_REVISION, 3, ace)) == NULL) return NT_STATUS_NO_MEMORY; - if ((*psd = make_sec_desc(ctx, SEC_DESC_REVISION, SEC_DESC_SELF_RELATIVE, NULL, NULL, NULL, psa, sd_size)) == NULL) + if ((*psd = make_sec_desc(ctx, SECURITY_DESCRIPTOR_REVISION_1, + SEC_DESC_SELF_RELATIVE, NULL, NULL, NULL, + psa, sd_size)) == NULL) return NT_STATUS_NO_MEMORY; return NT_STATUS_OK; diff --git a/source3/lib/util_sid.c b/source3/lib/util_sid.c index 52f65aa77d..37865238a5 100644 --- a/source3/lib/util_sid.c +++ b/source3/lib/util_sid.c @@ -382,7 +382,7 @@ bool sid_linearize(char *outbuf, size_t len, const DOM_SID *sid) { size_t i; - if (len < sid_size(sid)) + if (len < ndr_size_dom_sid(sid, 0)) return False; SCVAL(outbuf,0,sid->sid_rev_num); @@ -495,18 +495,6 @@ bool sid_equal(const DOM_SID *sid1, const DOM_SID *sid2) } /***************************************************************** - Calculates size of a sid. -*****************************************************************/ - -size_t sid_size(const DOM_SID *sid) -{ - if (sid == NULL) - return 0; - - return sid->num_auths * sizeof(uint32) + 8; -} - -/***************************************************************** Returns true if SID is internal (and non-mappable). *****************************************************************/ @@ -535,7 +523,7 @@ bool non_mappable_sid(DOM_SID *sid) char *sid_binstring(const DOM_SID *sid) { char *buf, *s; - int len = sid_size(sid); + int len = ndr_size_dom_sid(sid, 0); buf = (char *)SMB_MALLOC(len); if (!buf) return NULL; @@ -553,7 +541,7 @@ char *sid_binstring(const DOM_SID *sid) char *sid_binstring_hex(const DOM_SID *sid) { char *buf, *s; - int len = sid_size(sid); + int len = ndr_size_dom_sid(sid, 0); buf = (char *)SMB_MALLOC(len); if (!buf) return NULL; @@ -585,20 +573,20 @@ DOM_SID *sid_dup_talloc(TALLOC_CTX *ctx, const DOM_SID *src) Add SID to an array SIDs ********************************************************************/ -bool add_sid_to_array(TALLOC_CTX *mem_ctx, const DOM_SID *sid, - DOM_SID **sids, size_t *num) +NTSTATUS add_sid_to_array(TALLOC_CTX *mem_ctx, const DOM_SID *sid, + DOM_SID **sids, size_t *num) { *sids = TALLOC_REALLOC_ARRAY(mem_ctx, *sids, DOM_SID, (*num)+1); if (*sids == NULL) { *num = 0; - return False; + return NT_STATUS_NO_MEMORY; } sid_copy(&((*sids)[*num]), sid); *num += 1; - return True; + return NT_STATUS_OK; } @@ -606,14 +594,14 @@ bool add_sid_to_array(TALLOC_CTX *mem_ctx, const DOM_SID *sid, Add SID to an array SIDs ensuring that it is not already there ********************************************************************/ -bool add_sid_to_array_unique(TALLOC_CTX *mem_ctx, const DOM_SID *sid, - DOM_SID **sids, size_t *num_sids) +NTSTATUS add_sid_to_array_unique(TALLOC_CTX *mem_ctx, const DOM_SID *sid, + DOM_SID **sids, size_t *num_sids) { size_t i; for (i=0; i<(*num_sids); i++) { if (sid_compare(sid, &(*sids)[i]) == 0) - return True; + return NT_STATUS_OK; } return add_sid_to_array(mem_ctx, sid, sids, num_sids); @@ -682,6 +670,7 @@ NTSTATUS sid_array_from_info3(TALLOC_CTX *mem_ctx, size_t *num_user_sids, bool include_user_group_rid) { + NTSTATUS status; DOM_SID sid; DOM_SID *sid_array = NULL; size_t num_sids = 0; @@ -689,35 +678,47 @@ NTSTATUS sid_array_from_info3(TALLOC_CTX *mem_ctx, if (include_user_group_rid) { - if (!sid_compose(&sid, &(info3->dom_sid.sid), - info3->user_rid) - || !add_sid_to_array(mem_ctx, &sid, - &sid_array, &num_sids)) { - DEBUG(3,("could not add user SID from rid 0x%x\n", - info3->user_rid)); + if (!sid_compose(&sid, &(info3->dom_sid.sid), info3->user_rid)) + { + DEBUG(3, ("could not compose user SID from rid 0x%x\n", + info3->user_rid)); return NT_STATUS_INVALID_PARAMETER; } + status = add_sid_to_array(mem_ctx, &sid, &sid_array, &num_sids); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(3, ("could not append user SID from rid 0x%x\n", + info3->user_rid)); + return status; + } - if (!sid_compose(&sid, &(info3->dom_sid.sid), - info3->group_rid) - || !add_sid_to_array(mem_ctx, &sid, - &sid_array, &num_sids)) { - DEBUG(3,("could not append additional group rid 0x%x\n", - info3->group_rid)); - + if (!sid_compose(&sid, &(info3->dom_sid.sid), info3->group_rid)) + { + DEBUG(3, ("could not compose group SID from rid 0x%x\n", + info3->group_rid)); return NT_STATUS_INVALID_PARAMETER; } + status = add_sid_to_array(mem_ctx, &sid, &sid_array, &num_sids); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(3, ("could not append group SID from rid 0x%x\n", + info3->group_rid)); + return status; + } } for (i = 0; i < info3->num_groups2; i++) { if (!sid_compose(&sid, &(info3->dom_sid.sid), - info3->gids[i].g_rid) - || !add_sid_to_array(mem_ctx, &sid, - &sid_array, &num_sids)) { - DEBUG(3,("could not append additional group rid 0x%x\n", - info3->gids[i].g_rid)); + info3->gids[i].g_rid)) + { + DEBUG(3, ("could not compose SID from additional group " + "rid 0x%x\n", info3->gids[i].g_rid)); return NT_STATUS_INVALID_PARAMETER; } + status = add_sid_to_array(mem_ctx, &sid, &sid_array, &num_sids); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(3, ("could not append SID from additional group " + "rid 0x%x\n", info3->gids[i].g_rid)); + return status; + } } /* Copy 'other' sids. We need to do sid filtering here to @@ -727,11 +728,12 @@ NTSTATUS sid_array_from_info3(TALLOC_CTX *mem_ctx, */ for (i = 0; i < info3->num_other_sids; i++) { - if (!add_sid_to_array(mem_ctx, &info3->other_sids[i].sid, - &sid_array, &num_sids)) { + status = add_sid_to_array(mem_ctx, &info3->other_sids[i].sid, + &sid_array, &num_sids); + if (!NT_STATUS_IS_OK(status)) { DEBUG(3, ("could not add SID to array: %s\n", sid_string_dbg(&info3->other_sids[i].sid))); - return NT_STATUS_NO_MEMORY; + return status; } } diff --git a/source3/lib/util_sock.c b/source3/lib/util_sock.c index 8f1bd9e686..ced1130536 100644 --- a/source3/lib/util_sock.c +++ b/source3/lib/util_sock.c @@ -476,6 +476,29 @@ bool is_address_any(const struct sockaddr_storage *psa) } /**************************************************************************** + Get a port number in host byte order from a sockaddr_storage. +****************************************************************************/ + +uint16_t get_sockaddr_port(const struct sockaddr_storage *pss) +{ + uint16_t port = 0; + + if (pss->ss_family != AF_INET) { +#if defined(HAVE_IPV6) + /* IPv6 */ + const struct sockaddr_in6 *sa6 = + (const struct sockaddr_in6 *)pss; + port = ntohs(sa6->sin6_port); +#endif + } else { + const struct sockaddr_in *sa = + (const struct sockaddr_in *)pss; + port = ntohs(sa->sin_port); + } + return port; +} + +/**************************************************************************** Print out an IPv4 or IPv6 address from a struct sockaddr_storage. ****************************************************************************/ @@ -518,7 +541,7 @@ char *print_canonical_sockaddr(TALLOC_CTX *ctx, char *dest = NULL; int ret; - ret = getnameinfo((const struct sockaddr *)pss, + ret = sys_getnameinfo((const struct sockaddr *)pss, sizeof(struct sockaddr_storage), addr, sizeof(addr), NULL, 0, @@ -1031,44 +1054,7 @@ ssize_t read_socket_with_timeout(int fd, ssize_t read_data(int fd,char *buffer,size_t N, enum smb_read_errors *pre) { - ssize_t ret; - size_t total=0; - char addr[INET6_ADDRSTRLEN]; - - set_smb_read_error(pre,SMB_READ_OK); - - while (total < N) { - ret = sys_read(fd,buffer + total,N - total); - - if (ret == 0) { - DEBUG(10,("read_data: read of %d returned 0. " - "Error = %s\n", - (int)(N - total), strerror(errno) )); - set_smb_read_error(pre,SMB_READ_EOF); - return 0; - } - - if (ret == -1) { - if (fd == get_client_fd()) { - /* Try and give an error message saying - * what client failed. */ - DEBUG(0,("read_data: read failure for %d " - "bytes to client %s. Error = %s\n", - (int)(N - total), - get_peer_addr(fd,addr,sizeof(addr)), - strerror(errno) )); - } else { - DEBUG(0,("read_data: read failure for %d. " - "Error = %s\n", - (int)(N - total), - strerror(errno) )); - } - set_smb_read_error(pre,SMB_READ_ERROR); - return -1; - } - total += ret; - } - return (ssize_t)total; + return read_socket_with_timeout(fd, buffer, N, N, 0, pre); } /**************************************************************************** @@ -1137,25 +1123,16 @@ ssize_t read_smb_length_return_keepalive(int fd, { ssize_t len=0; int msg_type; - bool ok = false; - while (!ok) { - if (timeout > 0) { - ok = (read_socket_with_timeout(fd,inbuf,4,4, - timeout,pre) == 4); - } else { - ok = (read_data(fd,inbuf,4,pre) == 4); - } - if (!ok) { - return -1; - } + if (read_socket_with_timeout(fd, inbuf, 4, 4, timeout, pre) != 4) { + return -1; + } - len = smb_len(inbuf); - msg_type = CVAL(inbuf,0); + len = smb_len(inbuf); + msg_type = CVAL(inbuf,0); - if (msg_type == SMBkeepalive) { - DEBUG(5,("Got keepalive packet\n")); - } + if (msg_type == SMBkeepalive) { + DEBUG(5,("Got keepalive packet\n")); } DEBUG(10,("got smb length of %lu\n",(unsigned long)len)); @@ -1173,16 +1150,15 @@ ssize_t read_smb_length_return_keepalive(int fd, ssize_t read_smb_length(int fd, char *inbuf, unsigned int timeout, enum smb_read_errors *pre) { ssize_t len; + uint8_t msgtype = SMBkeepalive; - for(;;) { - len = read_smb_length_return_keepalive(fd, inbuf, timeout, pre); - - if(len < 0) + while (msgtype == SMBkeepalive) { + len = read_smb_length_return_keepalive(fd, inbuf, timeout, + pre); + if (len < 0) { return len; - - /* Ignore session keepalives. */ - if(CVAL(inbuf,0) != SMBkeepalive) - break; + } + msgtype = CVAL(inbuf, 0); } DEBUG(10,("read_smb_length: got smb length of %lu\n", @@ -1251,16 +1227,8 @@ ssize_t receive_smb_raw(int fd, len = MIN(len,maxlen); } - if (timeout > 0) { - ret = read_socket_with_timeout(fd, - buffer+4, - len, - len, - timeout, - pre); - } else { - ret = read_data(fd,buffer+4,len,pre); - } + ret = read_socket_with_timeout(fd, buffer+4, len, len, timeout, + pre); if (ret != len) { cond_set_smb_read_error(pre,SMB_READ_ERROR); @@ -1277,56 +1245,6 @@ ssize_t receive_smb_raw(int fd, } /**************************************************************************** - Wrapper for receive_smb_raw(). - Checks the MAC on signed packets. -****************************************************************************/ - -bool receive_smb(int fd, char *buffer, unsigned int timeout, enum smb_read_errors *pre) -{ - if (receive_smb_raw(fd, buffer, timeout, 0, pre) < 0) { - return false; - } - - /* Check the incoming SMB signature. */ - if (!srv_check_sign_mac(buffer, true)) { - DEBUG(0, ("receive_smb: SMB Signature verification " - "failed on incoming packet!\n")); - cond_set_smb_read_error(pre,SMB_READ_BAD_SIG); - return false; - } - - return true; -} - -/**************************************************************************** - Send an smb to a fd. -****************************************************************************/ - -bool send_smb(int fd, char *buffer) -{ - size_t len; - size_t nwritten=0; - ssize_t ret; - - /* Sign the outgoing packet if required. */ - srv_calculate_sign_mac(buffer); - - len = smb_len(buffer) + 4; - - while (nwritten < len) { - ret = write_data(fd,buffer+nwritten,len - nwritten); - if (ret <= 0) { - DEBUG(0,("Error writing %d bytes to client. %d. (%s)\n", - (int)len,(int)ret, strerror(errno) )); - return false; - } - nwritten += ret; - } - - return true; -} - -/**************************************************************************** Open a socket of the specified type, port, and address for incoming data. ****************************************************************************/ @@ -1800,18 +1718,66 @@ static bool matchname(const char *remotehost, return false; } -static struct { - struct sockaddr_storage ss; - char *name; -} nc; +/******************************************************************* + Deal with the singleton cache. +******************************************************************/ + +struct name_addr_pair { + struct sockaddr_storage ss; + const char *name; +}; + +/******************************************************************* + Lookup a name/addr pair. Returns memory allocated from memcache. +******************************************************************/ + +static bool lookup_nc(struct name_addr_pair *nc) +{ + DATA_BLOB tmp; + + ZERO_STRUCTP(nc); + + if (!memcache_lookup( + NULL, SINGLETON_CACHE, + data_blob_string_const("get_peer_name"), + &tmp)) { + return false; + } + + memcpy(&nc->ss, tmp.data, sizeof(nc->ss)); + nc->name = (const char *)tmp.data + sizeof(nc->ss); + return true; +} + +/******************************************************************* + Save a name/addr pair. +******************************************************************/ + +static void store_nc(const struct name_addr_pair *nc) +{ + DATA_BLOB tmp; + size_t namelen = strlen(nc->name); + + tmp = data_blob(NULL, sizeof(nc->ss) + namelen + 1); + if (!tmp.data) { + return; + } + memcpy(tmp.data, &nc->ss, sizeof(nc->ss)); + memcpy(tmp.data+sizeof(nc->ss), nc->name, namelen+1); + + memcache_add(NULL, SINGLETON_CACHE, + data_blob_string_const("get_peer_name"), + tmp); + data_blob_free(&tmp); +} /******************************************************************* Return the DNS name of the remote end of a socket. ******************************************************************/ -const char *get_peer_name(int fd, - bool force_lookup) +const char *get_peer_name(int fd, bool force_lookup) { + struct name_addr_pair nc; char addr_buf[INET6_ADDRSTRLEN]; struct sockaddr_storage ss; socklen_t length = sizeof(ss); @@ -1826,13 +1792,15 @@ const char *get_peer_name(int fd, possible */ if (!lp_hostname_lookups() && (force_lookup == false)) { length = sizeof(nc.ss); - p = get_peer_addr_internal(fd, addr_buf, sizeof(addr_buf), + nc.name = get_peer_addr_internal(fd, addr_buf, sizeof(addr_buf), &nc.ss, &length); - SAFE_FREE(nc.name); - nc.name = SMB_STRDUP(p); + store_nc(&nc); + lookup_nc(&nc); return nc.name ? nc.name : "UNKNOWN"; } + lookup_nc(&nc); + memset(&ss, '\0', sizeof(ss)); p = get_peer_addr_internal(fd, addr_buf, sizeof(addr_buf), &ss, &length); @@ -1841,15 +1809,13 @@ const char *get_peer_name(int fd, return nc.name ? nc.name : "UNKNOWN"; } - /* Not the same. Reset the cache. */ - zero_addr(&nc.ss); - SAFE_FREE(nc.name); + /* Not the same. We need to lookup. */ if (fd == -1) { return "UNKNOWN"; } /* Look up the remote host name. */ - ret = getnameinfo((struct sockaddr *)&ss, + ret = sys_getnameinfo((struct sockaddr *)&ss, length, name_buf, sizeof(name_buf), @@ -1880,7 +1846,11 @@ const char *get_peer_name(int fd, strlcpy(name_buf, "UNKNOWN", sizeof(name_buf)); } - nc.name = SMB_STRDUP(name_buf); + nc.name = name_buf; + nc.ss = ss; + + store_nc(&nc); + lookup_nc(&nc); return nc.name ? nc.name : "UNKNOWN"; } @@ -2002,50 +1972,69 @@ out_umask: const char *get_mydnsfullname(void) { - static char *dnshostname_cache; - - if (dnshostname_cache == NULL || !*dnshostname_cache) { - struct addrinfo *res = NULL; - char my_hostname[HOST_NAME_MAX]; - bool ret; + struct addrinfo *res = NULL; + char my_hostname[HOST_NAME_MAX]; + bool ret; + DATA_BLOB tmp; - /* get my host name */ - if (gethostname(my_hostname, sizeof(my_hostname)) == -1) { - DEBUG(0,("get_mydnsfullname: gethostname failed\n")); - return NULL; - } + if (memcache_lookup(NULL, SINGLETON_CACHE, + data_blob_string_const("get_mydnsfullname"), + &tmp)) { + SMB_ASSERT(tmp.length > 0); + return (const char *)tmp.data; + } - /* Ensure null termination. */ - my_hostname[sizeof(my_hostname)-1] = '\0'; + /* get my host name */ + if (gethostname(my_hostname, sizeof(my_hostname)) == -1) { + DEBUG(0,("get_mydnsfullname: gethostname failed\n")); + return NULL; + } - ret = interpret_string_addr_internal(&res, - my_hostname, - AI_ADDRCONFIG|AI_CANONNAME); + /* Ensure null termination. */ + my_hostname[sizeof(my_hostname)-1] = '\0'; - if (!ret || res == NULL) { - DEBUG(3,("get_mydnsfullname: getaddrinfo failed for " - "name %s [%s]\n", + ret = interpret_string_addr_internal(&res, my_hostname, - gai_strerror(ret) )); - return NULL; - } + AI_ADDRCONFIG|AI_CANONNAME); - /* - * Make sure that getaddrinfo() returns the "correct" host name. - */ + if (!ret || res == NULL) { + DEBUG(3,("get_mydnsfullname: getaddrinfo failed for " + "name %s [%s]\n", + my_hostname, + gai_strerror(ret) )); + return NULL; + } - if (res->ai_canonname == NULL) { - DEBUG(3,("get_mydnsfullname: failed to get " - "canonical name for %s\n", - my_hostname)); - freeaddrinfo(res); - return NULL; - } + /* + * Make sure that getaddrinfo() returns the "correct" host name. + */ - dnshostname_cache = SMB_STRDUP(res->ai_canonname); + if (res->ai_canonname == NULL) { + DEBUG(3,("get_mydnsfullname: failed to get " + "canonical name for %s\n", + my_hostname)); freeaddrinfo(res); + return NULL; } - return dnshostname_cache; + + /* This copies the data, so we must do a lookup + * afterwards to find the value to return. + */ + + memcache_add(NULL, SINGLETON_CACHE, + data_blob_string_const("get_mydnsfullname"), + data_blob_string_const(res->ai_canonname)); + + if (!memcache_lookup(NULL, SINGLETON_CACHE, + data_blob_string_const("get_mydnsfullname"), + &tmp)) { + tmp = data_blob_talloc(talloc_tos(), res->ai_canonname, + strlen(res->ai_canonname) + 1); + } + + freeaddrinfo(res); + + return (const char *)tmp.data; } /************************************************************ diff --git a/source3/lib/util_str.c b/source3/lib/util_str.c index ee76e33de8..bcb9197141 100644 --- a/source3/lib/util_str.c +++ b/source3/lib/util_str.c @@ -24,6 +24,17 @@ #include "includes.h" +char toupper_ascii_fast_table[128] = { + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, + 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f +}; + /** * @file * @brief String utilities. @@ -137,83 +148,6 @@ bool next_token_no_ltrim_talloc(TALLOC_CTX *ctx, } /** -This is like next_token but is not re-entrant and "remembers" the first -parameter so you can pass NULL. This is useful for user interface code -but beware the fact that it is not re-entrant! -**/ - -static const char *last_ptr=NULL; - -bool next_token_nr_talloc(TALLOC_CTX *ctx, - const char **ptr, - char **pp_buff, - const char *sep) -{ - bool ret; - if (!ptr) { - ptr = &last_ptr; - } - - ret = next_token_talloc(ctx, ptr, pp_buff, sep); - last_ptr = *ptr; - return ret; -} - -void set_first_token(char *ptr) -{ - last_ptr = ptr; -} - -/** - Convert list of tokens to array; dependent on above routine. - Uses last_ptr from above - bit of a hack. -**/ - -char **toktocliplist(int *ctok, const char *sep) -{ - char *s=(char *)last_ptr; - int ictok=0; - char **ret, **iret; - - if (!sep) - sep = " \t\n\r"; - - while(*s && strchr_m(sep,*s)) - s++; - - /* nothing left? */ - if (!*s) - return(NULL); - - do { - ictok++; - while(*s && (!strchr_m(sep,*s))) - s++; - while(*s && strchr_m(sep,*s)) - *s++=0; - } while(*s); - - *ctok=ictok; - s=(char *)last_ptr; - - if (!(ret=iret=SMB_MALLOC_ARRAY(char *,ictok+1))) - return NULL; - - while(ictok--) { - *iret++=s; - if (ictok > 0) { - while(*s++) - ; - while(!*s) - s++; - } - } - - ret[*ctok] = NULL; - return ret; -} - -/** * Case insensitive string compararison. * * iconv does not directly give us a way to compare strings in @@ -264,8 +198,8 @@ int StrCaseCmp(const char *s, const char *t) * from here on in */ break; - us = toupper_ascii(*ps); - ut = toupper_ascii(*pt); + us = toupper_ascii_fast(*ps); + ut = toupper_ascii_fast(*pt); if (us == ut) continue; else if (us < ut) @@ -323,8 +257,8 @@ int StrnCaseCmp(const char *s, const char *t, size_t len) * hard way from here on in */ break; - us = toupper_ascii(*ps); - ut = toupper_ascii(*pt); + us = toupper_ascii_fast(*ps); + ut = toupper_ascii_fast(*pt); if (us == ut) continue; else if (us < ut) @@ -1756,7 +1690,7 @@ void strupper_m(char *s) (ie. they match for the first 128 chars) */ while (*s && !(((unsigned char)s[0]) & 0x80)) { - *s = toupper_ascii((unsigned char)*s); + *s = toupper_ascii_fast((unsigned char)*s); s++; } @@ -2481,13 +2415,13 @@ void base64_decode_inplace(char *s) } /** - * Encode a base64 string into a malloc()ed string caller to free. + * Encode a base64 string into a talloc()ed string caller to free. * * From SQUID: adopted from http://ftp.sunet.se/pub2/gnu/vm/base64-encode.c * with adjustments **/ -char *base64_encode_data_blob(DATA_BLOB data) +char *base64_encode_data_blob(TALLOC_CTX *mem_ctx, DATA_BLOB data) { int bits = 0; int char_count = 0; @@ -2500,7 +2434,7 @@ char *base64_encode_data_blob(DATA_BLOB data) out_cnt = 0; len = data.length; output_len = data.length * 2; - result = TALLOC_ARRAY(talloc_tos(), char, output_len); /* get us plenty of space */ + result = TALLOC_ARRAY(mem_ctx, char, output_len); /* get us plenty of space */ SMB_ASSERT(result != NULL); while (len-- && out_cnt < (data.length * 2) - 5) { diff --git a/source3/lib/util_tdb.c b/source3/lib/util_tdb.c index ce2cb427d1..dd5ebcd7ab 100644 --- a/source3/lib/util_tdb.c +++ b/source3/lib/util_tdb.c @@ -656,6 +656,7 @@ int tdb_unpack(const uint8 *buf, int bufsize, const char *fmt, ...) return PTR_DIFF(buf, buf0); no_space: + va_end(ap); return -1; } diff --git a/source3/lib/util_transfer_file.c b/source3/lib/util_transfer_file.c new file mode 100644 index 0000000000..1e3b76fc77 --- /dev/null +++ b/source3/lib/util_transfer_file.c @@ -0,0 +1,110 @@ +/* + * Unix SMB/CIFS implementation. + * Utility functions to transfer files. + * + * Copyright (C) Jeremy Allison 2001-2002 + * Copyright (C) Michael Adam 2008 + * + * 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 + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + + +#include <includes.h> + +/**************************************************************************** + Transfer some data between two fd's. +****************************************************************************/ + +#ifndef TRANSFER_BUF_SIZE +#define TRANSFER_BUF_SIZE 65536 +#endif + + +ssize_t transfer_file_internal(void *in_file, + void *out_file, + size_t n, + ssize_t (*read_fn)(void *, void *, size_t), + ssize_t (*write_fn)(void *, const void *, size_t)) +{ + char *buf; + size_t total = 0; + ssize_t read_ret; + ssize_t write_ret; + size_t num_to_read_thistime; + size_t num_written = 0; + + if ((buf = SMB_MALLOC_ARRAY(char, TRANSFER_BUF_SIZE)) == NULL) { + return -1; + } + + while (total < n) { + num_to_read_thistime = MIN((n - total), TRANSFER_BUF_SIZE); + + read_ret = (*read_fn)(in_file, buf, num_to_read_thistime); + if (read_ret == -1) { + DEBUG(0,("transfer_file_internal: read failure. " + "Error = %s\n", strerror(errno) )); + SAFE_FREE(buf); + return -1; + } + if (read_ret == 0) { + break; + } + + num_written = 0; + + while (num_written < read_ret) { + write_ret = (*write_fn)(out_file, buf + num_written, + read_ret - num_written); + + if (write_ret == -1) { + DEBUG(0,("transfer_file_internal: " + "write failure. Error = %s\n", + strerror(errno) )); + SAFE_FREE(buf); + return -1; + } + if (write_ret == 0) { + return (ssize_t)total; + } + + num_written += (size_t)write_ret; + } + + total += (size_t)read_ret; + } + + SAFE_FREE(buf); + return (ssize_t)total; +} + +static ssize_t sys_read_fn(void *file, void *buf, size_t len) +{ + int *fd = (int *)file; + + return sys_read(*fd, buf, len); +} + +static ssize_t sys_write_fn(void *file, const void *buf, size_t len) +{ + int *fd = (int *)file; + + return sys_write(*fd, buf, len); +} + +SMB_OFF_T transfer_file(int infd, int outfd, SMB_OFF_T n) +{ + return (SMB_OFF_T)transfer_file_internal(&infd, &outfd, (size_t)n, + sys_read_fn, sys_write_fn); +} diff --git a/source3/lib/version.c b/source3/lib/version.c index 204c2044a8..3cae02ad2e 100644 --- a/source3/lib/version.c +++ b/source3/lib/version.c @@ -51,6 +51,8 @@ const char *samba_version_string(void) */ assert(res != -1); + SAFE_FREE(samba_version); + samba_version = tmp_version; #endif diff --git a/source3/lib/winbind_util.c b/source3/lib/winbind_util.c new file mode 100644 index 0000000000..14356b09cf --- /dev/null +++ b/source3/lib/winbind_util.c @@ -0,0 +1,321 @@ +/* + Unix SMB/CIFS implementation. + Winbind Utility functions + + Copyright (C) Gerald (Jerry) Carter 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 + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" + +#if defined(WITH_WINBIND) + +#include "nsswitch/libwbclient/wbclient.h" + +/* Call winbindd to convert a name to a sid */ + +bool winbind_lookup_name(const char *dom_name, const char *name, DOM_SID *sid, + enum lsa_SidType *name_type) +{ + struct wbcDomainSid dom_sid; + wbcErr result; + enum wbcSidType type; + + result = wbcLookupName(dom_name, name, &dom_sid, &type); + if (result != WBC_ERR_SUCCESS) + return false; + + memcpy(sid, &dom_sid, sizeof(DOM_SID)); + *name_type = (enum lsa_SidType)type; + + return true; +} + +/* Call winbindd to convert sid to name */ + +bool winbind_lookup_sid(TALLOC_CTX *mem_ctx, const DOM_SID *sid, + const char **domain, const char **name, + enum lsa_SidType *name_type) +{ + struct wbcDomainSid dom_sid; + wbcErr result; + enum wbcSidType type; + char *domain_name = NULL; + char *account_name = NULL; + + memcpy(&dom_sid, sid, sizeof(dom_sid)); + + result = wbcLookupSid(&dom_sid, &domain_name, &account_name, &type); + if (result != WBC_ERR_SUCCESS) + return false; + + /* Copy out result */ + + if (domain) { + *domain = talloc_strdup(mem_ctx, domain_name); + } + if (name) { + *name = talloc_strdup(mem_ctx, account_name); + } + *name_type = (enum lsa_SidType)type; + + DEBUG(10, ("winbind_lookup_sid: SUCCESS: SID %s -> %s %s\n", + sid_string_dbg(sid), domain_name, account_name)); + + wbcFreeMemory(domain_name); + wbcFreeMemory(account_name); + + if ((domain && !*domain) || (name && !*name)) { + DEBUG(0,("winbind_lookup_sid: talloc() failed!\n")); + return false; + } + + + return true; +} + +/* Ping winbindd to see it is alive */ + +bool winbind_ping(void) +{ + wbcErr result = wbcPing(); + + return (result == WBC_ERR_SUCCESS); +} + +/* Call winbindd to convert SID to uid */ + +bool winbind_sid_to_uid(uid_t *puid, const DOM_SID *sid) +{ + struct wbcDomainSid dom_sid; + wbcErr result; + + memcpy(&dom_sid, sid, sizeof(dom_sid)); + + result = wbcSidToUid(&dom_sid, puid); + + return (result == WBC_ERR_SUCCESS); +} + +/* Call winbindd to convert uid to sid */ + +bool winbind_uid_to_sid(DOM_SID *sid, uid_t uid) +{ + struct wbcDomainSid dom_sid; + wbcErr result; + + result = wbcUidToSid(uid, &dom_sid); + if (result == WBC_ERR_SUCCESS) { + memcpy(sid, &dom_sid, sizeof(DOM_SID)); + } else { + sid_copy(sid, &global_sid_NULL); + } + + return (result == WBC_ERR_SUCCESS); +} + +/* Call winbindd to convert SID to gid */ + +bool winbind_sid_to_gid(gid_t *pgid, const DOM_SID *sid) +{ + struct wbcDomainSid dom_sid; + wbcErr result; + + memcpy(&dom_sid, sid, sizeof(dom_sid)); + + result = wbcSidToGid(&dom_sid, pgid); + + return (result == WBC_ERR_SUCCESS); +} + +/* Call winbindd to convert gid to sid */ + +bool winbind_gid_to_sid(DOM_SID *sid, gid_t gid) +{ + struct wbcDomainSid dom_sid; + wbcErr result; + + result = wbcGidToSid(gid, &dom_sid); + if (result == WBC_ERR_SUCCESS) { + memcpy(sid, &dom_sid, sizeof(DOM_SID)); + } else { + sid_copy(sid, &global_sid_NULL); + } + + return (result == WBC_ERR_SUCCESS); +} + +/* Check for a trusted domain */ + +wbcErr wb_is_trusted_domain(const char *domain) +{ + wbcErr result; + struct wbcDomainInfo *info = NULL; + + result = wbcDomainInfo(domain, &info); + + if (WBC_ERROR_IS_OK(result)) { + wbcFreeMemory(info); + } + + return result; +} + +/* Lookup a set of rids in a given domain */ + +bool winbind_lookup_rids(TALLOC_CTX *mem_ctx, + const DOM_SID *domain_sid, + int num_rids, uint32 *rids, + const char **domain_name, + const char ***names, enum lsa_SidType **types) +{ + const char *dom_name = NULL; + const char **namelist = NULL; + enum wbcSidType *name_types = NULL; + struct wbcDomainSid dom_sid; + wbcErr ret; + int i; + + memcpy(&dom_sid, domain_sid, sizeof(struct wbcDomainSid)); + + ret = wbcLookupRids(&dom_sid, num_rids, rids, + &dom_name, &namelist, &name_types); + if (ret != WBC_ERR_SUCCESS) { + return false; + } + + *domain_name = talloc_strdup(mem_ctx, dom_name); + *names = TALLOC_ARRAY(mem_ctx, const char*, num_rids); + *types = TALLOC_ARRAY(mem_ctx, enum lsa_SidType, num_rids); + + for(i=0; i<num_rids; i++) { + (*names)[i] = talloc_strdup(*names, namelist[i]); + (*types)[i] = (enum lsa_SidType)name_types[i]; + } + + wbcFreeMemory(CONST_DISCARD(char*, dom_name)); + wbcFreeMemory(namelist); + wbcFreeMemory(name_types); + + return true; +} + +/* Ask Winbind to allocate a new uid for us */ + +bool winbind_allocate_uid(uid_t *uid) +{ + wbcErr ret; + + ret = wbcAllocateUid(uid); + + return (ret == WBC_ERR_SUCCESS); +} + +/* Ask Winbind to allocate a new gid for us */ + +bool winbind_allocate_gid(gid_t *gid) +{ + wbcErr ret; + + ret = wbcAllocateGid(gid); + + return (ret == WBC_ERR_SUCCESS); +} + +#else /* WITH_WINBIND */ + +bool winbind_lookup_name(const char *dom_name, const char *name, DOM_SID *sid, + enum lsa_SidType *name_type) +{ + return false; +} + +/* Call winbindd to convert sid to name */ + +bool winbind_lookup_sid(TALLOC_CTX *mem_ctx, const DOM_SID *sid, + const char **domain, const char **name, + enum lsa_SidType *name_type) +{ + return false; +} + +/* Ping winbindd to see it is alive */ + +bool winbind_ping(void) +{ + return false; +} + +/* Call winbindd to convert SID to uid */ + +bool winbind_sid_to_uid(uid_t *puid, const DOM_SID *sid) +{ + return false; +} + +/* Call winbindd to convert uid to sid */ + +bool winbind_uid_to_sid(DOM_SID *sid, uid_t uid) +{ + return false; +} + +/* Call winbindd to convert SID to gid */ + +bool winbind_sid_to_gid(gid_t *pgid, const DOM_SID *sid) +{ + return false; +} + +/* Call winbindd to convert gid to sid */ + +bool winbind_gid_to_sid(DOM_SID *sid, gid_t gid) +{ + return false; +} + +/* Check for a trusted domain */ + +wbcErr wb_is_trusted_domain(const char *domain) +{ + return WBC_ERR_UNKNOWN_FAILURE; +} + +/* Lookup a set of rids in a given domain */ + +bool winbind_lookup_rids(TALLOC_CTX *mem_ctx, + const DOM_SID *domain_sid, + int num_rids, uint32 *rids, + const char **domain_name, + const char ***names, enum lsa_SidType **types) +{ + return false; +} + +/* Ask Winbind to allocate a new uid for us */ + +bool winbind_allocate_uid(uid_t *uid) +{ + return false; +} + +/* Ask Winbind to allocate a new gid for us */ + +bool winbind_allocate_gid(gid_t *gid) +{ + return false; +} + +#endif /* WITH_WINBIND */ diff --git a/source3/lib/xfile.c b/source3/lib/xfile.c index c98522200b..ee6e581332 100644 --- a/source3/lib/xfile.c +++ b/source3/lib/xfile.c @@ -223,9 +223,15 @@ size_t x_fwrite(const void *p, size_t size, size_t nmemb, XFILE *f) VA_COPY(ap2, ap); len = vasprintf(&p, format, ap2); - if (len <= 0) return len; + if (len <= 0) { + va_end(ap2); + return len; + } ret = x_fwrite(p, 1, len, f); SAFE_FREE(p); + + va_end(ap2); + return ret; } |