diff options
author | Andrew Tridgell <tridge@samba.org> | 2003-08-13 01:53:07 +0000 |
---|---|---|
committer | Andrew Tridgell <tridge@samba.org> | 2003-08-13 01:53:07 +0000 |
commit | ef2e26c91b80556af033d3335e55f5dfa6fff31d (patch) | |
tree | faa21bfd7e7b5247250b47c7891dc1a5ebee6be9 /source4/lib | |
download | samba-ef2e26c91b80556af033d3335e55f5dfa6fff31d.tar.gz samba-ef2e26c91b80556af033d3335e55f5dfa6fff31d.tar.bz2 samba-ef2e26c91b80556af033d3335e55f5dfa6fff31d.zip |
first public release of samba4 code
(This used to be commit b0510b5428b3461aeb9bbe3cc95f62fc73e2b97f)
Diffstat (limited to 'source4/lib')
90 files changed, 30905 insertions, 0 deletions
diff --git a/source4/lib/.cvsignore b/source4/lib/.cvsignore new file mode 100644 index 0000000000..07da2225c7 --- /dev/null +++ b/source4/lib/.cvsignore @@ -0,0 +1,3 @@ +*.po +*.po32 + diff --git a/source4/lib/account_pol.c b/source4/lib/account_pol.c new file mode 100644 index 0000000000..df1479da3a --- /dev/null +++ b/source4/lib/account_pol.c @@ -0,0 +1,169 @@ +/* + * Unix SMB/CIFS implementation. + * account policy storage + * Copyright (C) Jean Fran�ois Micouleau 1998-2001. + * Copyright (C) Andrew Bartlett 2002 + * + * 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" +static TDB_CONTEXT *tdb; /* used for driver files */ + +#define DATABASE_VERSION 1 + +/**************************************************************************** + Open the account policy tdb. +****************************************************************************/ + +BOOL init_account_policy(void) +{ + static pid_t local_pid; + const char *vstring = "INFO/version"; + uint32 version; + TALLOC_CTX *mem_ctx; + + if (tdb && local_pid == getpid()) + return True; + mem_ctx = talloc_init("init_account_policy"); + if (!mem_ctx) { + DEBUG(0,("No memory to open account policy database\n")); + return False; + } + tdb = tdb_open_log(lock_path(mem_ctx, "account_policy.tdb"), 0, TDB_DEFAULT, O_RDWR|O_CREAT, 0600); + talloc_destroy(mem_ctx); + if (!tdb) { + DEBUG(0,("Failed to open account policy database\n")); + return False; + } + + local_pid = getpid(); + + /* handle a Samba upgrade */ + tdb_lock_bystring(tdb, vstring,0); + if (!tdb_fetch_uint32(tdb, vstring, &version) || version != DATABASE_VERSION) { + tdb_traverse(tdb, tdb_traverse_delete_fn, NULL); + tdb_store_uint32(tdb, vstring, DATABASE_VERSION); + + account_policy_set(AP_MIN_PASSWORD_LEN, MINPASSWDLENGTH); /* 5 chars minimum */ + account_policy_set(AP_PASSWORD_HISTORY, 0); /* don't keep any old password */ + account_policy_set(AP_USER_MUST_LOGON_TO_CHG_PASS, 0); /* don't force user to logon */ + account_policy_set(AP_MAX_PASSWORD_AGE, MAX_PASSWORD_AGE); /* 21 days */ + account_policy_set(AP_MIN_PASSWORD_AGE, 0); /* 0 days */ + account_policy_set(AP_LOCK_ACCOUNT_DURATION, 0); /* lockout for 0 minutes */ + account_policy_set(AP_RESET_COUNT_TIME, 0); /* reset immediatly */ + account_policy_set(AP_BAD_ATTEMPT_LOCKOUT, 0); /* don't lockout */ + account_policy_set(AP_TIME_TO_LOGOUT, -1); /* don't force logout */ + } + tdb_unlock_bystring(tdb, vstring); + + return True; +} + +static const struct { + int field; + const char *string; +} account_policy_names[] = { + {AP_MIN_PASSWORD_LEN, "min password length"}, + {AP_PASSWORD_HISTORY, "password history"}, + {AP_USER_MUST_LOGON_TO_CHG_PASS, "user must logon to change password"}, + {AP_MAX_PASSWORD_AGE, "maximum password age"}, + {AP_MIN_PASSWORD_AGE,"minimum password age"}, + {AP_LOCK_ACCOUNT_DURATION, "lockout duration"}, + {AP_RESET_COUNT_TIME, "reset count minutes"}, + {AP_BAD_ATTEMPT_LOCKOUT, "bad lockout attempt"}, + {AP_TIME_TO_LOGOUT, "disconnect time"}, + {0, NULL} +}; + +/**************************************************************************** +Get the account policy name as a string from its #define'ed number +****************************************************************************/ + +static const char *decode_account_policy_name(int field) +{ + int i; + for (i=0; account_policy_names[i].string; i++) { + if (field == account_policy_names[i].field) + return account_policy_names[i].string; + } + return NULL; + +} + +/**************************************************************************** +Get the account policy name as a string from its #define'ed number +****************************************************************************/ + +int account_policy_name_to_fieldnum(const char *name) +{ + int i; + for (i=0; account_policy_names[i].string; i++) { + if (strcmp(name, account_policy_names[i].string) == 0) + return account_policy_names[i].field; + } + return 0; + +} + + +/**************************************************************************** +****************************************************************************/ +BOOL account_policy_get(int field, uint32 *value) +{ + fstring name; + + init_account_policy(); + + *value = 0; + + fstrcpy(name, decode_account_policy_name(field)); + if (!*name) { + DEBUG(1, ("account_policy_get: Field %d is not a valid account policy type! Cannot get, returning 0.\n", field)); + return False; + } + if (!tdb_fetch_uint32(tdb, name, value)) { + DEBUG(1, ("account_policy_get: tdb_fetch_uint32 failed for efild %d (%s), returning 0", field, name)); + return False; + } + DEBUG(10,("account_policy_get: %s:%d\n", name, *value)); + return True; +} + + +/**************************************************************************** +****************************************************************************/ +BOOL account_policy_set(int field, uint32 value) +{ + fstring name; + + init_account_policy(); + + fstrcpy(name, decode_account_policy_name(field)); + if (!*name) { + DEBUG(1, ("Field %d is not a valid account policy type! Cannot set.\n", field)); + return False; + } + + if (!tdb_store_uint32(tdb, name, value)) { + DEBUG(1, ("tdb_store_uint32 failed for field %d (%s) on value %u", field, name, value)); + return False; + } + + DEBUG(10,("account_policy_set: %s:%d\n", name, value)); + + return True; +} + diff --git a/source4/lib/adt_tree.c b/source4/lib/adt_tree.c new file mode 100644 index 0000000000..0bc224ec23 --- /dev/null +++ b/source4/lib/adt_tree.c @@ -0,0 +1,464 @@ +/* + * Unix SMB/CIFS implementation. + * Generic Abstract Data Types + * Copyright (C) Gerald Carter 2002. + * + * 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" + + +/************************************************************************** + Initialize the tree's root. The cmp_fn is a callback function used + for comparision of two children + *************************************************************************/ + +static BOOL trim_tree_keypath( char *path, char **base, char **new_path ) +{ + char *p; + + *new_path = *base = NULL; + + if ( !path ) + return False; + + *base = path; + + p = strchr( path, '/' ); + + if ( p ) { + *p = '\0'; + *new_path = p+1; + } + + return True; +} + + +/************************************************************************** + Initialize the tree's root. The cmp_fn is a callback function used + for comparision of two children + *************************************************************************/ + +SORTED_TREE* sorted_tree_init( void *data_p, + int (cmp_fn)(void*, void*), + void (free_fn)(void*) ) +{ + SORTED_TREE *tree = NULL; + + if ( !(tree = (SORTED_TREE*)malloc( sizeof(SORTED_TREE) )) ) + return NULL; + + ZERO_STRUCTP( tree ); + + tree->compare = cmp_fn; + tree->free = free_fn; + + if ( !(tree->root = (TREE_NODE*)malloc( sizeof(TREE_NODE) )) ) { + SAFE_FREE( tree ); + return NULL; + } + + ZERO_STRUCTP( tree->root ); + tree->root->data_p = data_p; + + return tree; +} + + +/************************************************************************** + Delete a tree and free all allocated memory + *************************************************************************/ + +static void sorted_tree_destroy_children( TREE_NODE *root ) +{ + int i; + + if ( !root ) + return; + + for ( i=0; i<root->num_children; i++ ) + { + sorted_tree_destroy_children( root->children[i] ); + } + + SAFE_FREE( root->children ); + SAFE_FREE( root->key ); + + return; +} + +/************************************************************************** + Delete a tree and free all allocated memory + *************************************************************************/ + +void sorted_tree_destroy( SORTED_TREE *tree ) +{ + if ( tree->root ) + sorted_tree_destroy_children( tree->root ); + + if ( tree->free ) + tree->free( tree->root ); + + SAFE_FREE( tree ); +} + +/************************************************************************** + Find the next child given a key string + *************************************************************************/ + +static TREE_NODE* sorted_tree_birth_child( TREE_NODE *node, char* key ) +{ + TREE_NODE *infant = NULL; + TREE_NODE **siblings; + int i; + + if ( !(infant = (TREE_NODE*)malloc( sizeof(TREE_NODE) )) ) + return NULL; + + ZERO_STRUCTP( infant ); + + infant->key = strdup( key ); + infant->parent = node; + + siblings = Realloc( node->children, sizeof(TREE_NODE*)*(node->num_children+1) ); + + if ( siblings ) + node->children = siblings; + + node->num_children++; + + /* first child */ + + if ( node->num_children == 1 ) { + DEBUG(11,("sorted_tree_birth_child: First child of node [%s]! [%s]\n", + node->key ? node->key : "NULL", infant->key )); + node->children[0] = infant; + } + else + { + /* + * multiple siblings .... (at least 2 children) + * + * work from the end of the list forward + * The last child is not set at this point + * Insert the new infanct in ascending order + * from left to right + */ + + for ( i = node->num_children-1; i>=1; i-- ) + { + DEBUG(11,("sorted_tree_birth_child: Looking for crib; infant -> [%s], child -> [%s]\n", + infant->key, node->children[i-1]->key)); + + /* the strings should never match assuming that we + have called sorted_tree_find_child() first */ + + if ( StrCaseCmp( infant->key, node->children[i-1]->key ) > 0 ) { + DEBUG(11,("sorted_tree_birth_child: storing infant in i == [%d]\n", + i)); + node->children[i] = infant; + break; + } + + /* bump everything towards the end on slot */ + + node->children[i] = node->children[i-1]; + } + + DEBUG(11,("sorted_tree_birth_child: Exiting loop (i == [%d])\n", i )); + + /* if we haven't found the correct slot yet, the child + will be first in the list */ + + if ( i == 0 ) + node->children[0] = infant; + } + + return infant; +} + +/************************************************************************** + Find the next child given a key string + *************************************************************************/ + +static TREE_NODE* sorted_tree_find_child( TREE_NODE *node, char* key ) +{ + TREE_NODE *next = NULL; + int i, result; + + if ( !node ) { + DEBUG(0,("sorted_tree_find_child: NULL node passed into function!\n")); + return NULL; + } + + if ( !key ) { + DEBUG(0,("sorted_tree_find_child: NULL key string passed into function!\n")); + return NULL; + } + + for ( i=0; i<node->num_children; i++ ) + { + DEBUG(11,("sorted_tree_find_child: child key => [%s]\n", + node->children[i]->key)); + + result = StrCaseCmp( node->children[i]->key, key ); + + if ( result == 0 ) + next = node->children[i]; + + /* if result > 0 then we've gone to far because + the list of children is sorted by key name + If result == 0, then we have a match */ + + if ( result > 0 ) + break; + } + + DEBUG(11,("sorted_tree_find_child: %s [%s]\n", + next ? "Found" : "Did not find", key )); + + return next; +} + +/************************************************************************** + Add a new node into the tree given a key path and a blob of data + *************************************************************************/ + +BOOL sorted_tree_add( SORTED_TREE *tree, const char *path, void *data_p ) +{ + char *str, *base, *path2; + TREE_NODE *current, *next; + BOOL ret = True; + + DEBUG(8,("sorted_tree_add: Enter\n")); + + if ( !path || *path != '/' ) { + DEBUG(0,("sorted_tree_add: Attempt to add a node with a bad path [%s]\n", + path ? path : "NULL" )); + return False; + } + + if ( !tree ) { + DEBUG(0,("sorted_tree_add: Attempt to add a node to an uninitialized tree!\n")); + return False; + } + + /* move past the first '/' */ + + path++; + path2 = strdup( path ); + if ( !path2 ) { + DEBUG(0,("sorted_tree_add: strdup() failed on string [%s]!?!?!\n", path)); + return False; + } + + + /* + * this works sort of like a 'mkdir -p' call, possibly + * creating an entire path to the new node at once + * The path should be of the form /<key1>/<key2>/... + */ + + base = path2; + str = path2; + current = tree->root; + + do { + /* break off the remaining part of the path */ + + str = strchr( str, '/' ); + if ( str ) + *str = '\0'; + + /* iterate to the next child--birth it if necessary */ + + next = sorted_tree_find_child( current, base ); + if ( !next ) { + next = sorted_tree_birth_child( current, base ); + if ( !next ) { + DEBUG(0,("sorted_tree_add: Failed to create new child!\n")); + ret = False; + goto done; + } + } + current = next; + + /* setup the next part of the path */ + + base = str; + if ( base ) { + *base = '/'; + base++; + str = base; + } + + } while ( base != NULL ); + + current->data_p = data_p; + + DEBUG(10,("sorted_tree_add: Successfully added node [%s] to tree\n", + path )); + + DEBUG(8,("sorted_tree_add: Exit\n")); + +done: + SAFE_FREE( path2 ); + return ret; +} + + +/************************************************************************** + Recursive routine to print out all children of a TREE_NODE + *************************************************************************/ + +static void sorted_tree_print_children( TREE_NODE *node, int debug, const char *path ) +{ + int i; + int num_children; + pstring path2; + + if ( !node ) + return; + + + if ( node->key ) + DEBUG(debug,("%s: [%s] (%s)\n", path ? path : "NULL", node->key, + node->data_p ? "data" : "NULL" )); + + *path2 = '\0'; + if ( path ) + pstrcpy( path2, path ); + pstrcat( path2, node->key ? node->key : "NULL" ); + pstrcat( path2, "/" ); + + num_children = node->num_children; + for ( i=0; i<num_children; i++ ) + sorted_tree_print_children( node->children[i], debug, path2 ); + + +} + +/************************************************************************** + Dump the kys for a tree to the log file + *************************************************************************/ + +void sorted_tree_print_keys( SORTED_TREE *tree, int debug ) +{ + int i; + int num_children = tree->root->num_children; + + if ( tree->root->key ) + DEBUG(debug,("ROOT/: [%s] (%s)\n", tree->root->key, + tree->root->data_p ? "data" : "NULL" )); + + for ( i=0; i<num_children; i++ ) { + sorted_tree_print_children( tree->root->children[i], debug, + tree->root->key ? tree->root->key : "ROOT/" ); + } + +} + +/************************************************************************** + return the data_p for for the node in tree matching the key string + The key string is the full path. We must break it apart and walk + the tree + *************************************************************************/ + +void* sorted_tree_find( SORTED_TREE *tree, char *key ) +{ + char *keystr, *base, *str, *p; + TREE_NODE *current; + void *result = NULL; + + DEBUG(10,("sorted_tree_find: Enter [%s]\n", key ? key : "NULL" )); + + /* sanity checks first */ + + if ( !key ) { + DEBUG(0,("sorted_tree_find: Attempt to search tree using NULL search string!\n")); + return NULL; + } + + if ( !tree ) { + DEBUG(0,("sorted_tree_find: Attempt to search an uninitialized tree using string [%s]!\n", + key ? key : "NULL" )); + return NULL; + } + + if ( !tree->root ) + return NULL; + + /* make a copy to play with */ + + if ( *key == '/' ) + keystr = strdup( key+1 ); + else + keystr = strdup( key ); + + if ( !keystr ) { + DEBUG(0,("sorted_tree_find: strdup() failed on string [%s]!?!?!\n", key)); + return NULL; + } + + /* start breaking the path apart */ + + p = keystr; + current = tree->root; + + if ( tree->root->data_p ) + result = tree->root->data_p; + + do + { + /* break off the remaining part of the path */ + + trim_tree_keypath( p, &base, &str ); + + DEBUG(11,("sorted_tree_find: [loop] base => [%s], new_path => [%s]\n", + base, str)); + + /* iterate to the next child */ + + current = sorted_tree_find_child( current, base ); + + /* + * the idea is that the data_p for a parent should + * be inherited by all children, but allow it to be + * overridden farther down + */ + + if ( current && current->data_p ) + result = current->data_p; + + /* reset the path pointer 'p' to the remaining part of the key string */ + + p = str; + + } while ( str && current ); + + /* result should be the data_p from the lowest match node in the tree */ + if ( result ) + DEBUG(11,("sorted_tree_find: Found data_p!\n")); + + SAFE_FREE( keystr ); + + DEBUG(10,("sorted_tree_find: Exit\n")); + + return result; +} + + diff --git a/source4/lib/bitmap.c b/source4/lib/bitmap.c new file mode 100644 index 0000000000..1023dd6541 --- /dev/null +++ b/source4/lib/bitmap.c @@ -0,0 +1,163 @@ +/* + Unix SMB/CIFS implementation. + simple bitmap functions + Copyright (C) Andrew Tridgell 1992-1998 + + 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" + +/* these functions provide a simple way to allocate integers from a + pool without repetition */ + +/**************************************************************************** +allocate a bitmap of the specified size +****************************************************************************/ +struct bitmap *bitmap_allocate(int n) +{ + struct bitmap *bm; + + bm = (struct bitmap *)malloc(sizeof(*bm)); + + if (!bm) return NULL; + + bm->n = n; + bm->b = (uint32 *)malloc(sizeof(bm->b[0])*(n+31)/32); + if (!bm->b) { + SAFE_FREE(bm); + return NULL; + } + + memset(bm->b, 0, sizeof(bm->b[0])*(n+31)/32); + + return bm; +} + +/**************************************************************************** +free a bitmap. +****************************************************************************/ + +void bitmap_free(struct bitmap *bm) +{ + if (!bm) + return; + + SAFE_FREE(bm->b); + SAFE_FREE(bm); +} + +/**************************************************************************** +talloc a bitmap +****************************************************************************/ +struct bitmap *bitmap_talloc(TALLOC_CTX *mem_ctx, int n) +{ + struct bitmap *bm; + + if (!mem_ctx) return NULL; + + bm = (struct bitmap *)talloc(mem_ctx, sizeof(*bm)); + + if (!bm) return NULL; + + bm->n = n; + bm->b = (uint32 *)talloc(mem_ctx, sizeof(bm->b[0])*(n+31)/32); + if (!bm->b) { + return NULL; + } + + memset(bm->b, 0, sizeof(bm->b[0])*(n+31)/32); + + return bm; +} + +/**************************************************************************** +set a bit in a bitmap +****************************************************************************/ +BOOL bitmap_set(struct bitmap *bm, unsigned i) +{ + if (i >= bm->n) { + DEBUG(0,("Setting invalid bitmap entry %d (of %d)\n", + i, bm->n)); + return False; + } + bm->b[i/32] |= (1<<(i%32)); + return True; +} + +/**************************************************************************** +clear a bit in a bitmap +****************************************************************************/ +BOOL bitmap_clear(struct bitmap *bm, unsigned i) +{ + if (i >= bm->n) { + DEBUG(0,("clearing invalid bitmap entry %d (of %d)\n", + i, bm->n)); + return False; + } + bm->b[i/32] &= ~(1<<(i%32)); + return True; +} + +/**************************************************************************** +query a bit in a bitmap +****************************************************************************/ +BOOL bitmap_query(struct bitmap *bm, unsigned i) +{ + if (i >= bm->n) return False; + if (bm->b[i/32] & (1<<(i%32))) { + return True; + } + return False; +} + +/**************************************************************************** +find a zero bit in a bitmap starting at the specified offset, with +wraparound +****************************************************************************/ +int bitmap_find(struct bitmap *bm, unsigned ofs) +{ + unsigned int i, j; + + if (ofs > bm->n) ofs = 0; + + i = ofs; + while (i < bm->n) { + if (~(bm->b[i/32])) { + j = i; + do { + if (!bitmap_query(bm, j)) return j; + j++; + } while (j & 31 && j < bm->n); + } + i += 32; + i &= ~31; + } + + i = 0; + while (i < ofs) { + if (~(bm->b[i/32])) { + j = i; + do { + if (!bitmap_query(bm, j)) return j; + j++; + } while (j & 31 && j < bm->n); + } + i += 32; + i &= ~31; + } + + return -1; +} diff --git a/source4/lib/charcnv.c b/source4/lib/charcnv.c new file mode 100644 index 0000000000..90ddbb4d53 --- /dev/null +++ b/source4/lib/charcnv.c @@ -0,0 +1,925 @@ +/* + Unix SMB/CIFS implementation. + Character set conversion Extensions + Copyright (C) Igor Vergeichik <iverg@mail.ru> 2001 + Copyright (C) Andrew Tridgell 2001 + Copyright (C) Simo Sorce 2001 + + 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" + +/** + * @file + * + * @brief Character-set conversion routines built on our iconv. + * + * @note Samba's internal character set (at least in the 3.0 series) + * is always the same as the one for the Unix filesystem. It is + * <b>not</b> necessarily UTF-8 and may be different on machines that + * need i18n filenames to be compatible with Unix software. It does + * have to be a superset of ASCII. All multibyte sequences must start + * with a byte with the high bit set. + * + * @sa lib/iconv.c + */ + +static smb_iconv_t conv_handles[NUM_CHARSETS][NUM_CHARSETS]; + + +/** + * Return the name of a charset to give to iconv(). + **/ +static const char *charset_name(charset_t ch) +{ + const char *ret = NULL; + + if (ch == CH_UCS2) ret = "UCS-2LE"; + else if (ch == CH_UNIX) ret = lp_unix_charset(); + else if (ch == CH_DOS) ret = lp_dos_charset(); + else if (ch == CH_DISPLAY) ret = lp_display_charset(); + else if (ch == CH_UTF8) ret = "UTF8"; + + if (!ret || !*ret) ret = "ASCII"; + return ret; +} + +static void lazy_initialize_conv(void) +{ + static int initialized = False; + + if (!initialized) { + initialized = True; + load_case_tables(); + init_iconv(); + init_valid_table(); + } +} + +/** + Initialize iconv conversion descriptors. +**/ + +void init_iconv(void) +{ + int c1, c2; + BOOL did_reload = False; + + /* so that charset_name() works we need to get the UNIX<->UCS2 going + first */ + if (!conv_handles[CH_UNIX][CH_UCS2]) + conv_handles[CH_UNIX][CH_UCS2] = smb_iconv_open("UCS-2LE", "ASCII"); + + if (!conv_handles[CH_UCS2][CH_UNIX]) + conv_handles[CH_UCS2][CH_UNIX] = smb_iconv_open("ASCII", "UCS-2LE"); + + for (c1=0;c1<NUM_CHARSETS;c1++) { + for (c2=0;c2<NUM_CHARSETS;c2++) { + const char *n1 = charset_name((charset_t)c1); + const char *n2 = charset_name((charset_t)c2); + if (conv_handles[c1][c2] && + strcmp(n1, conv_handles[c1][c2]->from_name) == 0 && + strcmp(n2, conv_handles[c1][c2]->to_name) == 0) + continue; + + did_reload = True; + + if (conv_handles[c1][c2]) + smb_iconv_close(conv_handles[c1][c2]); + + conv_handles[c1][c2] = smb_iconv_open(n2,n1); + if (conv_handles[c1][c2] == (smb_iconv_t)-1) { + DEBUG(0,("Conversion from %s to %s not supported\n", + charset_name((charset_t)c1), charset_name((charset_t)c2))); + conv_handles[c1][c2] = NULL; + } + } + } + + if (did_reload) { + init_valid_table(); + } +} + +/** + * Convert string from one encoding to another, making error checking etc + * + * @param src pointer to source string (multibyte or singlebyte) + * @param srclen length of the source string in bytes + * @param dest pointer to destination string (multibyte or singlebyte) + * @param destlen maximal length allowed for string + * @returns the number of bytes occupied in the destination + **/ +ssize_t convert_string(charset_t from, charset_t to, + void const *src, size_t srclen, + void *dest, size_t destlen) +{ + size_t i_len, o_len; + size_t retval; + const char* inbuf = (const char*)src; + char* outbuf = (char*)dest; + smb_iconv_t descriptor; + + if (srclen == (size_t)-1) + srclen = strlen(src)+1; + + lazy_initialize_conv(); + + descriptor = conv_handles[from][to]; + + if (descriptor == (smb_iconv_t)-1 || descriptor == (smb_iconv_t)0) { + /* conversion not supported, use as is */ + size_t len = MIN(srclen,destlen); + memcpy(dest,src,len); + return len; + } + + i_len=srclen; + o_len=destlen; + retval = smb_iconv(descriptor, &inbuf, &i_len, &outbuf, &o_len); + if(retval==(size_t)-1) { + const char *reason="unknown error"; + switch(errno) { + case EINVAL: + reason="Incomplete multibyte sequence"; + break; + case E2BIG: + reason="No more room"; + DEBUG(0, ("convert_string: Required %d, available %d\n", + srclen, destlen)); + /* we are not sure we need srclen bytes, + may be more, may be less. + We only know we need more than destlen + bytes ---simo */ + break; + case EILSEQ: + reason="Illegal multibyte sequence"; + break; + } + /* smb_panic(reason); */ + } + return destlen-o_len; +} + +/** + * Convert between character sets, allocating a new buffer for the result. + * + * @param srclen length of source buffer. + * @param dest always set at least to NULL + * @note -1 is not accepted for srclen. + * + * @returns Size in bytes of the converted string; or -1 in case of error. + **/ + +ssize_t convert_string_allocate(charset_t from, charset_t to, + void const *src, size_t srclen, void **dest) +{ + size_t i_len, o_len, destlen; + size_t retval; + const char *inbuf = (const char *)src; + char *outbuf, *ob; + smb_iconv_t descriptor; + + *dest = NULL; + + if (src == NULL || srclen == (size_t)-1 || srclen == 0) + return (size_t)-1; + + lazy_initialize_conv(); + + descriptor = conv_handles[from][to]; + + if (descriptor == (smb_iconv_t)-1 || descriptor == (smb_iconv_t)0) { + /* conversion not supported, return -1*/ + DEBUG(3, ("convert_string_allocate: conversion not supported!\n")); + return -1; + } + + destlen = MAX(srclen, 512); + outbuf = NULL; +convert: + destlen = destlen * 2; + ob = (char *)realloc(outbuf, destlen); + if (!ob) { + DEBUG(0, ("convert_string_allocate: realloc failed!\n")); + SAFE_FREE(outbuf); + return (size_t)-1; + } + else + outbuf = ob; + i_len = srclen; + o_len = destlen; + retval = smb_iconv(descriptor, + &inbuf, &i_len, + &outbuf, &o_len); + if(retval == (size_t)-1) { + const char *reason="unknown error"; + switch(errno) { + case EINVAL: + reason="Incomplete multibyte sequence"; + break; + case E2BIG: + goto convert; + case EILSEQ: + reason="Illegal multibyte sequence"; + break; + } + DEBUG(0,("Conversion error: %s(%s)\n",reason,inbuf)); + /* smb_panic(reason); */ + return (size_t)-1; + } + + destlen = destlen - o_len; + *dest = (char *)Realloc(ob,destlen); + if (!*dest) { + DEBUG(0, ("convert_string_allocate: out of memory!\n")); + SAFE_FREE(ob); + return (size_t)-1; + } + + return destlen; +} + + +/** + * Convert between character sets, allocating a new buffer using talloc for the result. + * + * @param srclen length of source buffer. + * @param dest always set at least to NULL + * @note -1 is not accepted for srclen. + * + * @returns Size in bytes of the converted string; or -1 in case of error. + **/ +ssize_t convert_string_talloc(TALLOC_CTX *ctx, charset_t from, charset_t to, + void const *src, size_t srclen, const void **dest) +{ + void *alloced_string; + size_t dest_len; + void *dst; + + *dest = NULL; + dest_len=convert_string_allocate(from, to, src, srclen, &alloced_string); + if (dest_len == (size_t)-1) + return (size_t)-1; + dst = talloc(ctx, dest_len + 2); + /* we want to be absolutely sure that the result is terminated */ + memcpy(dst, alloced_string, dest_len); + SSVAL(dst, dest_len, 0); + SAFE_FREE(alloced_string); + if (dst == NULL) + return -1; + *dest = dst; + return dest_len; +} + +size_t unix_strupper(const char *src, size_t srclen, char *dest, size_t destlen) +{ + size_t size; + smb_ucs2_t *buffer; + + size = convert_string_allocate(CH_UNIX, CH_UCS2, src, srclen, + (void **) &buffer); + if (size == -1) { + smb_panic("failed to create UCS2 buffer"); + } + if (!strupper_w(buffer) && (dest == src)) { + free(buffer); + return srclen; + } + + size = convert_string(CH_UCS2, CH_UNIX, buffer, size, dest, destlen); + free(buffer); + return size; +} + +size_t unix_strlower(const char *src, size_t srclen, char *dest, size_t destlen) +{ + size_t size; + smb_ucs2_t *buffer; + + size = convert_string_allocate(CH_UNIX, CH_UCS2, src, srclen, + (void **) &buffer); + if (size == -1) { + smb_panic("failed to create UCS2 buffer"); + } + if (!strlower_w(buffer) && (dest == src)) { + free(buffer); + return srclen; + } + size = convert_string(CH_UCS2, CH_UNIX, buffer, size, dest, destlen); + free(buffer); + return size; +} + +size_t ucs2_align(const void *base_ptr, const void *p, int flags) +{ + if (flags & (STR_NOALIGN|STR_ASCII)) + return 0; + return PTR_DIFF(p, base_ptr) & 1; +} + + +/** + * Copy a string from a char* unix src to a dos codepage string destination. + * + * @return the number of bytes occupied by the string in the destination. + * + * @param flags can include + * <dl> + * <dt>STR_TERMINATE</dt> <dd>means include the null termination</dd> + * <dt>STR_UPPER</dt> <dd>means uppercase in the destination</dd> + * </dl> + * + * @param dest_len the maximum length in bytes allowed in the + * destination. If @p dest_len is -1 then no maximum is used. + **/ +ssize_t push_ascii(void *dest, const char *src, size_t dest_len, int flags) +{ + size_t src_len = strlen(src); + pstring tmpbuf; + + /* treat a pstring as "unlimited" length */ + if (dest_len == (size_t)-1) + dest_len = sizeof(pstring); + + if (flags & STR_UPPER) { + pstrcpy(tmpbuf, src); + strupper(tmpbuf); + src = tmpbuf; + } + + if (flags & (STR_TERMINATE | STR_TERMINATE_ASCII)) + src_len++; + + return convert_string(CH_UNIX, CH_DOS, src, src_len, dest, dest_len); +} + +ssize_t push_ascii_fstring(void *dest, const char *src) +{ + return push_ascii(dest, src, sizeof(fstring), STR_TERMINATE); +} + +ssize_t push_ascii_pstring(void *dest, const char *src) +{ + return push_ascii(dest, src, sizeof(pstring), STR_TERMINATE); +} + +ssize_t push_pstring(void *dest, const char *src) +{ + return push_ascii(dest, src, sizeof(pstring), STR_TERMINATE); +} + +/** + * Copy a string from a dos codepage source to a unix char* destination. + * + * The resulting string in "dest" is always null terminated. + * + * @param flags can have: + * <dl> + * <dt>STR_TERMINATE</dt> + * <dd>STR_TERMINATE means the string in @p src + * is null terminated, and src_len is ignored.</dd> + * </dl> + * + * @param src_len is the length of the source area in bytes. + * @returns the number of bytes occupied by the string in @p src. + **/ +ssize_t pull_ascii(char *dest, const void *src, size_t dest_len, size_t src_len, int flags) +{ + size_t ret; + + if (dest_len == (size_t)-1) + dest_len = sizeof(pstring); + + if (flags & STR_TERMINATE) { + if (src_len == (size_t)-1) { + src_len = strlen(src) + 1; + } else { + size_t len = strnlen(src, src_len); + if (len < src_len) + len++; + src_len = len; + } + } + + ret = convert_string(CH_DOS, CH_UNIX, src, src_len, dest, dest_len); + + if (dest_len) + dest[MIN(ret, dest_len-1)] = 0; + + return src_len; +} + +ssize_t pull_ascii_pstring(char *dest, const void *src) +{ + return pull_ascii(dest, src, sizeof(pstring), -1, STR_TERMINATE); +} + +ssize_t pull_ascii_fstring(char *dest, const void *src) +{ + return pull_ascii(dest, src, sizeof(fstring), -1, STR_TERMINATE); +} + +/** + * Copy a string from a char* src to a unicode destination. + * + * @returns the number of bytes occupied by the string in the destination. + * + * @param flags can have: + * + * <dl> + * <dt>STR_TERMINATE <dd>means include the null termination. + * <dt>STR_UPPER <dd>means uppercase in the destination. + * <dt>STR_NOALIGN <dd>means don't do alignment. + * </dl> + * + * @param dest_len is the maximum length allowed in the + * destination. If dest_len is -1 then no maxiumum is used. + **/ +ssize_t push_ucs2(const void *base_ptr, void *dest, const char *src, size_t dest_len, int flags) +{ + size_t len=0; + size_t src_len = strlen(src); + pstring tmpbuf; + + /* treat a pstring as "unlimited" length */ + if (dest_len == (size_t)-1) + dest_len = sizeof(pstring); + + if (flags & STR_UPPER) { + pstrcpy(tmpbuf, src); + strupper(tmpbuf); + src = tmpbuf; + } + + if (flags & STR_TERMINATE) + src_len++; + + if (ucs2_align(base_ptr, dest, flags)) { + *(char *)dest = 0; + dest = (void *)((char *)dest + 1); + if (dest_len) dest_len--; + len++; + } + + /* ucs2 is always a multiple of 2 bytes */ + dest_len &= ~1; + + len += convert_string(CH_UNIX, CH_UCS2, src, src_len, dest, dest_len); + return len; +} + + +/** + * Copy a string from a unix char* src to a UCS2 destination, + * allocating a buffer using talloc(). + * + * @param dest always set at least to NULL + * + * @returns The number of bytes occupied by the string in the destination + * or -1 in case of error. + **/ +ssize_t push_ucs2_talloc(TALLOC_CTX *ctx, smb_ucs2_t **dest, const char *src) +{ + size_t src_len = strlen(src)+1; + + *dest = NULL; + return convert_string_talloc(ctx, CH_UNIX, CH_UCS2, src, src_len, (const void **)dest); +} + + +/** + * Copy a string from a unix char* src to a UCS2 destination, allocating a buffer + * + * @param dest always set at least to NULL + * + * @returns The number of bytes occupied by the string in the destination + * or -1 in case of error. + **/ + +ssize_t push_ucs2_allocate(smb_ucs2_t **dest, const char *src) +{ + size_t src_len = strlen(src)+1; + + *dest = NULL; + return convert_string_allocate(CH_UNIX, CH_UCS2, src, src_len, (void **)dest); +} + +/** + Copy a string from a char* src to a UTF-8 destination. + Return the number of bytes occupied by the string in the destination + Flags can have: + STR_TERMINATE means include the null termination + STR_UPPER means uppercase in the destination + dest_len is the maximum length allowed in the destination. If dest_len + is -1 then no maxiumum is used. +**/ + +ssize_t push_utf8(void *dest, const char *src, size_t dest_len, int flags) +{ + size_t src_len = strlen(src); + pstring tmpbuf; + + /* treat a pstring as "unlimited" length */ + if (dest_len == (size_t)-1) + dest_len = sizeof(pstring); + + if (flags & STR_UPPER) { + pstrcpy(tmpbuf, src); + strupper(tmpbuf); + src = tmpbuf; + } + + if (flags & STR_TERMINATE) + src_len++; + + return convert_string(CH_UNIX, CH_UTF8, src, src_len, dest, dest_len); +} + +ssize_t push_utf8_fstring(void *dest, const char *src) +{ + return push_utf8(dest, src, sizeof(fstring), STR_TERMINATE); +} + +ssize_t push_utf8_pstring(void *dest, const char *src) +{ + return push_utf8(dest, src, sizeof(pstring), STR_TERMINATE); +} + +/** + * Copy a string from a unix char* src to a UTF-8 destination, allocating a buffer using talloc + * + * @param dest always set at least to NULL + * + * @returns The number of bytes occupied by the string in the destination + **/ + +ssize_t push_utf8_talloc(TALLOC_CTX *ctx, char **dest, const char *src) +{ + size_t src_len = strlen(src)+1; + + *dest = NULL; + return convert_string_talloc(ctx, CH_UNIX, CH_UTF8, src, src_len, (const void **)dest); +} + +/** + * Copy a string from a unix char* src to a UTF-8 destination, allocating a buffer + * + * @param dest always set at least to NULL + * + * @returns The number of bytes occupied by the string in the destination + **/ + +ssize_t push_utf8_allocate(char **dest, const char *src) +{ + size_t src_len = strlen(src)+1; + + *dest = NULL; + return convert_string_allocate(CH_UNIX, CH_UTF8, src, src_len, (void **)dest); +} + +/** + Copy a string from a ucs2 source to a unix char* destination. + Flags can have: + STR_TERMINATE means the string in src is null terminated. + STR_NOALIGN means don't try to align. + if STR_TERMINATE is set then src_len is ignored if it is -1. + src_len is the length of the source area in bytes + Return the number of bytes occupied by the string in src. + The resulting string in "dest" is always null terminated. +**/ + +size_t pull_ucs2(const void *base_ptr, char *dest, const void *src, size_t dest_len, size_t src_len, int flags) +{ + size_t ret; + + if (dest_len == (size_t)-1) + dest_len = sizeof(pstring); + + if (ucs2_align(base_ptr, src, flags)) { + src = (const void *)((const char *)src + 1); + if (src_len > 0) + src_len--; + } + + if (flags & STR_TERMINATE) { + if (src_len == (size_t)-1) { + src_len = strlen_w(src)*2 + 2; + } else { + size_t len = strnlen_w(src, src_len/2); + if (len < src_len/2) + len++; + src_len = len*2; + } + } + + /* ucs2 is always a multiple of 2 bytes */ + if (src_len != (size_t)-1) + src_len &= ~1; + + ret = convert_string(CH_UCS2, CH_UNIX, src, src_len, dest, dest_len); + if (dest_len) + dest[MIN(ret, dest_len-1)] = 0; + + return src_len; +} + +ssize_t pull_ucs2_pstring(char *dest, const void *src) +{ + return pull_ucs2(NULL, dest, src, sizeof(pstring), -1, STR_TERMINATE); +} + +ssize_t pull_ucs2_fstring(char *dest, const void *src) +{ + return pull_ucs2(NULL, dest, src, sizeof(fstring), -1, STR_TERMINATE); +} + +/** + * Copy a string from a UCS2 src to a unix char * destination, allocating a buffer using talloc + * + * @param dest always set at least to NULL + * + * @returns The number of bytes occupied by the string in the destination + **/ + +ssize_t pull_ucs2_talloc(TALLOC_CTX *ctx, char **dest, const smb_ucs2_t *src) +{ + size_t src_len = (strlen_w(src)+1) * sizeof(smb_ucs2_t); + *dest = NULL; + return convert_string_talloc(ctx, CH_UCS2, CH_UNIX, src, src_len, (const void **)dest); +} + +/** + * Copy a string from a UCS2 src to a unix char * destination, allocating a buffer + * + * @param dest always set at least to NULL + * + * @returns The number of bytes occupied by the string in the destination + **/ + +ssize_t pull_ucs2_allocate(void **dest, const smb_ucs2_t *src) +{ + size_t src_len = (strlen_w(src)+1) * sizeof(smb_ucs2_t); + *dest = NULL; + return convert_string_allocate(CH_UCS2, CH_UNIX, src, src_len, dest); +} + +/** + Copy a string from a utf-8 source to a unix char* destination. + Flags can have: + STR_TERMINATE means the string in src is null terminated. + if STR_TERMINATE is set then src_len is ignored. + src_len is the length of the source area in bytes + Return the number of bytes occupied by the string in src. + The resulting string in "dest" is always null terminated. +**/ + +ssize_t pull_utf8(char *dest, const void *src, size_t dest_len, size_t src_len, int flags) +{ + size_t ret; + + if (dest_len == (size_t)-1) + dest_len = sizeof(pstring); + + if (flags & STR_TERMINATE) { + if (src_len == (size_t)-1) { + src_len = strlen(src) + 1; + } else { + size_t len = strnlen(src, src_len); + if (len < src_len) + len++; + src_len = len; + } + } + + ret = convert_string(CH_UTF8, CH_UNIX, src, src_len, dest, dest_len); + if (dest_len) + dest[MIN(ret, dest_len-1)] = 0; + + return src_len; +} + +ssize_t pull_utf8_pstring(char *dest, const void *src) +{ + return pull_utf8(dest, src, sizeof(pstring), -1, STR_TERMINATE); +} + +ssize_t pull_utf8_fstring(char *dest, const void *src) +{ + return pull_utf8(dest, src, sizeof(fstring), -1, STR_TERMINATE); +} + +/** + * Copy a string from a UTF-8 src to a unix char * destination, allocating a buffer using talloc + * + * @param dest always set at least to NULL + * + * @returns The number of bytes occupied by the string in the destination + **/ + +ssize_t pull_utf8_talloc(TALLOC_CTX *ctx, char **dest, const char *src) +{ + size_t src_len = strlen(src)+1; + *dest = NULL; + return convert_string_talloc(ctx, CH_UTF8, CH_UNIX, src, src_len, (const void **)dest); +} + +/** + * Copy a string from a UTF-8 src to a unix char * destination, allocating a buffer + * + * @param dest always set at least to NULL + * + * @returns The number of bytes occupied by the string in the destination + **/ + +ssize_t pull_utf8_allocate(void **dest, const char *src) +{ + size_t src_len = strlen(src)+1; + *dest = NULL; + return convert_string_allocate(CH_UTF8, CH_UNIX, src, src_len, dest); +} + +/** + Copy a string from a char* src to a unicode or ascii + dos codepage destination choosing unicode or ascii based on the + flags in the SMB buffer starting at base_ptr. + Return the number of bytes occupied by the string in the destination. + flags can have: + STR_TERMINATE means include the null termination. + STR_UPPER means uppercase in the destination. + STR_ASCII use ascii even with unicode packet. + STR_NOALIGN means don't do alignment. + dest_len is the maximum length allowed in the destination. If dest_len + is -1 then no maxiumum is used. +**/ + +ssize_t push_string(const void *base_ptr, void *dest, const char *src, size_t dest_len, int flags) +{ + if (!(flags & STR_ASCII) && \ + ((flags & STR_UNICODE || \ + (SVAL(base_ptr, NBT_HDR_SIZE+HDR_FLG2) & FLAGS2_UNICODE_STRINGS)))) { + return push_ucs2(base_ptr, dest, src, dest_len, flags); + } + return push_ascii(dest, src, dest_len, flags); +} + + +/** + Copy a string from a unicode or ascii source (depending on + the packet flags) to a char* destination. + Flags can have: + STR_TERMINATE means the string in src is null terminated. + STR_UNICODE means to force as unicode. + STR_ASCII use ascii even with unicode packet. + STR_NOALIGN means don't do alignment. + if STR_TERMINATE is set then src_len is ignored is it is -1 + src_len is the length of the source area in bytes. + Return the number of bytes occupied by the string in src. + The resulting string in "dest" is always null terminated. +**/ + +ssize_t pull_string(const void *base_ptr, char *dest, const void *src, size_t dest_len, size_t src_len, int flags) +{ + if (!(flags & STR_ASCII) && \ + ((flags & STR_UNICODE || \ + (SVAL(base_ptr, NBT_HDR_SIZE+HDR_FLG2) & FLAGS2_UNICODE_STRINGS)))) { + return pull_ucs2(base_ptr, dest, src, dest_len, src_len, flags); + } + return pull_ascii(dest, src, dest_len, src_len, flags); +} + +ssize_t align_string(const void *base_ptr, const char *p, int flags) +{ + if (!(flags & STR_ASCII) && \ + ((flags & STR_UNICODE || \ + (SVAL(base_ptr, NBT_HDR_SIZE+HDR_FLG2) & FLAGS2_UNICODE_STRINGS)))) { + return ucs2_align(base_ptr, p, flags); + } + return 0; +} + +/** + Copy a string from a unicode or ascii source (depending on + the packet flags) to a TALLOC'ed destination. + Flags can have: + STR_TERMINATE means the string in src is null terminated. + STR_UNICODE means to force as unicode. + STR_ASCII use ascii even with unicode packet. + STR_NOALIGN means don't do alignment. + if STR_TERMINATE is set then src_len is ignored is it is -1 + src_len is the length of the source area in bytes. + Return the number of bytes occupied by the string in src. + The resulting string in "dest" is always null terminated. +**/ + +ssize_t pull_string_talloc(TALLOC_CTX *ctx, char **dest, const void *src, size_t src_len, int flags) +{ + if (!(flags & STR_ASCII) && \ + (flags & STR_UNICODE)) { + return pull_ucs2_talloc(ctx, dest, src); + } + *dest = NULL; + if (flags & STR_TERMINATE) { + *dest = talloc_strdup(ctx, src); + return strlen(*dest); + } + *dest = talloc_strndup(ctx, src, src_len); + return src_len; +} + +/** + Convert from ucs2 to unix charset and return the + allocated and converted string or NULL if an error occurred. + You must provide a zero terminated string. + The returning string will be zero terminated. +**/ + +char *acnv_u2ux(const smb_ucs2_t *src) +{ + size_t slen; + size_t dlen; + void *dest; + + slen = (strlen_w(src) + 1) * sizeof(smb_ucs2_t); + dlen = convert_string_allocate(CH_UCS2, CH_UNIX, src, slen, &dest); + if (dlen == (size_t)-1) + return NULL; + else + return dest; +} + +/** + Convert from unix to ucs2 charset and return the + allocated and converted string or NULL if an error occurred. + You must provide a zero terminated string. + The returning string will be zero terminated. +**/ + +smb_ucs2_t *acnv_uxu2(const char *src) +{ + size_t slen; + size_t dlen; + void *dest; + + slen = strlen(src) + 1; + dlen = convert_string_allocate(CH_UNIX, CH_UCS2, src, slen, &dest); + if (dlen == (size_t)-1) + return NULL; + else + return dest; +} + +/** + Convert from ucs2 to dos charset and return the + allocated and converted string or NULL if an error occurred. + You must provide a zero terminated string. + The returning string will be zero terminated. +**/ + +char *acnv_u2dos(const smb_ucs2_t *src) +{ + size_t slen; + size_t dlen; + void *dest; + + slen = (strlen_w(src) + 1) * sizeof(smb_ucs2_t); + dlen = convert_string_allocate(CH_UCS2, CH_DOS, src, slen, &dest); + if (dlen == (size_t)-1) + return NULL; + else + return dest; +} + +/** + Convert from dos to ucs2 charset and return the + allocated and converted string or NULL if an error occurred. + You must provide a zero terminated string. + The returning string will be zero terminated. +**/ + +smb_ucs2_t *acnv_dosu2(const char *src) +{ + size_t slen; + size_t dlen; + void *dest; + + slen = strlen(src) + 1; + dlen = convert_string_allocate(CH_DOS, CH_UCS2, src, slen, &dest); + if (dlen == (size_t)-1) + return NULL; + else + return dest; +} diff --git a/source4/lib/cmdline/popt_common.c b/source4/lib/cmdline/popt_common.c new file mode 100644 index 0000000000..ccdecc91cc --- /dev/null +++ b/source4/lib/cmdline/popt_common.c @@ -0,0 +1,327 @@ +/* + Unix SMB/CIFS implementation. + Common popt routines + + Copyright (C) Tim Potter 2001,2002 + Copyright (C) Jelmer Vernooij 2002,2003 + + 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" + +/* Handle command line options: + * -d,--debuglevel + * -s,--configfile + * -O,--socket-options + * -V,--version + * -l,--log-base + * -n,--netbios-name + * -W,--workgroup + * -i,--scope + */ + +extern pstring user_socket_options; +extern BOOL AllowDebugChange; + +struct user_auth_info cmdline_auth_info; + +static void popt_common_callback(poptContext con, + enum poptCallbackReason reason, + const struct poptOption *opt, + const char *arg, const void *data) +{ + pstring logfile; + const char *pname; + + /* Find out basename of current program */ + pname = strrchr_m(poptGetInvocationName(con),'/'); + + if (!pname) + pname = poptGetInvocationName(con); + else + pname++; + + if (reason == POPT_CALLBACK_REASON_PRE) { + pstr_sprintf(logfile, "%s/log.%s", dyn_LOGFILEBASE, pname); + lp_set_cmdline("log file", logfile); + return; + } + + switch(opt->val) { + case 'd': + lp_set_cmdline("log level", arg); + break; + + case 'V': + printf( "Version %s\n", SAMBA_VERSION ); + exit(0); + break; + + case 's': + if (arg) { + pstrcpy(dyn_CONFIGFILE, arg); + } + break; + + case 'l': + if (arg) { + pstr_sprintf(logfile, "%s/log.%s", arg, pname); + lp_set_cmdline("log file", logfile); + } + break; + + case 'W': + lp_set_cmdline("workgroup", arg); + break; + + case 'n': + lp_set_cmdline("netbios name", arg); + break; + + case 'i': + lp_set_cmdline("netbios scope", arg); + break; + } +} + +struct poptOption popt_common_connection[] = { + { NULL, 0, POPT_ARG_CALLBACK, popt_common_callback }, + { "socket-options", 'O', POPT_ARG_STRING, NULL, 'O', "socket options to use", + "SOCKETOPTIONS" }, + { "netbiosname", 'n', POPT_ARG_STRING, NULL, 'n', "Primary netbios name", "NETBIOSNAME" }, + { "workgroup", 'W', POPT_ARG_STRING, NULL, 'W', "Set the workgroup name", "WORKGROUP" }, + { "scope", 'i', POPT_ARG_STRING, NULL, 'i', "Use this Netbios scope", "SCOPE" }, + POPT_TABLEEND +}; + +struct poptOption popt_common_samba[] = { + { NULL, 0, POPT_ARG_CALLBACK|POPT_CBFLAG_PRE, popt_common_callback }, + { "debuglevel", 'd', POPT_ARG_STRING, NULL, 'd', "Set debug level", "DEBUGLEVEL" }, + { "configfile", 's', POPT_ARG_STRING, NULL, 's', "Use alternative configuration file", "CONFIGFILE" }, + { "log-basename", 'l', POPT_ARG_STRING, NULL, 'l', "Basename for log/debug files", "LOGFILEBASE" }, + { "version", 'V', POPT_ARG_NONE, NULL, 'V', "Print version" }, + POPT_TABLEEND +}; + +struct poptOption popt_common_version[] = { + { NULL, 0, POPT_ARG_CALLBACK, popt_common_callback }, + { "version", 'V', POPT_ARG_NONE, NULL, 'V', "Print version" }, + POPT_TABLEEND +}; + + + +/**************************************************************************** + * get a password from a a file or file descriptor + * exit on failure + * ****************************************************************************/ +static void get_password_file(struct user_auth_info *a) +{ + int fd = -1; + char *p; + BOOL close_it = False; + pstring spec; + char pass[128]; + + if ((p = getenv("PASSWD_FD")) != NULL) { + pstrcpy(spec, "descriptor "); + pstrcat(spec, p); + sscanf(p, "%d", &fd); + close_it = False; + } else if ((p = getenv("PASSWD_FILE")) != NULL) { + fd = sys_open(p, O_RDONLY, 0); + pstrcpy(spec, p); + if (fd < 0) { + fprintf(stderr, "Error opening PASSWD_FILE %s: %s\n", + spec, strerror(errno)); + exit(1); + } + close_it = True; + } + + for(p = pass, *p = '\0'; /* ensure that pass is null-terminated */ + p && p - pass < sizeof(pass);) { + switch (read(fd, p, 1)) { + case 1: + if (*p != '\n' && *p != '\0') { + *++p = '\0'; /* advance p, and null-terminate pass */ + break; + } + case 0: + if (p - pass) { + *p = '\0'; /* null-terminate it, just in case... */ + p = NULL; /* then force the loop condition to become false */ + break; + } else { + fprintf(stderr, "Error reading password from file %s: %s\n", + spec, "empty password\n"); + exit(1); + } + + default: + fprintf(stderr, "Error reading password from file %s: %s\n", + spec, strerror(errno)); + exit(1); + } + } + pstrcpy(a->password, pass); + if (close_it) + close(fd); +} + +static void get_credentials_file(const char *file, struct user_auth_info *info) +{ + XFILE *auth; + fstring buf; + uint16 len = 0; + char *ptr, *val, *param; + + if ((auth=x_fopen(file, O_RDONLY, 0)) == NULL) + { + /* fail if we can't open the credentials file */ + d_printf("ERROR: Unable to open credentials file!\n"); + exit(-1); + } + + while (!x_feof(auth)) + { + /* get a line from the file */ + if (!x_fgets(buf, sizeof(buf), auth)) + continue; + len = strlen(buf); + + if ((len) && (buf[len-1]=='\n')) + { + buf[len-1] = '\0'; + len--; + } + if (len == 0) + continue; + + /* break up the line into parameter & value. + * will need to eat a little whitespace possibly */ + param = buf; + if (!(ptr = strchr_m (buf, '='))) + continue; + + val = ptr+1; + *ptr = '\0'; + + /* eat leading white space */ + while ((*val!='\0') && ((*val==' ') || (*val=='\t'))) + val++; + + if (strwicmp("password", param) == 0) + { + pstrcpy(info->password, val); + info->got_pass = True; + } + else if (strwicmp("username", param) == 0) + pstrcpy(info->username, val); + //else if (strwicmp("domain", param) == 0) + // set_global_myworkgroup(val); + memset(buf, 0, sizeof(buf)); + } + x_fclose(auth); +} + +/* Handle command line options: + * -U,--user + * -A,--authentication-file + * -k,--use-kerberos + * -N,--no-pass + */ + + +static void popt_common_credentials_callback(poptContext con, + enum poptCallbackReason reason, + const struct poptOption *opt, + const char *arg, const void *data) +{ + char *p; + + if (reason == POPT_CALLBACK_REASON_PRE) { + cmdline_auth_info.use_kerberos = False; + cmdline_auth_info.got_pass = False; + pstrcpy(cmdline_auth_info.username, "GUEST"); + + if (getenv("LOGNAME"))pstrcpy(cmdline_auth_info.username,getenv("LOGNAME")); + + if (getenv("USER")) { + pstrcpy(cmdline_auth_info.username,getenv("USER")); + + if ((p = strchr_m(cmdline_auth_info.username,'%'))) { + *p = 0; + pstrcpy(cmdline_auth_info.password,p+1); + cmdline_auth_info.got_pass = True; + memset(strchr_m(getenv("USER"),'%')+1,'X',strlen(cmdline_auth_info.password)); + } + } + + if (getenv("PASSWD")) { + pstrcpy(cmdline_auth_info.password,getenv("PASSWD")); + cmdline_auth_info.got_pass = True; + } + + if (getenv("PASSWD_FD") || getenv("PASSWD_FILE")) { + get_password_file(&cmdline_auth_info); + cmdline_auth_info.got_pass = True; + } + + return; + } + + switch(opt->val) { + case 'U': + { + char *lp; + + pstrcpy(cmdline_auth_info.username,arg); + if ((lp=strchr_m(cmdline_auth_info.username,'%'))) { + *lp = 0; + pstrcpy(cmdline_auth_info.password,lp+1); + cmdline_auth_info.got_pass = True; + memset(strchr_m(arg,'%')+1,'X',strlen(cmdline_auth_info.password)); + } + } + break; + + case 'A': + get_credentials_file(arg, &cmdline_auth_info); + break; + + case 'k': +#ifndef HAVE_KRB5 + d_printf("No kerberos support compiled in\n"); + exit(1); +#else + cmdline_auth_info.use_kerberos = True; + cmdline_auth_info.got_pass = True; +#endif + break; + } +} + + + +struct poptOption popt_common_credentials[] = { + { NULL, 0, POPT_ARG_CALLBACK|POPT_CBFLAG_PRE, popt_common_credentials_callback }, + { "user", 'U', POPT_ARG_STRING, NULL, 'U', "Set the network username", "USERNAME" }, + { "no-pass", 'N', POPT_ARG_NONE, &cmdline_auth_info.got_pass, True, "Don't ask for a password" }, + { "kerberos", 'k', POPT_ARG_NONE, &cmdline_auth_info.use_kerberos, True, "Use kerberos (active directory) authentication" }, + { "authentication-file", 'A', POPT_ARG_STRING, NULL, 'A', "Get the credentials from a file", "FILE" }, + POPT_TABLEEND +}; diff --git a/source4/lib/cmdline/readline.c b/source4/lib/cmdline/readline.c new file mode 100644 index 0000000000..c5da88b3e0 --- /dev/null +++ b/source4/lib/cmdline/readline.c @@ -0,0 +1,159 @@ +/* + Unix SMB/CIFS implementation. + Samba readline wrapper implementation + Copyright (C) Simo Sorce 2001 + Copyright (C) Andrew Tridgell 2001 + + 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" + +#ifdef HAVE_LIBREADLINE +# ifdef HAVE_READLINE_READLINE_H +# include <readline/readline.h> +# ifdef HAVE_READLINE_HISTORY_H +# include <readline/history.h> +# endif +# else +# ifdef HAVE_READLINE_H +# include <readline.h> +# ifdef HAVE_HISTORY_H +# include <history.h> +# endif +# else +# undef HAVE_LIBREADLINE +# endif +# endif +#endif + +#ifdef HAVE_NEW_LIBREADLINE +# define RL_COMPLETION_CAST (rl_completion_func_t *) +#else +/* This type is missing from libreadline<4.0 (approximately) */ +# define RL_COMPLETION_CAST +#endif /* HAVE_NEW_LIBREADLINE */ + +/**************************************************************************** + Display the prompt and wait for input. Call callback() regularly +****************************************************************************/ + +static char *smb_readline_replacement(const char *prompt, void (*callback)(void), + char **(completion_fn)(const char *text, int start, int end)) +{ + fd_set fds; + static pstring line; + struct timeval timeout; + int fd = x_fileno(x_stdin); + char *ret; + + do_debug("%s", prompt); + + while (1) { + timeout.tv_sec = 5; + timeout.tv_usec = 0; + + FD_ZERO(&fds); + FD_SET(fd,&fds); + + if (sys_select_intr(fd+1,&fds,NULL,NULL,&timeout) == 1) { + ret = x_fgets(line, sizeof(line), x_stdin); + return ret; + } + if (callback) + callback(); + } +} + +/**************************************************************************** + Display the prompt and wait for input. Call callback() regularly. +****************************************************************************/ + +char *smb_readline(const char *prompt, void (*callback)(void), + char **(completion_fn)(const char *text, int start, int end)) +{ +#if HAVE_LIBREADLINE + if (isatty(x_fileno(x_stdin))) { + char *ret; + + /* Aargh! Readline does bizzare things with the terminal width + that mucks up expect(1). Set CLI_NO_READLINE in the environment + to force readline not to be used. */ + + if (getenv("CLI_NO_READLINE")) + return smb_readline_replacement(prompt, callback, completion_fn); + + if (completion_fn) { + /* The callback prototype has changed slightly between + different versions of Readline, so the same function + works in all of them to date, but we get compiler + warnings in some. */ + rl_attempted_completion_function = RL_COMPLETION_CAST completion_fn; + } + + if (callback) + rl_event_hook = (Function *)callback; + ret = readline(prompt); + if (ret && *ret) + add_history(ret); + return ret; + } else +#endif + return smb_readline_replacement(prompt, callback, completion_fn); +} + +/**************************************************************************** + * return line buffer text + ****************************************************************************/ +const char *smb_readline_get_line_buffer(void) +{ +#if defined(HAVE_LIBREADLINE) + return rl_line_buffer; +#else + return NULL; +#endif +} + +/**************************************************************************** + * set completion append character + ***************************************************************************/ +void smb_readline_ca_char(char c) +{ +#if defined(HAVE_LIBREADLINE) + rl_completion_append_character = c; +#endif +} + + +/**************************************************************************** +history +****************************************************************************/ +int cmd_history(void) +{ +#if defined(HAVE_LIBREADLINE) + HIST_ENTRY **hlist; + int i; + + hlist = history_list(); + + for (i = 0; hlist && hlist[i]; i++) { + DEBUG(0, ("%d: %s\n", i, hlist[i]->line)); + } +#else + DEBUG(0,("no history without readline support\n")); +#endif + + return 0; +} diff --git a/source4/lib/crc32.c b/source4/lib/crc32.c new file mode 100644 index 0000000000..86b0bb6fd9 --- /dev/null +++ b/source4/lib/crc32.c @@ -0,0 +1,71 @@ +/* + Unix SMB/CIFS implementation. + crc32 implementation + Copyright (C) Andrew Tridgell 2003 + + 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" + +/* table generated using algorithm from Mark Adler */ +static const uint32 crc_table[] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, + 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, + 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, + 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, + 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, + 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, + 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, + 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, + 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, + 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, + 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, + 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, + 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, + 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, + 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, + 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, + 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, + 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, + 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, + 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, + 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, + 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d +}; + + +/* + see PNG specification or ISO-3309 for details +*/ +uint32 crc32_buffer(const uint8 *buf, int n) +{ + int i; + uint32 ret; + for (ret=~0, i=0;i<n; i++) { + ret = crc_table[0xff & (buf[i] ^ ret)] ^ (ret >> 8); + } + return ~ret; +} diff --git a/source4/lib/crypto/crc32.c b/source4/lib/crypto/crc32.c new file mode 100644 index 0000000000..86b0bb6fd9 --- /dev/null +++ b/source4/lib/crypto/crc32.c @@ -0,0 +1,71 @@ +/* + Unix SMB/CIFS implementation. + crc32 implementation + Copyright (C) Andrew Tridgell 2003 + + 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" + +/* table generated using algorithm from Mark Adler */ +static const uint32 crc_table[] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, + 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, + 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, + 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, + 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, + 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, + 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, + 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, + 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, + 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, + 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, + 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, + 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, + 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, + 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, + 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, + 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, + 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, + 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, + 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, + 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, + 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d +}; + + +/* + see PNG specification or ISO-3309 for details +*/ +uint32 crc32_buffer(const uint8 *buf, int n) +{ + int i; + uint32 ret; + for (ret=~0, i=0;i<n; i++) { + ret = crc_table[0xff & (buf[i] ^ ret)] ^ (ret >> 8); + } + return ~ret; +} diff --git a/source4/lib/crypto/hmacmd5.c b/source4/lib/crypto/hmacmd5.c new file mode 100644 index 0000000000..f436fd30c0 --- /dev/null +++ b/source4/lib/crypto/hmacmd5.c @@ -0,0 +1,134 @@ +/* + Unix SMB/CIFS implementation. + HMAC MD5 code for use in NTLMv2 + Copyright (C) Luke Kenneth Casson Leighton 1996-2000 + Copyright (C) Andrew Tridgell 1992-2000 + + 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. +*/ + +/* taken direct from rfc2104 implementation and modified for suitable use + * for ntlmv2. + */ + +#include "includes.h" + +/*********************************************************************** + the rfc 2104 version of hmac_md5 initialisation. +***********************************************************************/ +void hmac_md5_init_rfc2104(uchar* key, int key_len, HMACMD5Context *ctx) +{ + int i; + + /* if key is longer than 64 bytes reset it to key=MD5(key) */ + if (key_len > 64) + { + uchar tk[16]; + struct MD5Context tctx; + + MD5Init(&tctx); + MD5Update(&tctx, key, key_len); + MD5Final(tk, &tctx); + + key = tk; + key_len = 16; + } + + /* start out by storing key in pads */ + ZERO_STRUCT(ctx->k_ipad); + ZERO_STRUCT(ctx->k_opad); + memcpy( ctx->k_ipad, key, key_len); + memcpy( ctx->k_opad, key, key_len); + + /* XOR key with ipad and opad values */ + for (i=0; i<64; i++) + { + ctx->k_ipad[i] ^= 0x36; + ctx->k_opad[i] ^= 0x5c; + } + + MD5Init(&ctx->ctx); + MD5Update(&ctx->ctx, ctx->k_ipad, 64); +} + +/*********************************************************************** + the microsoft version of hmac_md5 initialisation. +***********************************************************************/ +void hmac_md5_init_limK_to_64(const uchar* key, int key_len, + HMACMD5Context *ctx) +{ + int i; + + /* if key is longer than 64 bytes truncate it */ + if (key_len > 64) + { + key_len = 64; + } + + /* start out by storing key in pads */ + ZERO_STRUCT(ctx->k_ipad); + ZERO_STRUCT(ctx->k_opad); + memcpy( ctx->k_ipad, key, key_len); + memcpy( ctx->k_opad, key, key_len); + + /* XOR key with ipad and opad values */ + for (i=0; i<64; i++) { + ctx->k_ipad[i] ^= 0x36; + ctx->k_opad[i] ^= 0x5c; + } + + MD5Init(&ctx->ctx); + MD5Update(&ctx->ctx, ctx->k_ipad, 64); +} + +/*********************************************************************** + update hmac_md5 "inner" buffer +***********************************************************************/ +void hmac_md5_update(const uchar* text, int text_len, HMACMD5Context *ctx) +{ + MD5Update(&ctx->ctx, text, text_len); /* then text of datagram */ +} + +/*********************************************************************** + finish off hmac_md5 "inner" buffer and generate outer one. +***********************************************************************/ +void hmac_md5_final(uchar *digest, HMACMD5Context *ctx) + +{ + struct MD5Context ctx_o; + + MD5Final(digest, &ctx->ctx); + + MD5Init(&ctx_o); + MD5Update(&ctx_o, ctx->k_opad, 64); + MD5Update(&ctx_o, digest, 16); + MD5Final(digest, &ctx_o); +} + +/*********************************************************** + single function to calculate an HMAC MD5 digest from data. + use the microsoft hmacmd5 init method because the key is 16 bytes. +************************************************************/ +void hmac_md5( uchar key[16], uchar* data, int data_len, uchar* digest) +{ + HMACMD5Context ctx; + hmac_md5_init_limK_to_64(key, 16, &ctx); + if (data_len != 0) + { + hmac_md5_update(data, data_len, &ctx); + } + hmac_md5_final(digest, &ctx); +} + diff --git a/source4/lib/crypto/md4.c b/source4/lib/crypto/md4.c new file mode 100644 index 0000000000..417e87bd8e --- /dev/null +++ b/source4/lib/crypto/md4.c @@ -0,0 +1,175 @@ +/* + Unix SMB/CIFS implementation. + a implementation of MD4 designed for use in the SMB authentication protocol + Copyright (C) Andrew Tridgell 1997-1998. + + 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" + +/* NOTE: This code makes no attempt to be fast! + + It assumes that a int is at least 32 bits long +*/ + +struct mdfour_state { + uint32 A, B, C, D; +}; + +static uint32 F(uint32 X, uint32 Y, uint32 Z) +{ + return (X&Y) | ((~X)&Z); +} + +static uint32 G(uint32 X, uint32 Y, uint32 Z) +{ + return (X&Y) | (X&Z) | (Y&Z); +} + +static uint32 H(uint32 X, uint32 Y, uint32 Z) +{ + return X^Y^Z; +} + +static uint32 lshift(uint32 x, int s) +{ + x &= 0xFFFFFFFF; + return ((x<<s)&0xFFFFFFFF) | (x>>(32-s)); +} + +#define ROUND1(a,b,c,d,k,s) a = lshift(a + F(b,c,d) + X[k], s) +#define ROUND2(a,b,c,d,k,s) a = lshift(a + G(b,c,d) + X[k] + (uint32)0x5A827999,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(struct mdfour_state *s, uint32 *M) +{ + int j; + uint32 AA, BB, CC, DD; + uint32 X[16]; + + for (j=0;j<16;j++) + X[j] = M[j]; + + AA = s->A; BB = s->B; CC = s->C; DD = s->D; + + ROUND1(s->A,s->B,s->C,s->D, 0, 3); ROUND1(s->D,s->A,s->B,s->C, 1, 7); + ROUND1(s->C,s->D,s->A,s->B, 2, 11); ROUND1(s->B,s->C,s->D,s->A, 3, 19); + ROUND1(s->A,s->B,s->C,s->D, 4, 3); ROUND1(s->D,s->A,s->B,s->C, 5, 7); + ROUND1(s->C,s->D,s->A,s->B, 6, 11); ROUND1(s->B,s->C,s->D,s->A, 7, 19); + ROUND1(s->A,s->B,s->C,s->D, 8, 3); ROUND1(s->D,s->A,s->B,s->C, 9, 7); + ROUND1(s->C,s->D,s->A,s->B, 10, 11); ROUND1(s->B,s->C,s->D,s->A, 11, 19); + ROUND1(s->A,s->B,s->C,s->D, 12, 3); ROUND1(s->D,s->A,s->B,s->C, 13, 7); + ROUND1(s->C,s->D,s->A,s->B, 14, 11); ROUND1(s->B,s->C,s->D,s->A, 15, 19); + + ROUND2(s->A,s->B,s->C,s->D, 0, 3); ROUND2(s->D,s->A,s->B,s->C, 4, 5); + ROUND2(s->C,s->D,s->A,s->B, 8, 9); ROUND2(s->B,s->C,s->D,s->A, 12, 13); + ROUND2(s->A,s->B,s->C,s->D, 1, 3); ROUND2(s->D,s->A,s->B,s->C, 5, 5); + ROUND2(s->C,s->D,s->A,s->B, 9, 9); ROUND2(s->B,s->C,s->D,s->A, 13, 13); + ROUND2(s->A,s->B,s->C,s->D, 2, 3); ROUND2(s->D,s->A,s->B,s->C, 6, 5); + ROUND2(s->C,s->D,s->A,s->B, 10, 9); ROUND2(s->B,s->C,s->D,s->A, 14, 13); + ROUND2(s->A,s->B,s->C,s->D, 3, 3); ROUND2(s->D,s->A,s->B,s->C, 7, 5); + ROUND2(s->C,s->D,s->A,s->B, 11, 9); ROUND2(s->B,s->C,s->D,s->A, 15, 13); + + ROUND3(s->A,s->B,s->C,s->D, 0, 3); ROUND3(s->D,s->A,s->B,s->C, 8, 9); + ROUND3(s->C,s->D,s->A,s->B, 4, 11); ROUND3(s->B,s->C,s->D,s->A, 12, 15); + ROUND3(s->A,s->B,s->C,s->D, 2, 3); ROUND3(s->D,s->A,s->B,s->C, 10, 9); + ROUND3(s->C,s->D,s->A,s->B, 6, 11); ROUND3(s->B,s->C,s->D,s->A, 14, 15); + ROUND3(s->A,s->B,s->C,s->D, 1, 3); ROUND3(s->D,s->A,s->B,s->C, 9, 9); + ROUND3(s->C,s->D,s->A,s->B, 5, 11); ROUND3(s->B,s->C,s->D,s->A, 13, 15); + ROUND3(s->A,s->B,s->C,s->D, 3, 3); ROUND3(s->D,s->A,s->B,s->C, 11, 9); + ROUND3(s->C,s->D,s->A,s->B, 7, 11); ROUND3(s->B,s->C,s->D,s->A, 15, 15); + + s->A += AA; + s->B += BB; + s->C += CC; + s->D += DD; + + s->A &= 0xFFFFFFFF; + s->B &= 0xFFFFFFFF; + s->C &= 0xFFFFFFFF; + s->D &= 0xFFFFFFFF; + + for (j=0;j<16;j++) + X[j] = 0; +} + +static void copy64(uint32 *M, const unsigned char *in) +{ + int i; + + for (i=0;i<16;i++) + M[i] = (in[i*4+3]<<24) | (in[i*4+2]<<16) | + (in[i*4+1]<<8) | (in[i*4+0]<<0); +} + +static void copy4(unsigned char *out, uint32 x) +{ + out[0] = x&0xFF; + out[1] = (x>>8)&0xFF; + out[2] = (x>>16)&0xFF; + out[3] = (x>>24)&0xFF; +} + +/* produce a md4 message digest from data of length n bytes */ +void mdfour(unsigned char *out, const unsigned char *in, int n) +{ + unsigned char buf[128]; + uint32 M[16]; + uint32 b = n * 8; + int i; + struct mdfour_state state; + + state.A = 0x67452301; + state.B = 0xefcdab89; + state.C = 0x98badcfe; + state.D = 0x10325476; + + while (n > 64) { + copy64(M, in); + mdfour64(&state, M); + in += 64; + n -= 64; + } + + for (i=0;i<128;i++) + buf[i] = 0; + memcpy(buf, in, n); + buf[n] = 0x80; + + if (n <= 55) { + copy4(buf+56, b); + copy64(M, buf); + mdfour64(&state, M); + } else { + copy4(buf+120, b); + copy64(M, buf); + mdfour64(&state, M); + copy64(M, buf+64); + mdfour64(&state, M); + } + + for (i=0;i<128;i++) + buf[i] = 0; + copy64(M, buf); + + copy4(out, state.A); + copy4(out+4, state.B); + copy4(out+8, state.C); + copy4(out+12, state.D); +} + + diff --git a/source4/lib/crypto/md5.c b/source4/lib/crypto/md5.c new file mode 100644 index 0000000000..2121b17047 --- /dev/null +++ b/source4/lib/crypto/md5.c @@ -0,0 +1,247 @@ +/* + * This code implements the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + */ + +/* This code slightly modified to fit into Samba by + abartlet@samba.org Jun 2001 */ + +#include "includes.h" + +#include "md5.h" + +static void MD5Transform(uint32 buf[4], uint32 const in[16]); + +/* + * Note: this code is harmless on little-endian machines. + */ +static void byteReverse(unsigned char *buf, unsigned longs) +{ + uint32 t; + do { + t = (uint32) ((unsigned) buf[3] << 8 | buf[2]) << 16 | + ((unsigned) buf[1] << 8 | buf[0]); + *(uint32 *) buf = t; + buf += 4; + } while (--longs); +} + +/* + * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious + * initialization constants. + */ +void MD5Init(struct MD5Context *ctx) +{ + ctx->buf[0] = 0x67452301; + ctx->buf[1] = 0xefcdab89; + ctx->buf[2] = 0x98badcfe; + ctx->buf[3] = 0x10325476; + + ctx->bits[0] = 0; + ctx->bits[1] = 0; +} + +/* + * Update context to reflect the concatenation of another buffer full + * of bytes. + */ +void MD5Update(struct MD5Context *ctx, unsigned char const *buf, unsigned len) +{ + register uint32 t; + + /* Update bitcount */ + + t = ctx->bits[0]; + if ((ctx->bits[0] = t + ((uint32) len << 3)) < t) + ctx->bits[1]++; /* Carry from low to high */ + ctx->bits[1] += len >> 29; + + t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ + + /* Handle any leading odd-sized chunks */ + + if (t) { + unsigned char *p = (unsigned char *) ctx->in + t; + + t = 64 - t; + if (len < t) { + memmove(p, buf, len); + return; + } + memmove(p, buf, t); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (uint32 *) ctx->in); + buf += t; + len -= t; + } + /* Process data in 64-byte chunks */ + + while (len >= 64) { + memmove(ctx->in, buf, 64); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (uint32 *) ctx->in); + buf += 64; + len -= 64; + } + + /* Handle any remaining bytes of data. */ + + memmove(ctx->in, buf, len); +} + +/* + * Final wrapup - pad to 64-byte boundary with the bit pattern + * 1 0* (64-bit count of bits processed, MSB-first) + */ +void MD5Final(unsigned char digest[16], struct MD5Context *ctx) +{ + unsigned int count; + unsigned char *p; + + /* Compute number of bytes mod 64 */ + count = (ctx->bits[0] >> 3) & 0x3F; + + /* Set the first char of padding to 0x80. This is safe since there is + always at least one byte free */ + p = ctx->in + count; + *p++ = 0x80; + + /* Bytes of padding needed to make 64 bytes */ + count = 64 - 1 - count; + + /* Pad out to 56 mod 64 */ + if (count < 8) { + /* Two lots of padding: Pad the first block to 64 bytes */ + memset(p, 0, count); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (uint32 *) ctx->in); + + /* Now fill the next block with 56 bytes */ + memset(ctx->in, 0, 56); + } else { + /* Pad block to 56 bytes */ + memset(p, 0, count - 8); + } + byteReverse(ctx->in, 14); + + /* Append length in bits and transform */ + ((uint32 *) ctx->in)[14] = ctx->bits[0]; + ((uint32 *) ctx->in)[15] = ctx->bits[1]; + + MD5Transform(ctx->buf, (uint32 *) ctx->in); + byteReverse((unsigned char *) ctx->buf, 4); + memmove(digest, ctx->buf, 16); + memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */ +} + +/* The four core functions - F1 is optimized somewhat */ + +/* #define F1(x, y, z) (x & y | ~x & z) */ +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +/* This is the central step in the MD5 algorithm. */ +#define MD5STEP(f, w, x, y, z, data, s) \ + ( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x ) + +/* + * The core of the MD5 algorithm, this alters an existing MD5 hash to + * reflect the addition of 16 longwords of new data. MD5Update blocks + * the data and converts bytes into longwords for this routine. + */ +static void MD5Transform(uint32 buf[4], uint32 const in[16]) +{ + register uint32 a, b, c, d; + + a = buf[0]; + b = buf[1]; + c = buf[2]; + d = buf[3]; + + MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); + MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); + MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); + MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); + MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); + MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); + MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); + MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); + MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); + MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); + MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); + MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); + MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); + MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); + MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); + MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); + + MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); + MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); + MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); + MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); + MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); + MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); + MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); + MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); + MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); + MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); + MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); + MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); + MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); + MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); + MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); + MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); + + MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); + MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); + MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); + MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); + MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); + MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); + MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); + MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); + MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); + MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); + MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); + MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); + MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); + MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); + MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); + MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); + + MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); + MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); + MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); + MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); + MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); + MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); + MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); + MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); + MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); + MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); + MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); + MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); + MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); + MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); + MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); + MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} diff --git a/source4/lib/data_blob.c b/source4/lib/data_blob.c new file mode 100644 index 0000000000..8e7df52bef --- /dev/null +++ b/source4/lib/data_blob.c @@ -0,0 +1,141 @@ +/* + Unix SMB/CIFS implementation. + Easy management of byte-length data + Copyright (C) Andrew Tridgell 2001 + Copyright (C) Andrew Bartlett 2001 + + 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" + +/******************************************************************* + free() a data blob +*******************************************************************/ +static void free_data_blob(DATA_BLOB *d) +{ + if ((d) && (d->free)) { + SAFE_FREE(d->data); + } +} + +/******************************************************************* + construct a data blob, must be freed with data_blob_free() + you can pass NULL for p and get a blank data blob +*******************************************************************/ +DATA_BLOB data_blob(const void *p, size_t length) +{ + DATA_BLOB ret; + + if (!length) { + ZERO_STRUCT(ret); + return ret; + } + + if (p) { + ret.data = smb_xmemdup(p, length); + } else { + ret.data = smb_xmalloc(length); + } + ret.length = length; + ret.free = free_data_blob; + return ret; +} + +/******************************************************************* + construct a data blob, using supplied TALLOC_CTX +*******************************************************************/ +DATA_BLOB data_blob_talloc(TALLOC_CTX *mem_ctx, const void *p, size_t length) +{ + DATA_BLOB ret; + + if (length == 0) { + ZERO_STRUCT(ret); + return ret; + } + + if (p == NULL) { + ret.data = talloc(mem_ctx, length); + if (ret.data == NULL) { + smb_panic("data_blob_talloc: talloc_memdup failed.\n"); + } + ret.length = length; + memset(ret.data, 0, ret.length); + return ret; + } + + ret.data = talloc_memdup(mem_ctx, p, length); + if (ret.data == NULL) { + smb_panic("data_blob_talloc: talloc_memdup failed.\n"); + } + + ret.length = length; + ret.free = NULL; + return ret; +} + +/******************************************************************* +free a data blob +*******************************************************************/ +void data_blob_free(DATA_BLOB *d) +{ + if (d) { + if (d->free) { + (d->free)(d); + } + d->length = 0; + } +} + +/******************************************************************* +clear a DATA_BLOB's contents +*******************************************************************/ +void data_blob_clear(DATA_BLOB *d) +{ + if (d->data) { + memset(d->data, 0, d->length); + } +} + +/******************************************************************* +free a data blob and clear its contents +*******************************************************************/ +void data_blob_clear_free(DATA_BLOB *d) +{ + data_blob_clear(d); + data_blob_free(d); +} + + +/******************************************************************* +check if two data blobs are equal +*******************************************************************/ +BOOL data_blob_equal(DATA_BLOB *d1, DATA_BLOB *d2) +{ + if (d1->length != d2->length) { + return False; + } + if (d1->data == d2->data) { + return True; + } + if (d1->data == NULL || d2->data == NULL) { + return False; + } + if (memcmp(d1->data, d2->data, d1->length) == 0) { + return True; + } + return False; +} + diff --git a/source4/lib/debug.c b/source4/lib/debug.c new file mode 100644 index 0000000000..37f93b9ae5 --- /dev/null +++ b/source4/lib/debug.c @@ -0,0 +1,157 @@ +/* + Unix SMB/CIFS implementation. + Samba debug functions + Copyright (C) Andrew Tridgell 2003 + Copyright (C) James J Myers 2003 + + 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" + +/* this global variable determines what messages are printed */ +int DEBUGLEVEL; + + +/* the registered mutex handlers */ +static struct { + const char *name; + struct debug_ops ops; +} debug_handlers; + +/* state variables for the debug system */ +static struct { + int fd; + enum debug_logtype logtype; + const char *prog_name; +} state; + +/* + the backend for debug messages. Note that the DEBUG() macro has already + ensured that the log level has been met before this is called +*/ +void do_debug(const char *format, ...) +{ + va_list ap; + char *s = NULL; + + if (state.fd == 0) { + reopen_logs(); + } + + if (state.fd <= 0) return; + + va_start(ap, format); + vasprintf(&s, format, ap); + va_end(ap); + + write(state.fd, s, strlen(s)); + free(s); +} + +/* + reopen the log file (usually called because the log file name might have changed) +*/ +void reopen_logs(void) +{ + char *logfile = lp_logfile(); + char *fname = NULL; + int old_fd = state.fd; + + state.fd = 0; + + switch (state.logtype) { + case DEBUG_STDOUT: + state.fd = 1; + break; + + case DEBUG_STDERR: + state.fd = 2; + break; + + case DEBUG_FILE: + if ((*logfile) == '/') { + fname = strdup(logfile); + } else { + asprintf(&fname, "%s/%s.log", dyn_LOGFILEBASE, logfile); + } + if (fname) { + state.fd = open(fname, O_CREAT|O_APPEND|O_WRONLY, 0644); + free(fname); + } + break; + } + + if (old_fd > 2) { + close(old_fd); + } +} + +/* + control the name of the logfile and whether logging will be to stdout, stderr + or a file +*/ +void setup_logging(const char *prog_name, enum debug_logtype new_logtype) +{ + state.logtype = new_logtype; + state.prog_name = prog_name; + reopen_logs(); +} + +/* + return a string constant containing n tabs + no more than 10 tabs are returned +*/ +const char *do_debug_tab(uint_t n) +{ + const char *tabs[] = {"", "\t", "\t\t", "\t\t\t", "\t\t\t\t", "\t\t\t\t\t", + "\t\t\t\t\t\t", "\t\t\t\t\t\t\t", "\t\t\t\t\t\t\t\t", + "\t\t\t\t\t\t\t\t\t", "\t\t\t\t\t\t\t\t\t\t"}; + return tabs[MIN(n, 10)]; +} + + +/* + log/print suspicious usage - print comments and backtrace +*/ +void log_suspicious_usage(const char *from, const char *info) +{ + if (debug_handlers.ops.log_suspicious_usage) { + return debug_handlers.ops.log_suspicious_usage(from, info); + } +} +void print_suspicious_usage(const char* from, const char* info) +{ + if (debug_handlers.ops.print_suspicious_usage) { + return debug_handlers.ops.print_suspicious_usage(from, info); + } +} + +uint32 get_task_id(void) +{ + if (debug_handlers.ops.get_task_id) { + return debug_handlers.ops.get_task_id(); + } + return getpid(); +} + +/* + register a set of debug handlers. +*/ +void register_debug_handlers(const char *name, struct debug_ops *ops) +{ + debug_handlers.name = name; + debug_handlers.ops = *ops; +} diff --git a/source4/lib/dmallocmsg.c b/source4/lib/dmallocmsg.c new file mode 100644 index 0000000000..a83ed518d7 --- /dev/null +++ b/source4/lib/dmallocmsg.c @@ -0,0 +1,72 @@ +/* + samba -- Unix SMB/CIFS implementation. + Copyright (C) 2002 by Martin Pool + + 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" + +/** + * @file dmallocmsg.c + * + * Glue code to cause dmalloc info to come out when we receive a prod + * over samba messaging. + **/ + +#ifdef ENABLE_DMALLOC +static unsigned long our_dm_mark = 0; +#endif /* ENABLE_DMALLOC */ + + +/** + * Respond to a POOL_USAGE message by sending back string form of memory + * usage stats. + **/ +static void msg_req_dmalloc_mark(int UNUSED(msg_type), pid_t UNUSED(src_pid), + void *UNUSED(buf), size_t UNUSED(len)) +{ +#ifdef ENABLE_DMALLOC + our_dm_mark = dmalloc_mark(); + DEBUG(2,("Got MSG_REQ_DMALLOC_MARK: mark set\n")); +#else + DEBUG(2,("Got MSG_REQ_DMALLOC_MARK but dmalloc not in this process\n")); +#endif +} + + + +static void msg_req_dmalloc_log_changed(int UNUSED(msg_type), + pid_t UNUSED(src_pid), + void *UNUSED(buf), size_t UNUSED(len)) +{ +#ifdef ENABLE_DMALLOC + dmalloc_log_changed(our_dm_mark, True, True, True); + DEBUG(2,("Got MSG_REQ_DMALLOC_LOG_CHANGED: done\n")); +#else + DEBUG(2,("Got MSG_REQ_DMALLOC_LOG_CHANGED but dmalloc not in this process\n")); +#endif +} + + +/** + * Register handler for MSG_REQ_POOL_USAGE + **/ +void register_dmalloc_msgs(void) +{ + message_register(MSG_REQ_DMALLOC_MARK, msg_req_dmalloc_mark); + message_register(MSG_REQ_DMALLOC_LOG_CHANGED, msg_req_dmalloc_log_changed); + DEBUG(2, ("Registered MSG_REQ_DMALLOC_MARK and LOG_CHANGED\n")); +} diff --git a/source4/lib/dprintf.c b/source4/lib/dprintf.c new file mode 100644 index 0000000000..70387bbd61 --- /dev/null +++ b/source4/lib/dprintf.c @@ -0,0 +1,113 @@ +/* + Unix SMB/CIFS implementation. + display print functions + Copyright (C) Andrew Tridgell 2001 + + 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. +*/ + + +/* + this module provides functions for printing internal strings in the "display charset" + This charset may be quite different from the chosen unix charset + + Eventually these functions will need to take care of column count constraints + + The d_ prefix on print functions in Samba refers to the display character set + conversion +*/ + +#include "includes.h" + + int d_vfprintf(FILE *f, const char *format, va_list ap) +{ + char *p, *p2; + int ret, maxlen, clen; + const char *msgstr; + va_list ap2; + + /* do any message translations */ + msgstr = lang_msg(format); + if (!msgstr) return -1; + + VA_COPY(ap2, ap); + + ret = vasprintf(&p, msgstr, ap2); + + lang_msg_free(msgstr); + + if (ret <= 0) return ret; + + /* now we have the string in unix format, convert it to the display + charset, but beware of it growing */ + maxlen = ret*2; +again: + p2 = malloc(maxlen); + if (!p2) { + SAFE_FREE(p); + return -1; + } + clen = convert_string(CH_UNIX, CH_DISPLAY, p, ret, p2, maxlen); + + if (clen >= maxlen) { + /* it didn't fit - try a larger buffer */ + maxlen *= 2; + SAFE_FREE(p2); + goto again; + } + + /* good, its converted OK */ + SAFE_FREE(p); + ret = fwrite(p2, 1, clen, f); + SAFE_FREE(p2); + + return ret; +} + + + int d_fprintf(FILE *f, const char *format, ...) +{ + int ret; + va_list ap; + + va_start(ap, format); + ret = d_vfprintf(f, format, ap); + va_end(ap); + + return ret; +} + +static FILE *outfile; + + int d_printf(const char *format, ...) +{ + int ret; + va_list ap; + + if (!outfile) outfile = stdout; + + va_start(ap, format); + ret = d_vfprintf(outfile, format, ap); + va_end(ap); + + return ret; +} + +/* interactive programs need a way of tell d_*() to write to stderr instead + of stdout */ +void display_set_stderr(void) +{ + outfile = stderr; +} diff --git a/source4/lib/events.c b/source4/lib/events.c new file mode 100644 index 0000000000..f95028b731 --- /dev/null +++ b/source4/lib/events.c @@ -0,0 +1,372 @@ +/* + Unix SMB/CIFS implementation. + main select loop and event handling + Copyright (C) Andrew Tridgell 2003 + + 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. +*/ + +/* + PLEASE READ THIS BEFORE MODIFYING! + + This module is a general abstraction for the main select loop and + event handling. Do not ever put any localised hacks in here, instead + register one of the possible event types and implement that event + somewhere else. + + There are 4 types of event handling that are handled in this module: + + 1) a file descriptor becoming readable or writeable. This is mostly + used for network sockets, but can be used for any type of file + descriptor. You may only register one handler for each file + descriptor/io combination or you will get unpredictable results + (this means that you can have a handler for read events, and a + separate handler for write events, but not two handlers that are + both handling read events) + + 2) a timed event. You can register an event that happens at a + specific time. You can register as many of these as you + like. When they are called the handler can choose to set the time + for the next event. If next_event is not set then the event is removed. + + 3) an event that happens every time through the select loop. These + sorts of events should be very fast, as they will occur a + lot. Mostly used for things like destroying a talloc context or + checking a signal flag. + + 4) an event triggered by a signal. These can be one shot or + repeated. You can have more than one handler registered for a + single signal if you want to. + + To setup a set of events you first need to create a event_context + structure using the function event_context_init(); This returns a + 'struct event_context' that you use in all subsequent calls. + + After that you can add/remove events that you are interested in + using event_add_*() and event_remove_*(). + + Finally, you call event_loop_wait() to block waiting for one of the + events to occor. In normal operation event_loop_wait() will loop + forever, unless you call event_loop_exit() from inside one of your + handler functions. + +*/ + +#include "includes.h" + +/* + create a event_context structure. This must be the first events + call, and all subsequent calls pass this event_context as the first + element. Event handlers also receive this as their first argument. +*/ +struct event_context *event_context_init(void) +{ + struct event_context *ev; + + ev = malloc(sizeof(*ev)); + if (!ev) return NULL; + + /* start off with no events */ + ZERO_STRUCTP(ev); + + return ev; +} + + + +/* + add a fd based event + return False on failure (memory allocation error) +*/ +BOOL event_add_fd(struct event_context *ev, struct fd_event *e) +{ + e = memdup(e, sizeof(*e)); + if (!e) return False; + DLIST_ADD(ev->fd_events, e); + e->ref_count = 1; + if (e->fd > ev->maxfd) { + ev->maxfd = e->fd; + } + return True; +} + + +/* + recalculate the maxfd +*/ +static void calc_maxfd(struct event_context *ev) +{ + struct fd_event *e; + ev->maxfd = 0; + for (e=ev->fd_events; e; e=e->next) { + if (e->ref_count && + e->fd > ev->maxfd) { + ev->maxfd = e->fd; + } + } +} + +/* + remove a fd based event + the event to remove is matched by looking at the handler + function and the file descriptor + return False on failure (event not found) +*/ +BOOL event_remove_fd(struct event_context *ev, struct fd_event *e1) +{ + struct fd_event *e; + for (e=ev->fd_events; e; e=e->next) { + if (e->ref_count && + e->fd == e1->fd && + e->handler == e1->handler) { + e->ref_count--; + /* possibly calculate the new maxfd */ + calc_maxfd(ev); + return True; + } + } + return False; +} + +/* + remove all fd based events that match a specified fd +*/ +void event_remove_fd_all(struct event_context *ev, int fd) +{ + struct fd_event *e; + for (e=ev->fd_events; e; e=e->next) { + if (e->ref_count && e->fd == fd) { + e->ref_count--; + } + } + calc_maxfd(ev); +} + +/* + remove all fd based events that match a specified handler +*/ +void event_remove_fd_all_handler(struct event_context *ev, void *handler) +{ + struct fd_event *e; + for (e=ev->fd_events; e; e=e->next) { + if (e->ref_count && + handler == (void *)e->handler) { + e->ref_count--; + } + } + calc_maxfd(ev); +} + + +/* + add a timed event + return False on failure (memory allocation error) +*/ +BOOL event_add_timed(struct event_context *ev, struct timed_event *e) +{ + e = memdup(e, sizeof(*e)); + if (!e) return False; + e->ref_count = 1; + DLIST_ADD(ev->timed_events, e); + return True; +} + +/* + remove a timed event + the event to remove is matched only on the handler function + return False on failure (event not found) +*/ +BOOL event_remove_timed(struct event_context *ev, struct timed_event *e1) +{ + struct timed_event *e; + for (e=ev->timed_events; e; e=e->next) { + if (e->ref_count && + e->handler == e1->handler) { + e->ref_count--; + return True; + } + } + return False; +} + +/* + add a loop event + return False on failure (memory allocation error) +*/ +BOOL event_add_loop(struct event_context *ev, struct loop_event *e) +{ + e = memdup(e, sizeof(*e)); + if (!e) return False; + e->ref_count = 1; + DLIST_ADD(ev->loop_events, e); + return True; +} + +/* + remove a loop event + the event to remove is matched only on the handler function + return False on failure (memory allocation error) +*/ +BOOL event_remove_loop(struct event_context *ev, struct loop_event *e1) +{ + struct loop_event *e; + for (e=ev->loop_events; e; e=e->next) { + if (e->ref_count && + e->handler == e1->handler) { + e->ref_count--; + return True; + } + } + return False; +} + + +/* + tell the event loop to exit with the specified code +*/ +void event_loop_exit(struct event_context *ev, int code) +{ + ev->exit.exit_now = True; + ev->exit.code = code; +} + +/* + go into an event loop using the events defined in ev this function + will return with the specified code if one of the handlers calls + event_loop_exit() + + also return (with code 0) if all fd events are removed +*/ +int event_loop_wait(struct event_context *ev) +{ + time_t t; + + ZERO_STRUCT(ev->exit); + + t = time(NULL); + + while (ev->fd_events && !ev->exit.exit_now) { + fd_set r_fds, w_fds; + struct fd_event *fe; + struct loop_event *le; + struct timed_event *te; + int selrtn; + struct timeval tval; + + /* the loop events are called on each loop. Be careful to allow the + event to remove itself */ + for (le=ev->loop_events;le;) { + struct loop_event *next = le->next; + if (le->ref_count == 0) { + DLIST_REMOVE(ev->loop_events, le); + free(le); + } else { + le->ref_count++; + le->handler(ev, le, t); + le->ref_count--; + } + le = next; + } + + ZERO_STRUCT(tval); + FD_ZERO(&r_fds); + FD_ZERO(&w_fds); + + /* setup any fd events */ + for (fe=ev->fd_events; fe; ) { + struct fd_event *next = fe->next; + if (fe->ref_count == 0) { + DLIST_REMOVE(ev->fd_events, fe); + free(fe); + } else { + if (fe->flags & EVENT_FD_READ) { + FD_SET(fe->fd, &r_fds); + } + if (fe->flags & EVENT_FD_WRITE) { + FD_SET(fe->fd, &w_fds); + } + } + fe = next; + } + + /* start with a reasonable max timeout */ + tval.tv_sec = 600; + + /* work out the right timeout for all timed events */ + for (te=ev->timed_events;te;te=te->next) { + int timeout = te->next_event - t; + if (timeout < 0) { + timeout = 0; + } + if (te->ref_count && + timeout < tval.tv_sec) { + tval.tv_sec = timeout; + } + } + + + selrtn = sys_select(ev->maxfd+1, &r_fds, &w_fds, NULL, &tval); + + t = time(NULL); + + if (selrtn == -1 && errno == EBADF) { + /* the socket is dead! this should never + happen as the socket should have first been + made readable and that should have removed + the event, so this must be a bug. This is a + fatal error. */ + DEBUG(0,("EBADF on event_loop_wait - exiting\n")); + return -1; + } + + if (selrtn > 0) { + /* at least one file descriptor is ready - check + which ones and call the handler, being careful to allow + the handler to remove itself when called */ + for (fe=ev->fd_events; fe; fe=fe->next) { + uint16 flags = 0; + if (FD_ISSET(fe->fd, &r_fds)) flags |= EVENT_FD_READ; + if (FD_ISSET(fe->fd, &w_fds)) flags |= EVENT_FD_WRITE; + if (fe->ref_count && flags) { + fe->ref_count++; + fe->handler(ev, fe, t, flags); + fe->ref_count--; + } + } + } + + /* call any timed events that are now due */ + for (te=ev->timed_events;te;) { + struct timed_event *next = te->next; + if (te->ref_count == 0) { + DLIST_REMOVE(ev->timed_events, te); + free(te); + } else if (te->next_event <= t) { + te->ref_count++; + te->handler(ev, te, t); + te->ref_count--; + if (te->next_event <= t) { + /* the handler didn't set a time for the + next event - remove the event */ + event_remove_timed(ev, te); + } + } + te = next; + } + + } + + return ev->exit.code; +} diff --git a/source4/lib/fault.c b/source4/lib/fault.c new file mode 100644 index 0000000000..5a76ce2c0c --- /dev/null +++ b/source4/lib/fault.c @@ -0,0 +1,107 @@ +/* + Unix SMB/CIFS implementation. + Critical Fault handling + Copyright (C) Andrew Tridgell 1992-1998 + + 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" + +static void (*cont_fn)(void *); + +/* the registered fault handler */ +static struct { + const char *name; + void (*fault_handler)(int sig); +} fault_handlers; + + +/******************************************************************* +report a fault +********************************************************************/ +static void fault_report(int sig) +{ + static int counter; + + if (counter) _exit(1); + + DEBUG(0,("===============================================================\n")); + DEBUG(0,("INTERNAL ERROR: Signal %d in pid %d (%s)",sig,(int)getpid(),SAMBA_VERSION)); + DEBUG(0,("\nPlease read the file BUGS.txt in the distribution\n")); + DEBUG(0,("===============================================================\n")); + + smb_panic("internal error"); + + if (cont_fn) { + cont_fn(NULL); +#ifdef SIGSEGV + CatchSignal(SIGSEGV,SIGNAL_CAST SIG_DFL); +#endif +#ifdef SIGBUS + CatchSignal(SIGBUS,SIGNAL_CAST SIG_DFL); +#endif + return; /* this should cause a core dump */ + } + exit(1); +} + +/**************************************************************************** +catch serious errors +****************************************************************************/ +static void sig_fault(int sig) +{ + if (fault_handlers.fault_handler) { + /* we have a fault handler, call it. It may not return. */ + fault_handlers.fault_handler(sig); + } + /* If it returns or doean't exist, use regular reporter */ + fault_report(sig); +} + +/******************************************************************* +setup our fault handlers +********************************************************************/ +void fault_setup(void (*fn)(void *)) +{ + cont_fn = fn; + +#ifdef SIGSEGV + CatchSignal(SIGSEGV,SIGNAL_CAST sig_fault); +#endif +#ifdef SIGBUS + CatchSignal(SIGBUS,SIGNAL_CAST sig_fault); +#endif +} + +/* + register a fault handler. + Should only be called once in the execution of smbd. +*/ +BOOL register_fault_handler(const char *name, void (*fault_handler)(int sig)) +{ + if (fault_handlers.name != NULL) { + /* it's already registered! */ + DEBUG(2,("fault handler '%s' already registered - failed '%s'\n", + fault_handlers.name, name)); + return False; + } + + fault_handlers.name = name; + fault_handlers.fault_handler = fault_handler; + + DEBUG(2,("fault handler '%s' registered\n", name)); + return True; +} diff --git a/source4/lib/fsusage.c b/source4/lib/fsusage.c new file mode 100644 index 0000000000..bb7cff0645 --- /dev/null +++ b/source4/lib/fsusage.c @@ -0,0 +1,148 @@ +/* + Unix SMB/CIFS implementation. + functions to calculate the free disk space + Copyright (C) Andrew Tridgell 1998-2000 + + 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" + + +/* Return the number of TOSIZE-byte blocks used by + BLOCKS FROMSIZE-byte blocks, rounding away from zero. +*/ +static SMB_BIG_UINT adjust_blocks(SMB_BIG_UINT blocks, SMB_BIG_UINT fromsize, SMB_BIG_UINT tosize) +{ + if (fromsize == tosize) /* e.g., from 512 to 512 */ + return blocks; + else if (fromsize > tosize) /* e.g., from 2048 to 512 */ + return blocks * (fromsize / tosize); + else /* e.g., from 256 to 512 */ + return (blocks + 1) / (tosize / fromsize); +} + +/* this does all of the system specific guff to get the free disk space. + It is derived from code in the GNU fileutils package, but has been + considerably mangled for use here + + results are returned in *dfree and *dsize, in 512 byte units +*/ +int sys_fsusage(const char *path, SMB_BIG_UINT *dfree, SMB_BIG_UINT *dsize) +{ +#ifdef STAT_STATFS3_OSF1 +#define CONVERT_BLOCKS(B) adjust_blocks ((SMB_BIG_UINT)(B), (SMB_BIG_UINT)fsd.f_fsize, (SMB_BIG_UINT)512) + struct statfs fsd; + + if (statfs (path, &fsd, sizeof (struct statfs)) != 0) + return -1; +#endif /* STAT_STATFS3_OSF1 */ + +#ifdef STAT_STATFS2_FS_DATA /* Ultrix */ +#define CONVERT_BLOCKS(B) adjust_blocks ((SMB_BIG_UINT)(B), (SMB_BIG_UINT)1024, (SMB_BIG_UINT)512) + struct fs_data fsd; + + if (statfs (path, &fsd) != 1) + return -1; + + (*dsize) = CONVERT_BLOCKS (fsd.fd_req.btot); + (*dfree) = CONVERT_BLOCKS (fsd.fd_req.bfreen); +#endif /* STAT_STATFS2_FS_DATA */ + +#ifdef STAT_STATFS2_BSIZE /* 4.3BSD, SunOS 4, HP-UX, AIX */ +#define CONVERT_BLOCKS(B) adjust_blocks ((SMB_BIG_UINT)(B), (SMB_BIG_UINT)fsd.f_bsize, (SMB_BIG_UINT)512) + struct statfs fsd; + + if (statfs (path, &fsd) < 0) + return -1; + +#ifdef STATFS_TRUNCATES_BLOCK_COUNTS + /* In SunOS 4.1.2, 4.1.3, and 4.1.3_U1, the block counts in the + struct statfs are truncated to 2GB. These conditions detect that + truncation, presumably without botching the 4.1.1 case, in which + the values are not truncated. The correct counts are stored in + undocumented spare fields. */ + if (fsd.f_blocks == 0x1fffff && fsd.f_spare[0] > 0) { + fsd.f_blocks = fsd.f_spare[0]; + fsd.f_bfree = fsd.f_spare[1]; + fsd.f_bavail = fsd.f_spare[2]; + } +#endif /* STATFS_TRUNCATES_BLOCK_COUNTS */ +#endif /* STAT_STATFS2_BSIZE */ + + +#ifdef STAT_STATFS2_FSIZE /* 4.4BSD */ +#define CONVERT_BLOCKS(B) adjust_blocks ((SMB_BIG_UINT)(B), (SMB_BIG_UINT)fsd.f_fsize, (SMB_BIG_UINT)512) + + struct statfs fsd; + + if (statfs (path, &fsd) < 0) + return -1; +#endif /* STAT_STATFS2_FSIZE */ + +#ifdef STAT_STATFS4 /* SVR3, Dynix, Irix, AIX */ +# if _AIX || defined(_CRAY) +# define CONVERT_BLOCKS(B) adjust_blocks ((SMB_BIG_UINT)(B), (SMB_BIG_UINT)fsd.f_bsize, (SMB_BIG_UINT)512) +# ifdef _CRAY +# define f_bavail f_bfree +# endif +# else +# define CONVERT_BLOCKS(B) ((SMB_BIG_UINT)B) +# ifndef _SEQUENT_ /* _SEQUENT_ is DYNIX/ptx */ +# ifndef DOLPHIN /* DOLPHIN 3.8.alfa/7.18 has f_bavail */ +# define f_bavail f_bfree +# endif +# endif +# endif + + struct statfs fsd; + + if (statfs (path, &fsd, sizeof fsd, 0) < 0) + return -1; + /* Empirically, the block counts on most SVR3 and SVR3-derived + systems seem to always be in terms of 512-byte blocks, + no matter what value f_bsize has. */ + +#endif /* STAT_STATFS4 */ + +#if defined(STAT_STATVFS) || defined(STAT_STATVFS64) /* SVR4 */ +# define CONVERT_BLOCKS(B) \ + adjust_blocks ((SMB_BIG_UINT)(B), fsd.f_frsize ? (SMB_BIG_UINT)fsd.f_frsize : (SMB_BIG_UINT)fsd.f_bsize, (SMB_BIG_UINT)512) + +#ifdef STAT_STATVFS64 + struct statvfs64 fsd; + if (statvfs64(path, &fsd) < 0) return -1; +#else + struct statvfs fsd; + if (statvfs(path, &fsd) < 0) return -1; +#endif + + /* f_frsize isn't guaranteed to be supported. */ + +#endif /* STAT_STATVFS */ + +#ifndef CONVERT_BLOCKS + /* we don't have any dfree code! */ + return -1; +#else +#if !defined(STAT_STATFS2_FS_DATA) + /* !Ultrix */ + (*dsize) = CONVERT_BLOCKS (fsd.f_blocks); + (*dfree) = CONVERT_BLOCKS (fsd.f_bavail); +#endif /* not STAT_STATFS2_FS_DATA */ +#endif + + return 0; +} diff --git a/source4/lib/gencache.c b/source4/lib/gencache.c new file mode 100644 index 0000000000..f3740e3e12 --- /dev/null +++ b/source4/lib/gencache.c @@ -0,0 +1,372 @@ +/* + Unix SMB/CIFS implementation. + + Generic, persistent and shared between processes cache mechanism for use + by various parts of the Samba code + + Copyright (C) Rafal Szczesniak 2002 + + 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" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_TDB + +#define TIMEOUT_LEN 12 +#define CACHE_DATA_FMT "%12u/%s" + +static TDB_CONTEXT *cache; + +/** + * @file gencache.c + * @brief Generic, persistent and shared between processes cache mechanism + * for use by various parts of the Samba code + * + **/ + + +/** + * Cache initialisation function. Opens cache tdb file or creates + * it if does not exist. + * + * @return true on successful initialisation of the cache or + * false on failure + **/ + +BOOL gencache_init(void) +{ + char* cache_fname = NULL; + + /* skip file open if it's already opened */ + if (cache) return True; + + asprintf(&cache_fname, "%s/%s", lp_lockdir(), "gencache.tdb"); + if (cache_fname) + DEBUG(5, ("Opening cache file at %s\n", cache_fname)); + else { + DEBUG(0, ("Filename allocation failed.\n")); + return False; + } + + cache = tdb_open_log(cache_fname, 0, TDB_DEFAULT, + O_RDWR|O_CREAT, 0644); + + SAFE_FREE(cache_fname); + if (!cache) { + DEBUG(5, ("Attempt to open gencache.tdb has failed.\n")); + return False; + } + return True; +} + + +/** + * Cache shutdown function. Closes opened cache tdb file. + * + * @return true on successful closing the cache or + * false on failure during cache shutdown + **/ + +BOOL gencache_shutdown(void) +{ + /* tdb_close routine returns -1 on error */ + if (!cache) return False; + DEBUG(5, ("Closing cache file\n")); + return tdb_close(cache) != -1; +} + + +/** + * Set an entry in the cache file. If there's no such + * one, then add it. + * + * @param keystr string that represents a key of this entry + * @param value text representation value being cached + * @param timeout time when the value is expired + * + * @retval true when entry is successfuly stored + * @retval false on failure + **/ + +BOOL gencache_set(const char *keystr, const char *value, time_t timeout) +{ + int ret; + TDB_DATA keybuf, databuf; + char* valstr = NULL; + + /* fail completely if get null pointers passed */ + SMB_ASSERT(keystr && value); + + if (!gencache_init()) return False; + + asprintf(&valstr, CACHE_DATA_FMT, (int)timeout, value); + if (!valstr) + return False; + + keybuf.dptr = strdup(keystr); + keybuf.dsize = strlen(keystr)+1; + databuf.dptr = strdup(valstr); + databuf.dsize = strlen(valstr)+1; + DEBUG(10, ("Adding cache entry with key = %s; value = %s and timeout \ + = %s (%d seconds %s)\n", keybuf.dptr, value, ctime(&timeout), + (int)(timeout - time(NULL)), timeout > time(NULL) ? "ahead" : "in the past")); + + ret = tdb_store(cache, keybuf, databuf, 0); + SAFE_FREE(valstr); + SAFE_FREE(keybuf.dptr); + SAFE_FREE(databuf.dptr); + + return ret == 0; +} + + +/** + * Set existing entry to the cache file. + * + * @param keystr string that represents a key of this entry + * @param valstr text representation value being cached + * @param timeout time when the value is expired + * + * @retval true when entry is successfuly set + * @retval false on failure + **/ + +BOOL gencache_set_only(const char *keystr, const char *valstr, time_t timeout) +{ + int ret = -1; + TDB_DATA keybuf, databuf; + char *old_valstr, *datastr; + time_t old_timeout; + + /* fail completely if get null pointers passed */ + SMB_ASSERT(keystr && valstr); + + if (!gencache_init()) return False; + + /* + * Check whether entry exists in the cache + * Don't verify gencache_get exit code, since the entry may be expired + */ + gencache_get(keystr, &old_valstr, &old_timeout); + + if (!(old_valstr && old_timeout)) return False; + + DEBUG(10, ("Setting cache entry with key = %s; old value = %s and old timeout \ + = %s\n", keystr, old_valstr, ctime(&old_timeout))); + + asprintf(&datastr, CACHE_DATA_FMT, (int)timeout, valstr); + keybuf.dptr = strdup(keystr); + keybuf.dsize = strlen(keystr)+1; + databuf.dptr = strdup(datastr); + databuf.dsize = strlen(datastr)+1; + DEBUGADD(10, ("New value = %s, new timeout = %s (%d seconds %s)", valstr, + ctime(&timeout), (int)(timeout - time(NULL)), + timeout > time(NULL) ? "ahead" : "in the past")); + + + ret = tdb_store(cache, keybuf, databuf, TDB_REPLACE); + + SAFE_FREE(datastr); + SAFE_FREE(old_valstr); + SAFE_FREE(keybuf.dptr); + SAFE_FREE(databuf.dptr); + + return ret == 0; +} + + +/** + * Delete one entry from the cache file. + * + * @param keystr string that represents a key of this entry + * + * @retval true upon successful deletion + * @retval false in case of failure + **/ + +BOOL gencache_del(const char *keystr) +{ + int ret; + TDB_DATA keybuf; + + /* fail completely if get null pointers passed */ + SMB_ASSERT(keystr); + + if (!gencache_init()) return False; + + keybuf.dptr = strdup(keystr); + keybuf.dsize = strlen(keystr)+1; + DEBUG(10, ("Deleting cache entry (key = %s)\n", keystr)); + ret = tdb_delete(cache, keybuf); + + SAFE_FREE(keybuf.dptr); + return ret == 0; +} + + +/** + * Get existing entry from the cache file. + * + * @param keystr string that represents a key of this entry + * @param valstr buffer that is allocated and filled with the entry value + * buffer's disposing must be done outside + * @param timeout pointer to a time_t that is filled with entry's + * timeout + * + * @retval true when entry is successfuly fetched + * @retval False for failure + **/ + +BOOL gencache_get(const char *keystr, char **valstr, time_t *timeout) +{ + TDB_DATA keybuf, databuf; + + /* fail completely if get null pointers passed */ + SMB_ASSERT(keystr); + + if (!gencache_init()) + return False; + + keybuf.dptr = strdup(keystr); + keybuf.dsize = strlen(keystr)+1; + databuf = tdb_fetch(cache, keybuf); + SAFE_FREE(keybuf.dptr); + + if (databuf.dptr && databuf.dsize > TIMEOUT_LEN) { + char* entry_buf = strndup(databuf.dptr, databuf.dsize); + char *v; + time_t t; + + v = (char*)malloc(sizeof(char) * + (databuf.dsize - TIMEOUT_LEN)); + + SAFE_FREE(databuf.dptr); + sscanf(entry_buf, CACHE_DATA_FMT, (int*)&t, v); + SAFE_FREE(entry_buf); + + DEBUG(10, ("Returning %s cache entry: key = %s, value = %s, " + "timeout = %s\n", t > time(NULL) ? "valid" : + "expired", keystr, v, ctime(&t))); + + if (valstr) + *valstr = v; + else + SAFE_FREE(v); + + if (timeout) + *timeout = t; + + return t > time(NULL); + + } else { + SAFE_FREE(databuf.dptr); + + if (valstr) + *valstr = NULL; + + if (timeout) + timeout = NULL; + + DEBUG(10, ("Cache entry with key = %s couldn't be found\n", + keystr)); + + return False; + } +} + + +/** + * Iterate through all entries which key matches to specified pattern + * + * @param fn pointer to the function that will be supplied with each single + * matching cache entry (key, value and timeout) as an arguments + * @param data void pointer to an arbitrary data that is passed directly to the fn + * function on each call + * @param keystr_pattern pattern the existing entries' keys are matched to + * + **/ + +void gencache_iterate(void (*fn)(const char* key, const char *value, time_t timeout, void* dptr), + void* data, const char* keystr_pattern) +{ + TDB_LIST_NODE *node, *first_node; + TDB_DATA databuf; + char *keystr = NULL, *valstr = NULL, *entry = NULL; + time_t timeout = 0; + + /* fail completely if get null pointers passed */ + SMB_ASSERT(fn && keystr_pattern); + + if (!gencache_init()) return; + + DEBUG(5, ("Searching cache keys with pattern %s\n", keystr_pattern)); + node = tdb_search_keys(cache, keystr_pattern); + first_node = node; + + while (node) { + /* ensure null termination of the key string */ + keystr = strndup(node->node_key.dptr, node->node_key.dsize); + + /* + * We don't use gencache_get function, because we need to iterate through + * all of the entries. Validity verification is up to fn routine. + */ + databuf = tdb_fetch(cache, node->node_key); + if (!databuf.dptr || databuf.dsize <= TIMEOUT_LEN) { + SAFE_FREE(databuf.dptr); + SAFE_FREE(keystr); + node = node->next; + continue; + } + entry = strndup(databuf.dptr, databuf.dsize); + SAFE_FREE(databuf.dptr); + valstr = (char*)malloc(sizeof(char) * (databuf.dsize - TIMEOUT_LEN)); + sscanf(entry, CACHE_DATA_FMT, (int*)(&timeout), valstr); + + DEBUG(10, ("Calling function with arguments (key = %s, value = %s, timeout = %s)\n", + keystr, valstr, ctime(&timeout))); + fn(keystr, valstr, timeout, data); + + SAFE_FREE(valstr); + SAFE_FREE(entry); + SAFE_FREE(keystr); + node = node->next; + } + + tdb_search_list_free(first_node); +} + +/******************************************************************** + lock a key +********************************************************************/ + +int gencache_lock_entry( const char *key ) +{ + return tdb_lock_bystring(cache, key, 0); +} + +/******************************************************************** + unlock a key +********************************************************************/ + +void gencache_unlock_entry( const char *key ) +{ + tdb_unlock_bystring(cache, key); + return; +} + + diff --git a/source4/lib/genparser.c b/source4/lib/genparser.c new file mode 100644 index 0000000000..39c455def4 --- /dev/null +++ b/source4/lib/genparser.c @@ -0,0 +1,786 @@ +/* + Copyright (C) Andrew Tridgell <genstruct@tridgell.net> 2002 + + 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. +*/ + +/* + automatic marshalling/unmarshalling system for C structures +*/ + +#include "includes.h" + +/* see if a range of memory is all zero. Used to prevent dumping of zero elements */ +static int all_zero(const char *ptr, unsigned size) +{ + int i; + if (!ptr) return 1; + for (i=0;i<size;i++) { + if (ptr[i]) return 0; + } + return 1; +} + +/* encode a buffer of bytes into a escaped string */ +static char *encode_bytes(TALLOC_CTX *mem_ctx, const char *ptr, unsigned len) +{ + const char *hexdig = "0123456789abcdef"; + char *ret, *p; + unsigned i; + ret = talloc(mem_ctx, len*3 + 1); /* worst case size */ + if (!ret) return NULL; + for (p=ret,i=0;i<len;i++) { + if (isalnum(ptr[i]) || isspace(ptr[i]) || + (ispunct(ptr[i]) && !strchr("\\{}", ptr[i]))) { + *p++ = ptr[i]; + } else { + unsigned char c = *(const unsigned char *)(ptr+i); + if (c == 0 && all_zero(ptr+i, len-i)) break; + p[0] = '\\'; + p[1] = hexdig[c>>4]; + p[2] = hexdig[c&0xF]; + p += 3; + } + } + + *p = 0; + + return ret; +} + +/* decode an escaped string from encode_bytes() into a buffer */ +static char *decode_bytes(TALLOC_CTX *mem_ctx, const char *s, unsigned *len) +{ + char *ret, *p; + unsigned i; + int slen = strlen(s) + 1; + + ret = talloc(mem_ctx, slen); /* worst case length */ + if (!ret) + return NULL; + memset(ret, 0, slen); + + if (*s == '{') s++; + + for (p=ret,i=0;s[i];i++) { + if (s[i] == '}') { + break; + } else if (s[i] == '\\') { + unsigned v; + if (sscanf(&s[i+1], "%02x", &v) != 1 || v > 255) { + return NULL; + } + *(unsigned char *)p = v; + p++; + i += 2; + } else { + *p++ = s[i]; + } + } + *p = 0; + + (*len) = (unsigned)(p - ret); + + return ret; +} + +/* the add*() functions deal with adding things to a struct + parse_string */ + +/* allocate more space if needed */ +static int addgen_alloc(TALLOC_CTX *mem_ctx, struct parse_string *p, int n) +{ + if (p->length + n <= p->allocated) return 0; + p->allocated = p->length + n + 200; + p->s = talloc_realloc(mem_ctx, p->s, p->allocated); + if (!p->s) { + errno = ENOMEM; + return -1; + } + return 0; +} + +/* add a character to the buffer */ +static int addchar(TALLOC_CTX *mem_ctx, struct parse_string *p, char c) +{ + if (addgen_alloc(mem_ctx, p, 2) != 0) { + return -1; + } + p->s[p->length++] = c; + p->s[p->length] = 0; + return 0; +} + +/* add a string to the buffer */ +int addstr(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *s) +{ + int len = strlen(s); + if (addgen_alloc(mem_ctx, p, len+1) != 0) { + return -1; + } + memcpy(p->s + p->length, s, len+1); + p->length += len; + return 0; +} + +/* add a string to the buffer with a tab prefix */ +static int addtabbed(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *s, unsigned indent) +{ + int len = strlen(s); + if (addgen_alloc(mem_ctx, p, indent+len+1) != 0) { + return -1; + } + while (indent--) { + p->s[p->length++] = '\t'; + } + memcpy(p->s + p->length, s, len+1); + p->length += len; + return 0; +} + +/* note! this can only be used for results up to 60 chars wide! */ +int addshort(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *fmt, ...) +{ + char buf[60]; + int n; + va_list ap; + va_start(ap, fmt); + n = vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + if (addgen_alloc(mem_ctx, p, n + 1) != 0) { + return -1; + } + if (n != 0) { + memcpy(p->s + p->length, buf, n); + } + p->length += n; + p->s[p->length] = 0; + return 0; +} + +/* + this is here to make it easier for people to write dump functions + for their own types + */ +int gen_addgen(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *fmt, ...) +{ + char *buf = NULL; + int n; + va_list ap; + va_start(ap, fmt); + n = vasprintf(&buf, fmt, ap); + va_end(ap); + if (addgen_alloc(mem_ctx, p, n + 1) != 0) { + if (buf) free(buf); + return -1; + } + if (n != 0) { + memcpy(p->s + p->length, buf, n); + } + p->length += n; + p->s[p->length] = 0; + if (buf) free(buf); + return 0; +} + +/* dump a enumerated type */ +int gen_dump_enum(TALLOC_CTX *mem_ctx, + const struct enum_struct *einfo, + struct parse_string *p, + const char *ptr, + unsigned indent) +{ + unsigned v = *(const unsigned *)ptr; + int i; + for (i=0;einfo[i].name;i++) { + if (v == einfo[i].value) { + addstr(mem_ctx, p, einfo[i].name); + return 0; + } + } + /* hmm, maybe we should just fail? */ + return gen_dump_unsigned(mem_ctx, p, ptr, indent); +} + +/* dump a single non-array element, hanlding struct and enum */ +static int gen_dump_one(TALLOC_CTX *mem_ctx, + struct parse_string *p, + const struct parse_struct *pinfo, + const char *ptr, + unsigned indent) +{ + if (pinfo->dump_fn == gen_dump_char && pinfo->ptr_count == 1) { + char *s = encode_bytes(mem_ctx, ptr, strlen(ptr)); + if (addchar(mem_ctx, p,'{') || + addstr(mem_ctx, p, s) || + addstr(mem_ctx, p, "}")) { + return -1; + } + return 0; + } + + return pinfo->dump_fn(mem_ctx, p, ptr, indent); +} + +/* handle dumping of an array of arbitrary type */ +static int gen_dump_array(TALLOC_CTX *mem_ctx, + struct parse_string *p, + const struct parse_struct *pinfo, + const char *ptr, + int array_len, + int indent) +{ + int i, count=0; + + /* special handling of fixed length strings */ + if (array_len != 0 && + pinfo->ptr_count == 0 && + pinfo->dump_fn == gen_dump_char) { + char *s = encode_bytes(mem_ctx, ptr, array_len); + if (!s) return -1; + if (addtabbed(mem_ctx, p, pinfo->name, indent) || + addstr(mem_ctx, p, " = {") || + addstr(mem_ctx, p, s) || + addstr(mem_ctx, p, "}\n")) { + return -1; + } + free(s); + return 0; + } + + for (i=0;i<array_len;i++) { + const char *p2 = ptr; + unsigned size = pinfo->size; + + /* generic pointer dereference */ + if (pinfo->ptr_count) { + p2 = *(const char **)ptr; + size = sizeof(void *); + } + + if ((count || pinfo->ptr_count) && + !(pinfo->flags & FLAG_ALWAYS) && + all_zero(ptr, size)) { + ptr += size; + continue; + } + if (count == 0) { + if (addtabbed(mem_ctx, p, pinfo->name, indent) || + addshort(mem_ctx, p, " = %u:", i)) { + return -1; + } + } else { + if (addshort(mem_ctx, p, ", %u:", i) != 0) { + return -1; + } + } + if (gen_dump_one(mem_ctx, p, pinfo, p2, indent) != 0) { + return -1; + } + ptr += size; + count++; + } + if (count) { + return addstr(mem_ctx, p, "\n"); + } + return 0; +} + +/* find a variable by name in a loaded structure and return its value + as an integer. Used to support dynamic arrays */ +static int find_var(const struct parse_struct *pinfo, + const char *data, + const char *var) +{ + int i; + const char *ptr; + + /* this allows for constant lengths */ + if (isdigit(*var)) { + return atoi(var); + } + + for (i=0;pinfo[i].name;i++) { + if (strcmp(pinfo[i].name, var) == 0) break; + } + if (!pinfo[i].name) return -1; + + ptr = data + pinfo[i].offset; + + switch (pinfo[i].size) { + case sizeof(int): + return *(const int *)ptr; + case sizeof(char): + return *(const char *)ptr; + } + + return -1; +} + + +int gen_dump_struct(TALLOC_CTX *mem_ctx, + const struct parse_struct *pinfo, + struct parse_string *p, + const char *ptr, + unsigned indent) +{ + char *s = gen_dump(mem_ctx, pinfo, ptr, indent+1); + if (!s) return -1; + if (addstr(mem_ctx, p, "{\n") || + addstr(mem_ctx, p, s) || + addtabbed(mem_ctx, p, "}", indent)) { + return -1; + } + return 0; +} + +static int gen_dump_string(TALLOC_CTX *mem_ctx, + struct parse_string *p, + const struct parse_struct *pinfo, + const char *data, + unsigned indent) +{ + const char *ptr = *(const char **)data; + char *s = encode_bytes(mem_ctx, ptr, strlen(ptr)); + if (addtabbed(mem_ctx, p, pinfo->name, indent) || + addstr(mem_ctx, p, " = ") || + addchar(mem_ctx, p, '{') || + addstr(mem_ctx, p, s) || + addstr(mem_ctx, p, "}\n")) { + return -1; + } + return 0; +} + +/* + find the length of a nullterm array +*/ +static int len_nullterm(const char *ptr, int size, int array_len) +{ + int len; + + if (size == 1) { + len = strnlen(ptr, array_len); + } else { + for (len=0; len < array_len; len++) { + if (all_zero(ptr+len*size, size)) break; + } + } + + if (len == 0) len = 1; + + return len; +} + + +/* the generic dump routine. Scans the parse information for this structure + and processes it recursively */ +char *gen_dump(TALLOC_CTX *mem_ctx, + const struct parse_struct *pinfo, + const char *data, + unsigned indent) +{ + struct parse_string p; + int i; + + p.length = 0; + p.allocated = 0; + p.s = NULL; + + if (addstr(mem_ctx, &p, "") != 0) { + return NULL; + } + + for (i=0;pinfo[i].name;i++) { + const char *ptr = data + pinfo[i].offset; + unsigned size = pinfo[i].size; + + if (pinfo[i].ptr_count) { + size = sizeof(void *); + } + + /* special handling for array types */ + if (pinfo[i].array_len) { + unsigned len = pinfo[i].array_len; + if (pinfo[i].flags & FLAG_NULLTERM) { + len = len_nullterm(ptr, size, len); + } + if (gen_dump_array(mem_ctx, &p, &pinfo[i], ptr, + len, indent)) { + goto failed; + } + continue; + } + + /* and dynamically sized arrays */ + if (pinfo[i].dynamic_len) { + int len = find_var(pinfo, data, pinfo[i].dynamic_len); + struct parse_struct p2 = pinfo[i]; + if (len < 0) { + goto failed; + } + if (len > 0) { + if (pinfo[i].flags & FLAG_NULLTERM) { + len = len_nullterm(*(const char **)ptr, + pinfo[i].size, len); + } + p2.ptr_count--; + p2.dynamic_len = NULL; + if (gen_dump_array(mem_ctx, &p, &p2, + *(const char **)ptr, + len, indent) != 0) { + goto failed; + } + } + continue; + } + + /* don't dump zero elements */ + if (!(pinfo[i].flags & FLAG_ALWAYS) && all_zero(ptr, size)) continue; + + /* assume char* is a null terminated string */ + if (pinfo[i].size == 1 && pinfo[i].ptr_count == 1 && + pinfo[i].dump_fn == gen_dump_char) { + if (gen_dump_string(mem_ctx, &p, &pinfo[i], ptr, indent) != 0) { + goto failed; + } + continue; + } + + /* generic pointer dereference */ + if (pinfo[i].ptr_count) { + ptr = *(const char **)ptr; + } + + if (addtabbed(mem_ctx, &p, pinfo[i].name, indent) || + addstr(mem_ctx, &p, " = ") || + gen_dump_one(mem_ctx, &p, &pinfo[i], ptr, indent) || + addstr(mem_ctx, &p, "\n")) { + goto failed; + } + } + return p.s; + +failed: + return NULL; +} + +/* search for a character in a string, skipping over sections within + matching braces */ +static char *match_braces(char *s, char c) +{ + int depth = 0; + while (*s) { + switch (*s) { + case '}': + depth--; + break; + case '{': + depth++; + break; + } + if (depth == 0 && *s == c) { + return s; + } + s++; + } + return s; +} + +/* parse routine for enumerated types */ +int gen_parse_enum(TALLOC_CTX *mem_ctx, + const struct enum_struct *einfo, + char *ptr, + const char *str) +{ + unsigned v; + int i; + + if (isdigit(*str)) { + if (sscanf(str, "%u", &v) != 1) { + errno = EINVAL; + return -1; + } + *(unsigned *)ptr = v; + return 0; + } + + for (i=0;einfo[i].name;i++) { + if (strcmp(einfo[i].name, str) == 0) { + *(unsigned *)ptr = einfo[i].value; + return 0; + } + } + + /* unknown enum value?? */ + return -1; +} + + +/* parse all base types */ +static int gen_parse_base(TALLOC_CTX *mem_ctx, + const struct parse_struct *pinfo, + char *ptr, + const char *str) +{ + if (pinfo->parse_fn == gen_parse_char && pinfo->ptr_count==1) { + unsigned len; + char *s = decode_bytes(mem_ctx, str, &len); + if (!s) return -1; + *(char **)ptr = s; + return 0; + } + + if (pinfo->ptr_count) { + unsigned size = pinfo->ptr_count>1?sizeof(void *):pinfo->size; + struct parse_struct p2 = *pinfo; + *(void **)ptr = talloc(mem_ctx, size); + if (! *(void **)ptr) { + return -1; + } + memset(*(void **)ptr, 0, size); + ptr = *(char **)ptr; + p2.ptr_count--; + return gen_parse_base(mem_ctx, &p2, ptr, str); + } + + return pinfo->parse_fn(mem_ctx, ptr, str); +} + +/* parse a generic array */ +static int gen_parse_array(TALLOC_CTX *mem_ctx, + const struct parse_struct *pinfo, + char *ptr, + const char *str, + int array_len) +{ + char *p, *p2; + unsigned size = pinfo->size; + + /* special handling of fixed length strings */ + if (array_len != 0 && + pinfo->ptr_count == 0 && + pinfo->dump_fn == gen_dump_char) { + unsigned len = 0; + char *s = decode_bytes(mem_ctx, str, &len); + if (!s || (len > array_len)) return -1; + memset(ptr, 0, array_len); + memcpy(ptr, s, len); + return 0; + } + + if (pinfo->ptr_count) { + size = sizeof(void *); + } + + while (*str) { + unsigned idx; + int done; + + idx = atoi(str); + p = strchr(str,':'); + if (!p) break; + p++; + p2 = match_braces(p, ','); + done = (*p2 != ','); + *p2 = 0; + + if (*p == '{') { + p++; + p[strlen(p)-1] = 0; + } + + if (gen_parse_base(mem_ctx, pinfo, ptr + idx*size, p) != 0) { + return -1; + } + + if (done) break; + str = p2+1; + } + + return 0; +} + +/* parse one element, hanlding dynamic and static arrays */ +static int gen_parse_one(TALLOC_CTX *mem_ctx, + const struct parse_struct *pinfo, + const char *name, + char *data, + const char *str) +{ + int i; + for (i=0;pinfo[i].name;i++) { + if (strcmp(pinfo[i].name, name) == 0) { + break; + } + } + if (pinfo[i].name == NULL) { + return 0; + } + + if (pinfo[i].array_len) { + return gen_parse_array(mem_ctx, &pinfo[i], + data+pinfo[i].offset, + str, pinfo[i].array_len); + } + + if (pinfo[i].dynamic_len) { + int len = find_var(pinfo, data, pinfo[i].dynamic_len); + if (len < 0) { + errno = EINVAL; + return -1; + } + if (len > 0) { + struct parse_struct p2 = pinfo[i]; + char *ptr; + unsigned size = pinfo[i].ptr_count>1?sizeof(void*):pinfo[i].size; + ptr = talloc(mem_ctx, len*size); + if (!ptr) { + errno = ENOMEM; + return -1; + } + memset(ptr, 0, len*size); + *((char **)(data + pinfo[i].offset)) = ptr; + p2.ptr_count--; + p2.dynamic_len = NULL; + return gen_parse_array(mem_ctx, &p2, ptr, str, len); + } + return 0; + } + + return gen_parse_base(mem_ctx, &pinfo[i], data + pinfo[i].offset, str); +} + +int gen_parse_struct(TALLOC_CTX * mem_ctx, const struct parse_struct *pinfo, char *ptr, const char *str) +{ + return gen_parse(mem_ctx, pinfo, ptr, str); +} + +/* the main parse routine */ +int gen_parse(TALLOC_CTX *mem_ctx, const struct parse_struct *pinfo, char *data, const char *s) +{ + char *str, *s0; + + s0 = strdup(s); + str = s0; + + while (*str) { + char *p; + char *name; + char *value; + + /* skip leading whitespace */ + while (isspace(*str)) str++; + + p = strchr(str, '='); + if (!p) break; + value = p+1; + while (p > str && isspace(*(p-1))) { + p--; + } + + *p = 0; + name = str; + + while (isspace(*value)) value++; + + if (*value == '{') { + str = match_braces(value, '}'); + value++; + } else { + str = match_braces(value, '\n'); + } + + *str++ = 0; + + if (gen_parse_one(mem_ctx, pinfo, name, data, value) != 0) { + free(s0); + return -1; + } + } + + free(s0); + return 0; +} + + + +/* for convenience supply some standard dumpers and parsers here */ + +int gen_parse_char(TALLOC_CTX *mem_ctx, char *ptr, const char *str) +{ + *(unsigned char *)ptr = atoi(str); + return 0; +} + +int gen_parse_int(TALLOC_CTX *mem_ctx, char *ptr, const char *str) +{ + *(int *)ptr = atoi(str); + return 0; +} + +int gen_parse_unsigned(TALLOC_CTX *mem_ctx, char *ptr, const char *str) +{ + *(unsigned *)ptr = strtoul(str, NULL, 10); + return 0; +} + +int gen_parse_time_t(TALLOC_CTX *mem_ctx, char *ptr, const char *str) +{ + *(time_t *)ptr = strtoul(str, NULL, 10); + return 0; +} + +int gen_parse_double(TALLOC_CTX *mem_ctx, char *ptr, const char *str) +{ + *(double *)ptr = atof(str); + return 0; +} + +int gen_parse_float(TALLOC_CTX *mem_ctx, char *ptr, const char *str) +{ + *(float *)ptr = atof(str); + return 0; +} + +int gen_dump_char(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *ptr, unsigned indent) +{ + return addshort(mem_ctx, p, "%u", *(unsigned char *)(ptr)); +} + +int gen_dump_int(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *ptr, unsigned indent) +{ + return addshort(mem_ctx, p, "%d", *(int *)(ptr)); +} + +int gen_dump_unsigned(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *ptr, unsigned indent) +{ + return addshort(mem_ctx, p, "%u", *(unsigned *)(ptr)); +} + +int gen_dump_time_t(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *ptr, unsigned indent) +{ + return addshort(mem_ctx, p, "%u", *(time_t *)(ptr)); +} + +int gen_dump_double(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *ptr, unsigned indent) +{ + return addshort(mem_ctx, p, "%lg", *(double *)(ptr)); +} + +int gen_dump_float(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *ptr, unsigned indent) +{ + return addshort(mem_ctx, p, "%g", *(float *)(ptr)); +} diff --git a/source4/lib/genparser_samba.c b/source4/lib/genparser_samba.c new file mode 100644 index 0000000000..bece587747 --- /dev/null +++ b/source4/lib/genparser_samba.c @@ -0,0 +1,200 @@ +/* + Copyright (C) Andrew Tridgell <genstruct@tridgell.net> 2002 + Copyright (C) Simo Sorce <idra@samba.org> 2002 + + 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" +#include "genparser_samba.h" + +/* PARSE functions */ + +int gen_parse_uint8(TALLOC_CTX *mem_ctx, char *ptr, const char *str) +{ + *(uint8 *)ptr = atoi(str); + return 0; +} + +int gen_parse_uint16(TALLOC_CTX *mem_ctx, char *ptr, const char *str) +{ + *(uint16 *)ptr = atoi(str); + return 0; +} + +int gen_parse_uint32(TALLOC_CTX *mem_ctx, char *ptr, const char *str) +{ + *(uint32 *)ptr = strtoul(str, NULL, 10); + return 0; +} + +int gen_parse_NTTIME(TALLOC_CTX *mem_ctx, char *ptr, const char *str) +{ + if(sscanf(str, "%u,%u", &(((NTTIME *)(ptr))->high), &(((NTTIME *)(ptr))->low)) != 2) { + errno = EINVAL; + return -1; + } + return 0; +} + +int gen_parse_DOM_SID(TALLOC_CTX *mem_ctx, char *ptr, const char *str) +{ + if(!string_to_sid((DOM_SID *)ptr, str)) return -1; + return 0; +} + +int gen_parse_SEC_ACCESS(TALLOC_CTX *mem_ctx, char *ptr, const char *str) +{ + ((SEC_ACCESS *)ptr)->mask = strtoul(str, NULL, 10); + return 0; +} + +int gen_parse_GUID(TALLOC_CTX *mem_ctx, char *ptr, const char *str) +{ + int info[GUID_SIZE]; + int i; + char *sc; + char *p; + char *m; + + m = strdup(str); + if (!m) return -1; + sc = m; + + memset(info, 0, sizeof(info)); + for (i = 0; i < GUID_SIZE; i++) { + p = strchr(sc, ','); + if (p != NULL) p = '\0'; + info[i] = atoi(sc); + if (p != NULL) sc = p + 1; + } + free(m); + + for (i = 0; i < GUID_SIZE; i++) { + ((GUID *)ptr)->info[i] = info[i]; + } + + return 0; +} + +int gen_parse_SEC_ACE(TALLOC_CTX *mem_ctx, char *ptr, const char *str) +{ + return gen_parse_struct(mem_ctx, pinfo_security_ace_info, ptr, str); +} + +int gen_parse_SEC_ACL(TALLOC_CTX *mem_ctx, char *ptr, const char *str) +{ + return gen_parse_struct(mem_ctx, pinfo_security_acl_info, ptr, str); +} + +int gen_parse_SEC_DESC(TALLOC_CTX *mem_ctx, char *ptr, const char *str) +{ + return gen_parse_struct(mem_ctx, pinfo_security_descriptor_info, ptr, str); +} + +int gen_parse_LUID_ATTR(TALLOC_CTX *mem_ctx, char *ptr, const char *str) +{ + return gen_parse_struct(mem_ctx, pinfo_luid_attr_info, ptr, str); +} + +int gen_parse_LUID(TALLOC_CTX *mem_ctx, char *ptr, const char *str) +{ + if(sscanf(str, "%u,%u", &(((LUID *)(ptr))->high), &(((LUID *)(ptr))->low)) != 2) { + errno = EINVAL; + return -1; + } + return 0; +} + + + +/* DUMP functions */ + +int gen_dump_uint8(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *ptr, unsigned indent) +{ + return addshort(mem_ctx, p, "%u", *(uint8 *)(ptr)); +} + +int gen_dump_uint16(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *ptr, unsigned indent) +{ + return addshort(mem_ctx, p, "%u", *(uint16 *)(ptr)); +} + +int gen_dump_uint32(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *ptr, unsigned indent) +{ + return addshort(mem_ctx, p, "%u", *(uint32 *)(ptr)); +} + +int gen_dump_NTTIME(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *ptr, unsigned indent) +{ + uint32 low, high; + + high = ((NTTIME *)(ptr))->high; + low = ((NTTIME *)(ptr))->low; + return addshort(mem_ctx, p, "%u,%u", high, low); +} + +int gen_dump_DOM_SID(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *ptr, unsigned indent) +{ + fstring sidstr; + + sid_to_string(sidstr, (DOM_SID *)ptr); + return addstr(mem_ctx, p, sidstr); +} + +int gen_dump_SEC_ACCESS(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *ptr, unsigned indent) +{ + return addshort(mem_ctx, p, "%u", ((SEC_ACCESS *)ptr)->mask); +} + +int gen_dump_GUID(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *ptr, unsigned indent) +{ + int i, r; + + for (i = 0; i < (GUID_SIZE - 1); i++) { + if (!(r = addshort(mem_ctx, p, "%d,", ((GUID *)ptr)->info[i]))) return r; + } + return addshort(mem_ctx, p, "%d", ((GUID *)ptr)->info[i]); +} + +int gen_dump_SEC_ACE(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *ptr, unsigned indent) +{ + return gen_dump_struct(mem_ctx, pinfo_security_ace_info, p, ptr, indent); +} + +int gen_dump_SEC_ACL(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *ptr, unsigned indent) +{ + return gen_dump_struct(mem_ctx, pinfo_security_acl_info, p, ptr, indent); +} + +int gen_dump_SEC_DESC(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *ptr, unsigned indent) +{ + return gen_dump_struct(mem_ctx, pinfo_security_descriptor_info, p, ptr, indent); +} + +int gen_dump_LUID_ATTR(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *ptr, unsigned indent) +{ + return gen_dump_struct(mem_ctx, pinfo_luid_attr_info, p, ptr, indent); +} + +int gen_dump_LUID(TALLOC_CTX *mem_ctx, struct parse_string *p, const char *ptr, unsigned indent) +{ + uint32 low, high; + + high = ((LUID *)(ptr))->high; + low = ((LUID *)(ptr))->low; + return addshort(mem_ctx, p, "%u,%u", high, low); +} + diff --git a/source4/lib/genrand.c b/source4/lib/genrand.c new file mode 100644 index 0000000000..e2e66f7e58 --- /dev/null +++ b/source4/lib/genrand.c @@ -0,0 +1,267 @@ +/* + Unix SMB/CIFS implementation. + + Functions to create reasonable random numbers for crypto use. + + Copyright (C) Jeremy Allison 2001 + + 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" + +static unsigned char hash[258]; +static uint32 counter; +static unsigned char *reseed_data; +static size_t reseed_data_size; + +/**************************************************************** + Copy any user given reseed data. +*****************************************************************/ + +void set_rand_reseed_data(unsigned char *data, size_t len) +{ + SAFE_FREE(reseed_data); + reseed_data_size = 0; + + reseed_data = (unsigned char *)memdup(data, len); + if (reseed_data) + reseed_data_size = len; +} + +/**************************************************************** + Setup the seed. +*****************************************************************/ + +static void seed_random_stream(unsigned char *seedval, size_t seedlen) +{ + unsigned char j = 0; + size_t ind; + + for (ind = 0; ind < 256; ind++) + hash[ind] = (unsigned char)ind; + + for( ind = 0; ind < 256; ind++) { + unsigned char tc; + + j += (hash[ind] + seedval[ind%seedlen]); + + tc = hash[ind]; + hash[ind] = hash[j]; + hash[j] = tc; + } + + hash[256] = 0; + hash[257] = 0; +} + +/**************************************************************** + Get datasize bytes worth of random data. +*****************************************************************/ + +static void get_random_stream(unsigned char *data, size_t datasize) +{ + unsigned char index_i = hash[256]; + unsigned char index_j = hash[257]; + size_t ind; + + for( ind = 0; ind < datasize; ind++) { + unsigned char tc; + unsigned char t; + + index_i++; + index_j += hash[index_i]; + + tc = hash[index_i]; + hash[index_i] = hash[index_j]; + hash[index_j] = tc; + + t = hash[index_i] + hash[index_j]; + data[ind] = hash[t]; + } + + hash[256] = index_i; + hash[257] = index_j; +} + +/**************************************************************** + Get a 16 byte hash from the contents of a file. + Note that the hash is not initialised. +*****************************************************************/ + +static void do_filehash(const char *fname, unsigned char *the_hash) +{ + unsigned char buf[1011]; /* deliberate weird size */ + unsigned char tmp_md4[16]; + int fd, n; + + fd = sys_open(fname,O_RDONLY,0); + if (fd == -1) + return; + + while ((n = read(fd, (char *)buf, sizeof(buf))) > 0) { + mdfour(tmp_md4, buf, n); + for (n=0;n<16;n++) + the_hash[n] ^= tmp_md4[n]; + } + close(fd); +} + +/************************************************************** + Try and get a good random number seed. Try a number of + different factors. Firstly, try /dev/urandom - use if exists. + + We use /dev/urandom as a read of /dev/random can block if + the entropy pool dries up. This leads clients to timeout + or be very slow on connect. + + If we can't use /dev/urandom then seed the stream random generator + above... +**************************************************************/ + +static int do_reseed(BOOL use_fd, int fd) +{ + unsigned char seed_inbuf[40]; + uint32 v1, v2; struct timeval tval; pid_t mypid; + struct passwd *pw; + + if (use_fd) { + if (fd != -1) + return fd; + + fd = sys_open( "/dev/urandom", O_RDONLY,0); + if(fd >= 0) + return fd; + } + + /* Add in some secret file contents */ + + do_filehash("/etc/shadow", &seed_inbuf[0]); + do_filehash(lp_smb_passwd_file(), &seed_inbuf[16]); + + /* + * Add in the root encrypted password. + * On any system where security is taken + * seriously this will be secret. + */ + + pw = getpwnam_alloc("root"); + if (pw && pw->pw_passwd) { + size_t i; + unsigned char md4_tmp[16]; + mdfour(md4_tmp, (unsigned char *)pw->pw_passwd, strlen(pw->pw_passwd)); + for (i=0;i<16;i++) + seed_inbuf[8+i] ^= md4_tmp[i]; + passwd_free(&pw); + } + + /* + * Add the counter, time of day, and pid. + */ + + GetTimeOfDay(&tval); + mypid = getpid(); + v1 = (counter++) + mypid + tval.tv_sec; + v2 = (counter++) * mypid + tval.tv_usec; + + SIVAL(seed_inbuf, 32, v1 ^ IVAL(seed_inbuf, 32)); + SIVAL(seed_inbuf, 36, v2 ^ IVAL(seed_inbuf, 36)); + + /* + * Add any user-given reseed data. + */ + + if (reseed_data) { + size_t i; + for (i = 0; i < sizeof(seed_inbuf); i++) + seed_inbuf[i] ^= reseed_data[i % reseed_data_size]; + } + + seed_random_stream(seed_inbuf, sizeof(seed_inbuf)); + + return -1; +} + +/******************************************************************* + Interface to the (hopefully) good crypto random number generator. +********************************************************************/ + +void generate_random_buffer( unsigned char *out, int len, BOOL do_reseed_now) +{ + static BOOL done_reseed = False; + static int urand_fd = -1; + unsigned char md4_buf[64]; + unsigned char tmp_buf[16]; + unsigned char *p; + + if(!done_reseed || do_reseed_now) { + urand_fd = do_reseed(True, urand_fd); + done_reseed = True; + } + + if (urand_fd != -1 && len > 0) { + + if (read(urand_fd, out, len) == len) + return; /* len bytes of random data read from urandom. */ + + /* Read of urand error, drop back to non urand method. */ + close(urand_fd); + urand_fd = -1; + do_reseed(False, -1); + done_reseed = True; + } + + /* + * Generate random numbers in chunks of 64 bytes, + * then md4 them & copy to the output buffer. + * This way the raw state of the stream is never externally + * seen. + */ + + p = out; + while(len > 0) { + int copy_len = len > 16 ? 16 : len; + + get_random_stream(md4_buf, sizeof(md4_buf)); + mdfour(tmp_buf, md4_buf, sizeof(md4_buf)); + memcpy(p, tmp_buf, copy_len); + p += copy_len; + len -= copy_len; + } +} + +/******************************************************************* + Use the random number generator to generate a random string. +********************************************************************/ + +static char c_list[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+_-#.,"; + +char *generate_random_str(size_t len) +{ + static unsigned char retstr[256]; + size_t i; + + memset(retstr, '\0', sizeof(retstr)); + + if (len > sizeof(retstr)-1) + len = sizeof(retstr) -1; + generate_random_buffer( retstr, len, False); + for (i = 0; i < len; i++) + retstr[i] = c_list[ retstr[i] % (sizeof(c_list)-1) ]; + + retstr[i] = '\0'; + + return (char *)retstr; +} diff --git a/source4/lib/getsmbpass.c b/source4/lib/getsmbpass.c new file mode 100644 index 0000000000..b6ae09b318 --- /dev/null +++ b/source4/lib/getsmbpass.c @@ -0,0 +1,156 @@ +/* Copyright (C) 1992-1998 Free Software Foundation, Inc. +This file is part of the GNU C Library. + +The GNU C Library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +The GNU C 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 +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with the GNU C Library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 675 Mass Ave, +Cambridge, MA 02139, USA. */ + +/* Modified to use with samba by Jeremy Allison, 8th July 1995. */ + +#include "includes.h" + +#ifdef REPLACE_GETPASS + +#ifdef SYSV_TERMIO + +/* SYSTEM V TERMIO HANDLING */ + +static struct termio t; + +#define ECHO_IS_ON(t) ((t).c_lflag & ECHO) +#define TURN_ECHO_OFF(t) ((t).c_lflag &= ~ECHO) +#define TURN_ECHO_ON(t) ((t).c_lflag |= ECHO) + +#ifndef TCSAFLUSH +#define TCSAFLUSH 1 +#endif + +#ifndef TCSANOW +#define TCSANOW 0 +#endif + +static int tcgetattr(int fd, struct termio *t) +{ + return ioctl(fd, TCGETA, t); +} + +static int tcsetattr(int fd, int flags, struct termio *t) +{ + if(flags & TCSAFLUSH) + ioctl(fd, TCFLSH, TCIOFLUSH); + return ioctl(fd, TCSETS, t); +} + +#elif !defined(TCSAFLUSH) + +/* BSD TERMIO HANDLING */ + +static struct sgttyb t; + +#define ECHO_IS_ON(t) ((t).sg_flags & ECHO) +#define TURN_ECHO_OFF(t) ((t).sg_flags &= ~ECHO) +#define TURN_ECHO_ON(t) ((t).sg_flags |= ECHO) + +#define TCSAFLUSH 1 +#define TCSANOW 0 + +static int tcgetattr(int fd, struct sgttyb *t) +{ + return ioctl(fd, TIOCGETP, (char *)t); +} + +static int tcsetattr(int fd, int flags, struct sgttyb *t) +{ + return ioctl(fd, TIOCSETP, (char *)t); +} + +#else /* POSIX TERMIO HANDLING */ +#define ECHO_IS_ON(t) ((t).c_lflag & ECHO) +#define TURN_ECHO_OFF(t) ((t).c_lflag &= ~ECHO) +#define TURN_ECHO_ON(t) ((t).c_lflag |= ECHO) + +static struct termios t; +#endif /* SYSV_TERMIO */ + +char *getsmbpass(const char *prompt) +{ + FILE *in, *out; + int echo_off; + static char buf[256]; + static size_t bufsize = sizeof(buf); + size_t nread; + + /* Catch problematic signals */ + CatchSignal(SIGINT, SIGNAL_CAST SIG_IGN); + + /* Try to write to and read from the terminal if we can. + If we can't open the terminal, use stderr and stdin. */ + + in = fopen ("/dev/tty", "w+"); + if (in == NULL) + { + in = stdin; + out = stderr; + } + else + out = in; + + setvbuf(in, NULL, _IONBF, 0); + + /* Turn echoing off if it is on now. */ + + if (tcgetattr (fileno (in), &t) == 0) + { + if (ECHO_IS_ON(t)) + { + TURN_ECHO_OFF(t); + echo_off = tcsetattr (fileno (in), TCSAFLUSH, &t) == 0; + TURN_ECHO_ON(t); + } + else + echo_off = 0; + } + else + echo_off = 0; + + /* Write the prompt. */ + fputs (prompt, out); + fflush (out); + + /* Read the password. */ + buf[0] = 0; + fgets(buf, bufsize, in); + nread = strlen(buf); + if (buf[nread - 1] == '\n') + buf[nread - 1] = '\0'; + + /* Restore echoing. */ + if (echo_off) + (void) tcsetattr (fileno (in), TCSANOW, &t); + + if (in != stdin) + /* We opened the terminal; now close it. */ + fclose (in); + + /* Catch problematic signals */ + CatchSignal(SIGINT, SIGNAL_CAST SIG_DFL); + + printf("\n"); + return buf; +} + +#else + void getsmbpasswd_dummy(void); + void getsmbpasswd_dummy(void) {;} +#endif diff --git a/source4/lib/hmacmd5.c b/source4/lib/hmacmd5.c new file mode 100644 index 0000000000..f436fd30c0 --- /dev/null +++ b/source4/lib/hmacmd5.c @@ -0,0 +1,134 @@ +/* + Unix SMB/CIFS implementation. + HMAC MD5 code for use in NTLMv2 + Copyright (C) Luke Kenneth Casson Leighton 1996-2000 + Copyright (C) Andrew Tridgell 1992-2000 + + 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. +*/ + +/* taken direct from rfc2104 implementation and modified for suitable use + * for ntlmv2. + */ + +#include "includes.h" + +/*********************************************************************** + the rfc 2104 version of hmac_md5 initialisation. +***********************************************************************/ +void hmac_md5_init_rfc2104(uchar* key, int key_len, HMACMD5Context *ctx) +{ + int i; + + /* if key is longer than 64 bytes reset it to key=MD5(key) */ + if (key_len > 64) + { + uchar tk[16]; + struct MD5Context tctx; + + MD5Init(&tctx); + MD5Update(&tctx, key, key_len); + MD5Final(tk, &tctx); + + key = tk; + key_len = 16; + } + + /* start out by storing key in pads */ + ZERO_STRUCT(ctx->k_ipad); + ZERO_STRUCT(ctx->k_opad); + memcpy( ctx->k_ipad, key, key_len); + memcpy( ctx->k_opad, key, key_len); + + /* XOR key with ipad and opad values */ + for (i=0; i<64; i++) + { + ctx->k_ipad[i] ^= 0x36; + ctx->k_opad[i] ^= 0x5c; + } + + MD5Init(&ctx->ctx); + MD5Update(&ctx->ctx, ctx->k_ipad, 64); +} + +/*********************************************************************** + the microsoft version of hmac_md5 initialisation. +***********************************************************************/ +void hmac_md5_init_limK_to_64(const uchar* key, int key_len, + HMACMD5Context *ctx) +{ + int i; + + /* if key is longer than 64 bytes truncate it */ + if (key_len > 64) + { + key_len = 64; + } + + /* start out by storing key in pads */ + ZERO_STRUCT(ctx->k_ipad); + ZERO_STRUCT(ctx->k_opad); + memcpy( ctx->k_ipad, key, key_len); + memcpy( ctx->k_opad, key, key_len); + + /* XOR key with ipad and opad values */ + for (i=0; i<64; i++) { + ctx->k_ipad[i] ^= 0x36; + ctx->k_opad[i] ^= 0x5c; + } + + MD5Init(&ctx->ctx); + MD5Update(&ctx->ctx, ctx->k_ipad, 64); +} + +/*********************************************************************** + update hmac_md5 "inner" buffer +***********************************************************************/ +void hmac_md5_update(const uchar* text, int text_len, HMACMD5Context *ctx) +{ + MD5Update(&ctx->ctx, text, text_len); /* then text of datagram */ +} + +/*********************************************************************** + finish off hmac_md5 "inner" buffer and generate outer one. +***********************************************************************/ +void hmac_md5_final(uchar *digest, HMACMD5Context *ctx) + +{ + struct MD5Context ctx_o; + + MD5Final(digest, &ctx->ctx); + + MD5Init(&ctx_o); + MD5Update(&ctx_o, ctx->k_opad, 64); + MD5Update(&ctx_o, digest, 16); + MD5Final(digest, &ctx_o); +} + +/*********************************************************** + single function to calculate an HMAC MD5 digest from data. + use the microsoft hmacmd5 init method because the key is 16 bytes. +************************************************************/ +void hmac_md5( uchar key[16], uchar* data, int data_len, uchar* digest) +{ + HMACMD5Context ctx; + hmac_md5_init_limK_to_64(key, 16, &ctx); + if (data_len != 0) + { + hmac_md5_update(data, data_len, &ctx); + } + hmac_md5_final(digest, &ctx); +} + diff --git a/source4/lib/iconv.c b/source4/lib/iconv.c new file mode 100644 index 0000000000..8f85e29c2e --- /dev/null +++ b/source4/lib/iconv.c @@ -0,0 +1,526 @@ +/* + Unix SMB/CIFS implementation. + minimal iconv implementation + Copyright (C) Andrew Tridgell 2001 + Copyright (C) Jelmer Vernooij 2002 + + 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" + + +/** + * @file + * + * @brief Samba wrapper/stub for iconv character set conversion. + * + * iconv is the XPG2 interface for converting between character + * encodings. This file provides a Samba wrapper around it, and also + * a simple reimplementation that is used if the system does not + * implement iconv. + * + * Samba only works with encodings that are supersets of ASCII: ascii + * characters like whitespace can be tested for directly, multibyte + * sequences start with a byte with the high bit set, and strings are + * terminated by a nul byte. + * + * Note that the only function provided by iconv is conversion between + * characters. It doesn't directly support operations like + * uppercasing or comparison. We have to convert to UCS-2 and compare + * there. + * + * @sa Samba Developers Guide + **/ + +static size_t ascii_pull(void *,const char **, size_t *, char **, size_t *); +static size_t ascii_push(void *,const char **, size_t *, char **, size_t *); +static size_t utf8_pull(void *,const char **, size_t *, char **, size_t *); +static size_t utf8_push(void *,const char **, size_t *, char **, size_t *); +static size_t ucs2hex_pull(void *,const char **, size_t *, char **, size_t *); +static size_t ucs2hex_push(void *,const char **, size_t *, char **, size_t *); +static size_t iconv_copy(void *,const char **, size_t *, char **, size_t *); + +static struct charset_functions builtin_functions[] = { + {"UCS-2LE", iconv_copy, iconv_copy}, + {"UTF8", utf8_pull, utf8_push}, + {"ASCII", ascii_pull, ascii_push}, + {"UCS2-HEX", ucs2hex_pull, ucs2hex_push}, + {NULL, NULL, NULL} +}; + +static struct charset_functions *charsets = NULL; + +BOOL smb_register_charset(struct charset_functions *funcs) +{ + struct charset_functions *c = charsets; + + DEBUG(5, ("Attempting to register new charset %s\n", funcs->name)); + /* Check whether we already have this charset... */ + while(c) { + if(!strcasecmp(c->name, funcs->name)){ + DEBUG(2, ("Duplicate charset %s, not registering\n", funcs->name)); + return False; + } + c = c->next; + } + + funcs->next = funcs->prev = NULL; + DEBUG(5, ("Registered charset %s\n", funcs->name)); + DLIST_ADD(charsets, funcs); + return True; +} + +static void lazy_initialize_iconv(void) +{ + static BOOL initialized = False; + int i; + + if (!initialized) { + initialized = True; + for(i = 0; builtin_functions[i].name; i++) + smb_register_charset(&builtin_functions[i]); + } +} + +#ifdef HAVE_NATIVE_ICONV +/* if there was an error then reset the internal state, + this ensures that we don't have a shift state remaining for + character sets like SJIS */ +static size_t sys_iconv(void *cd, + const char **inbuf, size_t *inbytesleft, + char **outbuf, size_t *outbytesleft) +{ + size_t ret = iconv((iconv_t)cd, + inbuf, inbytesleft, + outbuf, outbytesleft); + if (ret == (size_t)-1) iconv(cd, NULL, NULL, NULL, NULL); + return ret; +} +#endif + +/** + * This is a simple portable iconv() implementaion. + * + * It only knows about a very small number of character sets - just + * enough that Samba works on systems that don't have iconv. + **/ +size_t smb_iconv(smb_iconv_t cd, + const char **inbuf, size_t *inbytesleft, + char **outbuf, size_t *outbytesleft) +{ + char cvtbuf[2048]; + char *bufp = cvtbuf; + size_t bufsize; + + /* in many cases we can go direct */ + if (cd->direct) { + return cd->direct(cd->cd_direct, + inbuf, inbytesleft, outbuf, outbytesleft); + } + + + /* otherwise we have to do it chunks at a time */ + while (*inbytesleft > 0) { + bufp = cvtbuf; + bufsize = sizeof(cvtbuf); + + if (cd->pull(cd->cd_pull, + inbuf, inbytesleft, &bufp, &bufsize) == -1 + && errno != E2BIG) return -1; + + bufp = cvtbuf; + bufsize = sizeof(cvtbuf) - bufsize; + + if (cd->push(cd->cd_push, + &bufp, &bufsize, + outbuf, outbytesleft) == -1) return -1; + } + + return 0; +} + +/* + simple iconv_open() wrapper + */ +smb_iconv_t smb_iconv_open(const char *tocode, const char *fromcode) +{ + smb_iconv_t ret; + struct charset_functions *from, *to; + + lazy_initialize_iconv(); + from = charsets; + to = charsets; + + ret = (smb_iconv_t)malloc(sizeof(*ret)); + if (!ret) { + errno = ENOMEM; + return (smb_iconv_t)-1; + } + memset(ret, 0, sizeof(*ret)); + + ret->from_name = strdup(fromcode); + ret->to_name = strdup(tocode); + + /* check for the simplest null conversion */ + if (strcmp(fromcode, tocode) == 0) { + ret->direct = iconv_copy; + return ret; + } + + while (from) { + if (strcasecmp(from->name, fromcode) == 0) break; + from = from->next; + } + + while (to) { + if (strcasecmp(to->name, tocode) == 0) break; + to = to->next; + } + +#ifdef HAVE_NATIVE_ICONV + if (!from) { + ret->pull = sys_iconv; + ret->cd_pull = iconv_open("UCS-2LE", fromcode); + if (ret->cd_pull == (iconv_t)-1) goto failed; + } + + if (!to) { + ret->push = sys_iconv; + ret->cd_push = iconv_open(tocode, "UCS-2LE"); + if (ret->cd_push == (iconv_t)-1) goto failed; + } +#else + if (!from || !to) { + goto failed; + } +#endif + + /* check for conversion to/from ucs2 */ + if (strcasecmp(fromcode, "UCS-2LE") == 0 && to) { + ret->direct = to->push; + return ret; + } + if (strcasecmp(tocode, "UCS-2LE") == 0 && from) { + ret->direct = from->pull; + return ret; + } + +#ifdef HAVE_NATIVE_ICONV + if (strcasecmp(fromcode, "UCS-2LE") == 0) { + ret->direct = sys_iconv; + ret->cd_direct = ret->cd_push; + ret->cd_push = NULL; + return ret; + } + if (strcasecmp(tocode, "UCS-2LE") == 0) { + ret->direct = sys_iconv; + ret->cd_direct = ret->cd_pull; + ret->cd_pull = NULL; + return ret; + } +#endif + + /* the general case has to go via a buffer */ + if (!ret->pull) ret->pull = from->pull; + if (!ret->push) ret->push = to->push; + return ret; + +failed: + SAFE_FREE(ret); + errno = EINVAL; + return (smb_iconv_t)-1; +} + +/* + simple iconv_close() wrapper +*/ +int smb_iconv_close (smb_iconv_t cd) +{ +#ifdef HAVE_NATIVE_ICONV + if (cd->cd_direct) iconv_close((iconv_t)cd->cd_direct); + if (cd->cd_pull) iconv_close((iconv_t)cd->cd_pull); + if (cd->cd_push) iconv_close((iconv_t)cd->cd_push); +#endif + + SAFE_FREE(cd->from_name); + SAFE_FREE(cd->to_name); + + memset(cd, 0, sizeof(*cd)); + SAFE_FREE(cd); + return 0; +} + + +/********************************************************************** + the following functions implement the builtin character sets in Samba + and also the "test" character sets that are designed to test + multi-byte character set support for english users +***********************************************************************/ +static size_t ascii_pull(void *cd, const char **inbuf, size_t *inbytesleft, + char **outbuf, size_t *outbytesleft) +{ + while (*inbytesleft >= 1 && *outbytesleft >= 2) { + (*outbuf)[0] = (*inbuf)[0]; + (*outbuf)[1] = 0; + (*inbytesleft) -= 1; + (*outbytesleft) -= 2; + (*inbuf) += 1; + (*outbuf) += 2; + } + + if (*inbytesleft > 0) { + errno = E2BIG; + return -1; + } + + return 0; +} + +static size_t ascii_push(void *cd, const char **inbuf, size_t *inbytesleft, + char **outbuf, size_t *outbytesleft) +{ + int ir_count=0; + + while (*inbytesleft >= 2 && *outbytesleft >= 1) { + (*outbuf)[0] = (*inbuf)[0] & 0x7F; + if ((*inbuf)[1]) ir_count++; + (*inbytesleft) -= 2; + (*outbytesleft) -= 1; + (*inbuf) += 2; + (*outbuf) += 1; + } + + if (*inbytesleft == 1) { + errno = EINVAL; + return -1; + } + + if (*inbytesleft > 1) { + errno = E2BIG; + return -1; + } + + return ir_count; +} + + +static size_t ucs2hex_pull(void *cd, const char **inbuf, size_t *inbytesleft, + char **outbuf, size_t *outbytesleft) +{ + while (*inbytesleft >= 1 && *outbytesleft >= 2) { + unsigned v; + + if ((*inbuf)[0] != '@') { + /* seven bit ascii case */ + (*outbuf)[0] = (*inbuf)[0]; + (*outbuf)[1] = 0; + (*inbytesleft) -= 1; + (*outbytesleft) -= 2; + (*inbuf) += 1; + (*outbuf) += 2; + continue; + } + /* it's a hex character */ + if (*inbytesleft < 5) { + errno = EINVAL; + return -1; + } + + if (sscanf(&(*inbuf)[1], "%04x", &v) != 1) { + errno = EILSEQ; + return -1; + } + + (*outbuf)[0] = v&0xff; + (*outbuf)[1] = v>>8; + (*inbytesleft) -= 5; + (*outbytesleft) -= 2; + (*inbuf) += 5; + (*outbuf) += 2; + } + + if (*inbytesleft > 0) { + errno = E2BIG; + return -1; + } + + return 0; +} + +static size_t ucs2hex_push(void *cd, const char **inbuf, size_t *inbytesleft, + char **outbuf, size_t *outbytesleft) +{ + while (*inbytesleft >= 2 && *outbytesleft >= 1) { + char buf[6]; + + if ((*inbuf)[1] == 0 && + ((*inbuf)[0] & 0x80) == 0 && + (*inbuf)[0] != '@') { + (*outbuf)[0] = (*inbuf)[0]; + (*inbytesleft) -= 2; + (*outbytesleft) -= 1; + (*inbuf) += 2; + (*outbuf) += 1; + continue; + } + if (*outbytesleft < 5) { + errno = E2BIG; + return -1; + } + snprintf(buf, 6, "@%04x", SVAL(*inbuf, 0)); + memcpy(*outbuf, buf, 5); + (*inbytesleft) -= 2; + (*outbytesleft) -= 5; + (*inbuf) += 2; + (*outbuf) += 5; + } + + if (*inbytesleft == 1) { + errno = EINVAL; + return -1; + } + + if (*inbytesleft > 1) { + errno = E2BIG; + return -1; + } + + return 0; +} + + +static size_t iconv_copy(void *cd, const char **inbuf, size_t *inbytesleft, + char **outbuf, size_t *outbytesleft) +{ + int n; + + n = MIN(*inbytesleft, *outbytesleft); + + memmove(*outbuf, *inbuf, n); + + (*inbytesleft) -= n; + (*outbytesleft) -= n; + (*inbuf) += n; + (*outbuf) += n; + + if (*inbytesleft > 0) { + errno = E2BIG; + return -1; + } + + return 0; +} + +static size_t utf8_pull(void *cd, const char **inbuf, size_t *inbytesleft, + char **outbuf, size_t *outbytesleft) +{ + while (*inbytesleft >= 1 && *outbytesleft >= 2) { + const unsigned char *c = (const unsigned char *)*inbuf; + unsigned char *uc = (unsigned char *)*outbuf; + int len = 1; + + if ((c[0] & 0x80) == 0) { + uc[0] = c[0]; + uc[1] = 0; + } else if ((c[0] & 0xf0) == 0xe0) { + if (*inbytesleft < 3) { + DEBUG(0,("short utf8 char\n")); + goto badseq; + } + uc[1] = ((c[0]&0xF)<<4) | ((c[1]>>2)&0xF); + uc[0] = (c[1]<<6) | (c[2]&0x3f); + len = 3; + } else if ((c[0] & 0xe0) == 0xc0) { + if (*inbytesleft < 2) { + DEBUG(0,("short utf8 char\n")); + goto badseq; + } + uc[1] = (c[0]>>2) & 0x7; + uc[0] = (c[0]<<6) | (c[1]&0x3f); + len = 2; + } + + (*inbuf) += len; + (*inbytesleft) -= len; + (*outbytesleft) -= 2; + (*outbuf) += 2; + } + + if (*inbytesleft > 0) { + errno = E2BIG; + return -1; + } + + return 0; + +badseq: + errno = EINVAL; + return -1; +} + +static size_t utf8_push(void *cd, const char **inbuf, size_t *inbytesleft, + char **outbuf, size_t *outbytesleft) +{ + while (*inbytesleft >= 2 && *outbytesleft >= 1) { + unsigned char *c = (unsigned char *)*outbuf; + const unsigned char *uc = (const unsigned char *)*inbuf; + int len=1; + + if (uc[1] & 0xf8) { + if (*outbytesleft < 3) { + DEBUG(0,("short utf8 write\n")); + goto toobig; + } + c[0] = 0xe0 | (uc[1]>>4); + c[1] = 0x80 | ((uc[1]&0xF)<<2) | (uc[0]>>6); + c[2] = 0x80 | (uc[0]&0x3f); + len = 3; + } else if (uc[1] | (uc[0] & 0x80)) { + if (*outbytesleft < 2) { + DEBUG(0,("short utf8 write\n")); + goto toobig; + } + c[0] = 0xc0 | (uc[1]<<2) | (uc[0]>>6); + c[1] = 0x80 | (uc[0]&0x3f); + len = 2; + } else { + c[0] = uc[0]; + } + + + (*inbytesleft) -= 2; + (*outbytesleft) -= len; + (*inbuf) += 2; + (*outbuf) += len; + } + + if (*inbytesleft == 1) { + errno = EINVAL; + return -1; + } + + if (*inbytesleft > 1) { + errno = E2BIG; + return -1; + } + + return 0; + +toobig: + errno = E2BIG; + return -1; +} + diff --git a/source4/lib/interface.c b/source4/lib/interface.c new file mode 100644 index 0000000000..2540c898ff --- /dev/null +++ b/source4/lib/interface.c @@ -0,0 +1,333 @@ +/* + Unix SMB/CIFS implementation. + multiple interface handling + Copyright (C) Andrew Tridgell 1992-1998 + + 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" + +static struct iface_struct *probed_ifaces; +static int total_probed; + +static struct in_addr allones_ip; +struct in_addr loopback_ip; + +static struct interface *local_interfaces; + +#define ALLONES ((uint32)0xFFFFFFFF) +#define MKBCADDR(_IP, _NM) ((_IP & _NM) | (_NM ^ ALLONES)) +#define MKNETADDR(_IP, _NM) (_IP & _NM) + +/**************************************************************************** +Try and find an interface that matches an ip. If we cannot, return NULL + **************************************************************************/ +static struct interface *iface_find(struct in_addr ip, BOOL CheckMask) +{ + struct interface *i; + if (is_zero_ip(ip)) return local_interfaces; + + for (i=local_interfaces;i;i=i->next) + if (CheckMask) { + if (same_net(i->ip,ip,i->nmask)) return i; + } else if ((i->ip).s_addr == ip.s_addr) return i; + + return NULL; +} + + +/**************************************************************************** +add an interface to the linked list of interfaces +****************************************************************************/ +static void add_interface(struct in_addr ip, struct in_addr nmask) +{ + struct interface *iface; + if (iface_find(ip, False)) { + DEBUG(3,("not adding duplicate interface %s\n",inet_ntoa(ip))); + return; + } + + if (ip_equal(nmask, allones_ip)) { + DEBUG(3,("not adding non-broadcast interface %s\n",inet_ntoa(ip))); + return; + } + + iface = (struct interface *)malloc(sizeof(*iface)); + if (!iface) return; + + ZERO_STRUCTPN(iface); + + iface->ip = ip; + iface->nmask = nmask; + iface->bcast.s_addr = MKBCADDR(iface->ip.s_addr, iface->nmask.s_addr); + + DLIST_ADD(local_interfaces, iface); + + DEBUG(2,("added interface ip=%s ",inet_ntoa(iface->ip))); + DEBUG(2,("bcast=%s ",inet_ntoa(iface->bcast))); + DEBUG(2,("nmask=%s\n",inet_ntoa(iface->nmask))); +} + + + +/**************************************************************************** +interpret a single element from a interfaces= config line + +This handles the following different forms: + +1) wildcard interface name +2) DNS name +3) IP/masklen +4) ip/mask +5) bcast/mask +****************************************************************************/ +static void interpret_interface(TALLOC_CTX *mem_ctx, const char *token) +{ + struct in_addr ip, nmask; + char *p; + int i, added=0; + + zero_ip(&ip); + zero_ip(&nmask); + + /* first check if it is an interface name */ + for (i=0;i<total_probed;i++) { + if (gen_fnmatch(token, probed_ifaces[i].name) == 0) { + add_interface(probed_ifaces[i].ip, + probed_ifaces[i].netmask); + added = 1; + } + } + if (added) return; + + /* maybe it is a DNS name */ + p = strchr_m(token,'/'); + if (!p) { + ip = *interpret_addr2(mem_ctx, token); + for (i=0;i<total_probed;i++) { + if (ip.s_addr == probed_ifaces[i].ip.s_addr && + !ip_equal(allones_ip, probed_ifaces[i].netmask)) { + add_interface(probed_ifaces[i].ip, + probed_ifaces[i].netmask); + return; + } + } + DEBUG(2,("can't determine netmask for %s\n", token)); + return; + } + + /* parse it into an IP address/netmasklength pair */ + *p++ = 0; + + ip = *interpret_addr2(mem_ctx, token); + + if (strlen(p) > 2) { + nmask = *interpret_addr2(mem_ctx, p); + } else { + nmask.s_addr = htonl(((ALLONES >> atoi(p)) ^ ALLONES)); + } + + /* maybe the first component was a broadcast address */ + if (ip.s_addr == MKBCADDR(ip.s_addr, nmask.s_addr) || + ip.s_addr == MKNETADDR(ip.s_addr, nmask.s_addr)) { + for (i=0;i<total_probed;i++) { + if (same_net(ip, probed_ifaces[i].ip, nmask)) { + add_interface(probed_ifaces[i].ip, nmask); + return; + } + } + DEBUG(2,("Can't determine ip for broadcast address %s\n", token)); + return; + } + + add_interface(ip, nmask); +} + + +/**************************************************************************** +load the list of network interfaces +****************************************************************************/ +void load_interfaces(void) +{ + const char **ptr; + int i; + struct iface_struct ifaces[MAX_INTERFACES]; + TALLOC_CTX *mem_ctx; + + ptr = lp_interfaces(); + mem_ctx = talloc_init("load_interfaces"); + if (!mem_ctx) { + DEBUG(2,("no memory to load interfaces \n")); + return; + } + + allones_ip = *interpret_addr2(mem_ctx, "255.255.255.255"); + loopback_ip = *interpret_addr2(mem_ctx, "127.0.0.1"); + + SAFE_FREE(probed_ifaces); + + /* dump the current interfaces if any */ + while (local_interfaces) { + struct interface *iface = local_interfaces; + DLIST_REMOVE(local_interfaces, local_interfaces); + ZERO_STRUCTPN(iface); + SAFE_FREE(iface); + } + + /* probe the kernel for interfaces */ + total_probed = get_interfaces(ifaces, MAX_INTERFACES); + + if (total_probed > 0) { + probed_ifaces = memdup(ifaces, sizeof(ifaces[0])*total_probed); + } + + /* if we don't have a interfaces line then use all broadcast capable + interfaces except loopback */ + if (!ptr || !*ptr || !**ptr) { + if (total_probed <= 0) { + DEBUG(0,("ERROR: Could not determine network interfaces, you must use a interfaces config line\n")); + exit(1); + } + for (i=0;i<total_probed;i++) { + if (probed_ifaces[i].netmask.s_addr != allones_ip.s_addr && + probed_ifaces[i].ip.s_addr != loopback_ip.s_addr) { + add_interface(probed_ifaces[i].ip, + probed_ifaces[i].netmask); + } + } + goto exit; + } + + if (ptr) { + while (*ptr) { + interpret_interface(mem_ctx, *ptr); + ptr++; + } + } + + if (!local_interfaces) { + DEBUG(0,("WARNING: no network interfaces found\n")); + } + +exit: + talloc_destroy(mem_ctx); +} + + +/**************************************************************************** +return True if the list of probed interfaces has changed +****************************************************************************/ +BOOL interfaces_changed(void) +{ + int n; + struct iface_struct ifaces[MAX_INTERFACES]; + + n = get_interfaces(ifaces, MAX_INTERFACES); + + if ((n > 0 )&& (n != total_probed || + memcmp(ifaces, probed_ifaces, sizeof(ifaces[0])*n))) { + return True; + } + + return False; +} + + +/**************************************************************************** + check if an IP is one of mine + **************************************************************************/ +BOOL ismyip(struct in_addr ip) +{ + struct interface *i; + for (i=local_interfaces;i;i=i->next) + if (ip_equal(i->ip,ip)) return True; + return False; +} + +/**************************************************************************** + check if a packet is from a local (known) net + **************************************************************************/ +BOOL is_local_net(struct in_addr from) +{ + struct interface *i; + for (i=local_interfaces;i;i=i->next) { + if((from.s_addr & i->nmask.s_addr) == + (i->ip.s_addr & i->nmask.s_addr)) + return True; + } + return False; +} + +/**************************************************************************** + how many interfaces do we have + **************************************************************************/ +int iface_count(void) +{ + int ret = 0; + struct interface *i; + + for (i=local_interfaces;i;i=i->next) + ret++; + return ret; +} + +/**************************************************************************** + return IP of the Nth interface + **************************************************************************/ +struct in_addr *iface_n_ip(int n) +{ + struct interface *i; + + for (i=local_interfaces;i && n;i=i->next) + n--; + + if (i) return &i->ip; + return NULL; +} + +/**************************************************************************** + return bcast of the Nth interface + **************************************************************************/ +struct in_addr *iface_n_bcast(int n) +{ + struct interface *i; + + for (i=local_interfaces;i && n;i=i->next) + n--; + + if (i) return &i->bcast; + return NULL; +} + + +/* these 3 functions return the ip/bcast/nmask for the interface + most appropriate for the given ip address. If they can't find + an appropriate interface they return the requested field of the + first known interface. */ + +struct in_addr *iface_ip(struct in_addr ip) +{ + struct interface *i = iface_find(ip, True); + return(i ? &i->ip : &local_interfaces->ip); +} + +/* + return True if a IP is directly reachable on one of our interfaces +*/ +BOOL iface_local(struct in_addr ip) +{ + return iface_find(ip, True) ? True : False; +} diff --git a/source4/lib/interfaces.c b/source4/lib/interfaces.c new file mode 100644 index 0000000000..96f4b4cd94 --- /dev/null +++ b/source4/lib/interfaces.c @@ -0,0 +1,407 @@ +/* + Unix SMB/CIFS implementation. + return a list of network interfaces + Copyright (C) Andrew Tridgell 1998 + + 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. +*/ + + +/* working out the interfaces for a OS is an incredibly non-portable + thing. We have several possible implementations below, and autoconf + tries each of them to see what works + + Note that this file does _not_ include includes.h. That is so this code + can be called directly from the autoconf tests. That also means + this code cannot use any of the normal Samba debug stuff or defines. + This is standalone code. + +*/ + +#include <unistd.h> +#include <stdio.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> +#include <sys/ioctl.h> +#include <sys/time.h> +#include <net/if.h> + +#ifdef AUTOCONF_TEST +struct iface_struct { + char name[16]; + struct in_addr ip; + struct in_addr netmask; +}; +#else +#include "config.h" +#include "interfaces.h" +#endif + +#ifdef HAVE_SYS_TIME_H +#include <sys/time.h> +#endif + +#ifndef SIOCGIFCONF +#ifdef HAVE_SYS_SOCKIO_H +#include <sys/sockio.h> +#endif +#endif + +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif + +#ifdef HAVE_STRING_H +#include <string.h> +#endif + +#ifdef HAVE_STRINGS_H +#include <strings.h> +#endif + +#ifdef __COMPAR_FN_T +#define QSORT_CAST (__compar_fn_t) +#endif + +#ifndef QSORT_CAST +#define QSORT_CAST (int (*)(const void *, const void *)) +#endif + +#if HAVE_IFACE_IFCONF + +/* this works for Linux 2.2, Solaris 2.5, SunOS4, HPUX 10.20, OSF1 + V4.0, Ultrix 4.4, SCO Unix 3.2, IRIX 6.4 and FreeBSD 3.2. + + It probably also works on any BSD style system. */ + +/**************************************************************************** + get the netmask address for a local interface +****************************************************************************/ +static int _get_interfaces(struct iface_struct *ifaces, int max_interfaces) +{ + struct ifconf ifc; + char buff[8192]; + int fd, i, n; + struct ifreq *ifr=NULL; + int total = 0; + struct in_addr ipaddr; + struct in_addr nmask; + char *iname; + + if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { + return -1; + } + + ifc.ifc_len = sizeof(buff); + ifc.ifc_buf = buff; + + if (ioctl(fd, SIOCGIFCONF, &ifc) != 0) { + close(fd); + return -1; + } + + ifr = ifc.ifc_req; + + n = ifc.ifc_len / sizeof(struct ifreq); + + /* Loop through interfaces, looking for given IP address */ + for (i=n-1;i>=0 && total < max_interfaces;i--) { + if (ioctl(fd, SIOCGIFADDR, &ifr[i]) != 0) { + continue; + } + + iname = ifr[i].ifr_name; + ipaddr = (*(struct sockaddr_in *)&ifr[i].ifr_addr).sin_addr; + + if (ioctl(fd, SIOCGIFFLAGS, &ifr[i]) != 0) { + continue; + } + + if (!(ifr[i].ifr_flags & IFF_UP)) { + continue; + } + + if (ioctl(fd, SIOCGIFNETMASK, &ifr[i]) != 0) { + continue; + } + + nmask = ((struct sockaddr_in *)&ifr[i].ifr_addr)->sin_addr; + + strncpy(ifaces[total].name, iname, sizeof(ifaces[total].name)-1); + ifaces[total].name[sizeof(ifaces[total].name)-1] = 0; + ifaces[total].ip = ipaddr; + ifaces[total].netmask = nmask; + total++; + } + + close(fd); + + return total; +} + +#elif HAVE_IFACE_IFREQ + +#ifndef I_STR +#include <sys/stropts.h> +#endif + +/**************************************************************************** +this should cover most of the streams based systems +Thanks to Andrej.Borsenkow@mow.siemens.ru for several ideas in this code +****************************************************************************/ +static int _get_interfaces(struct iface_struct *ifaces, int max_interfaces) +{ + struct ifreq ifreq; + struct strioctl strioctl; + char buff[8192]; + int fd, i, n; + struct ifreq *ifr=NULL; + int total = 0; + struct in_addr ipaddr; + struct in_addr nmask; + char *iname; + + if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { + return -1; + } + + strioctl.ic_cmd = SIOCGIFCONF; + strioctl.ic_dp = buff; + strioctl.ic_len = sizeof(buff); + if (ioctl(fd, I_STR, &strioctl) < 0) { + close(fd); + return -1; + } + + /* we can ignore the possible sizeof(int) here as the resulting + number of interface structures won't change */ + n = strioctl.ic_len / sizeof(struct ifreq); + + /* we will assume that the kernel returns the length as an int + at the start of the buffer if the offered size is a + multiple of the structure size plus an int */ + if (n*sizeof(struct ifreq) + sizeof(int) == strioctl.ic_len) { + ifr = (struct ifreq *)(buff + sizeof(int)); + } else { + ifr = (struct ifreq *)buff; + } + + /* Loop through interfaces */ + + for (i = 0; i<n && total < max_interfaces; i++) { + ifreq = ifr[i]; + + strioctl.ic_cmd = SIOCGIFFLAGS; + strioctl.ic_dp = (char *)&ifreq; + strioctl.ic_len = sizeof(struct ifreq); + if (ioctl(fd, I_STR, &strioctl) != 0) { + continue; + } + + if (!(ifreq.ifr_flags & IFF_UP)) { + continue; + } + + strioctl.ic_cmd = SIOCGIFADDR; + strioctl.ic_dp = (char *)&ifreq; + strioctl.ic_len = sizeof(struct ifreq); + if (ioctl(fd, I_STR, &strioctl) != 0) { + continue; + } + + ipaddr = (*(struct sockaddr_in *) &ifreq.ifr_addr).sin_addr; + iname = ifreq.ifr_name; + + strioctl.ic_cmd = SIOCGIFNETMASK; + strioctl.ic_dp = (char *)&ifreq; + strioctl.ic_len = sizeof(struct ifreq); + if (ioctl(fd, I_STR, &strioctl) != 0) { + continue; + } + + nmask = ((struct sockaddr_in *)&ifreq.ifr_addr)->sin_addr; + + strncpy(ifaces[total].name, iname, sizeof(ifaces[total].name)-1); + ifaces[total].name[sizeof(ifaces[total].name)-1] = 0; + ifaces[total].ip = ipaddr; + ifaces[total].netmask = nmask; + + total++; + } + + close(fd); + + return total; +} + +#elif HAVE_IFACE_AIX + +/**************************************************************************** +this one is for AIX (tested on 4.2) +****************************************************************************/ +static int _get_interfaces(struct iface_struct *ifaces, int max_interfaces) +{ + char buff[8192]; + int fd, i; + struct ifconf ifc; + struct ifreq *ifr=NULL; + struct in_addr ipaddr; + struct in_addr nmask; + char *iname; + int total = 0; + + if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { + return -1; + } + + + ifc.ifc_len = sizeof(buff); + ifc.ifc_buf = buff; + + if (ioctl(fd, SIOCGIFCONF, &ifc) != 0) { + close(fd); + return -1; + } + + ifr = ifc.ifc_req; + + /* Loop through interfaces */ + i = ifc.ifc_len; + + while (i > 0 && total < max_interfaces) { + unsigned inc; + + inc = ifr->ifr_addr.sa_len; + + if (ioctl(fd, SIOCGIFADDR, ifr) != 0) { + goto next; + } + + ipaddr = (*(struct sockaddr_in *) &ifr->ifr_addr).sin_addr; + iname = ifr->ifr_name; + + if (ioctl(fd, SIOCGIFFLAGS, ifr) != 0) { + goto next; + } + + if (!(ifr->ifr_flags & IFF_UP)) { + goto next; + } + + if (ioctl(fd, SIOCGIFNETMASK, ifr) != 0) { + goto next; + } + + nmask = ((struct sockaddr_in *)&ifr->ifr_addr)->sin_addr; + + strncpy(ifaces[total].name, iname, sizeof(ifaces[total].name)-1); + ifaces[total].name[sizeof(ifaces[total].name)-1] = 0; + ifaces[total].ip = ipaddr; + ifaces[total].netmask = nmask; + + total++; + + next: + /* + * Patch from Archie Cobbs (archie@whistle.com). The + * addresses in the SIOCGIFCONF interface list have a + * minimum size. Usually this doesn't matter, but if + * your machine has tunnel interfaces, etc. that have + * a zero length "link address", this does matter. */ + + if (inc < sizeof(ifr->ifr_addr)) + inc = sizeof(ifr->ifr_addr); + inc += IFNAMSIZ; + + ifr = (struct ifreq*) (((char*) ifr) + inc); + i -= inc; + } + + + close(fd); + return total; +} + +#else /* a dummy version */ +static int _get_interfaces(struct iface_struct *ifaces, int max_interfaces) +{ + return -1; +} +#endif + + +static int iface_comp(struct iface_struct *i1, struct iface_struct *i2) +{ + int r; + r = strcmp(i1->name, i2->name); + if (r) return r; + r = ntohl(i1->ip.s_addr) - ntohl(i2->ip.s_addr); + if (r) return r; + r = ntohl(i1->netmask.s_addr) - ntohl(i2->netmask.s_addr); + return r; +} + +/* this wrapper is used to remove duplicates from the interface list generated + above */ +int get_interfaces(struct iface_struct *ifaces, int max_interfaces) +{ + int total, i, j; + + total = _get_interfaces(ifaces, max_interfaces); + if (total <= 0) return total; + + /* now we need to remove duplicates */ + qsort(ifaces, total, sizeof(ifaces[0]), QSORT_CAST iface_comp); + + for (i=1;i<total;) { + if (iface_comp(&ifaces[i-1], &ifaces[i]) == 0) { + for (j=i-1;j<total-1;j++) { + ifaces[j] = ifaces[j+1]; + } + total--; + } else { + i++; + } + } + + return total; +} + + +#ifdef AUTOCONF_TEST +/* this is the autoconf driver to test get_interfaces() */ + +#define MAX_INTERFACES 128 + + int main() +{ + struct iface_struct ifaces[MAX_INTERFACES]; + int total = get_interfaces(ifaces, MAX_INTERFACES); + int i; + + printf("got %d interfaces:\n", total); + if (total <= 0) exit(1); + + for (i=0;i<total;i++) { + printf("%-10s ", ifaces[i].name); + printf("IP=%s ", inet_ntoa(ifaces[i].ip)); + printf("NETMASK=%s\n", inet_ntoa(ifaces[i].netmask)); + } + return 0; +} +#endif diff --git a/source4/lib/ldap_escape.c b/source4/lib/ldap_escape.c new file mode 100644 index 0000000000..9e88b4999c --- /dev/null +++ b/source4/lib/ldap_escape.c @@ -0,0 +1,90 @@ +/* + Unix SMB/CIFS implementation. + ldap filter argument escaping + + Copyright (C) 1998, 1999, 2000 Luke Howard <lukeh@padl.com>, + Copyright (C) 2003 Andrew Bartlett <abartlet@samba.org> + + + 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" + +/** + * Escape a parameter to an LDAP filter string, so they cannot contain + * embeded ( ) * or \ chars which may cause it not to parse correctly. + * + * @param s The input string + * + * @return A string allocated with malloc(), containing the escaped string, + * and to be free()ed by the caller. + **/ + +char *escape_ldap_string_alloc(const char *s) +{ + size_t len = strlen(s)+1; + char *output = malloc(len); + char *output_tmp; + const char *sub; + int i = 0; + char *p = output; + + while (*s) + { + switch (*s) + { + case '*': + sub = "\\2a"; + break; + case '(': + sub = "\\28"; + break; + case ')': + sub = "\\29"; + break; + case '\\': + sub = "\\5c"; + break; + default: + sub = NULL; + break; + } + + if (sub) { + len = len + 3; + output_tmp = realloc(output, len); + if (!output_tmp) { + SAFE_FREE(output); + return NULL; + } + output = output_tmp; + + p = &output[i]; + strncpy (p, sub, 3); + p += 3; + i += 3; + + } else { + *p = *s; + p++; + i++; + } + s++; + } + + *p = '\0'; + return output; +} diff --git a/source4/lib/md4.c b/source4/lib/md4.c new file mode 100644 index 0000000000..417e87bd8e --- /dev/null +++ b/source4/lib/md4.c @@ -0,0 +1,175 @@ +/* + Unix SMB/CIFS implementation. + a implementation of MD4 designed for use in the SMB authentication protocol + Copyright (C) Andrew Tridgell 1997-1998. + + 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" + +/* NOTE: This code makes no attempt to be fast! + + It assumes that a int is at least 32 bits long +*/ + +struct mdfour_state { + uint32 A, B, C, D; +}; + +static uint32 F(uint32 X, uint32 Y, uint32 Z) +{ + return (X&Y) | ((~X)&Z); +} + +static uint32 G(uint32 X, uint32 Y, uint32 Z) +{ + return (X&Y) | (X&Z) | (Y&Z); +} + +static uint32 H(uint32 X, uint32 Y, uint32 Z) +{ + return X^Y^Z; +} + +static uint32 lshift(uint32 x, int s) +{ + x &= 0xFFFFFFFF; + return ((x<<s)&0xFFFFFFFF) | (x>>(32-s)); +} + +#define ROUND1(a,b,c,d,k,s) a = lshift(a + F(b,c,d) + X[k], s) +#define ROUND2(a,b,c,d,k,s) a = lshift(a + G(b,c,d) + X[k] + (uint32)0x5A827999,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(struct mdfour_state *s, uint32 *M) +{ + int j; + uint32 AA, BB, CC, DD; + uint32 X[16]; + + for (j=0;j<16;j++) + X[j] = M[j]; + + AA = s->A; BB = s->B; CC = s->C; DD = s->D; + + ROUND1(s->A,s->B,s->C,s->D, 0, 3); ROUND1(s->D,s->A,s->B,s->C, 1, 7); + ROUND1(s->C,s->D,s->A,s->B, 2, 11); ROUND1(s->B,s->C,s->D,s->A, 3, 19); + ROUND1(s->A,s->B,s->C,s->D, 4, 3); ROUND1(s->D,s->A,s->B,s->C, 5, 7); + ROUND1(s->C,s->D,s->A,s->B, 6, 11); ROUND1(s->B,s->C,s->D,s->A, 7, 19); + ROUND1(s->A,s->B,s->C,s->D, 8, 3); ROUND1(s->D,s->A,s->B,s->C, 9, 7); + ROUND1(s->C,s->D,s->A,s->B, 10, 11); ROUND1(s->B,s->C,s->D,s->A, 11, 19); + ROUND1(s->A,s->B,s->C,s->D, 12, 3); ROUND1(s->D,s->A,s->B,s->C, 13, 7); + ROUND1(s->C,s->D,s->A,s->B, 14, 11); ROUND1(s->B,s->C,s->D,s->A, 15, 19); + + ROUND2(s->A,s->B,s->C,s->D, 0, 3); ROUND2(s->D,s->A,s->B,s->C, 4, 5); + ROUND2(s->C,s->D,s->A,s->B, 8, 9); ROUND2(s->B,s->C,s->D,s->A, 12, 13); + ROUND2(s->A,s->B,s->C,s->D, 1, 3); ROUND2(s->D,s->A,s->B,s->C, 5, 5); + ROUND2(s->C,s->D,s->A,s->B, 9, 9); ROUND2(s->B,s->C,s->D,s->A, 13, 13); + ROUND2(s->A,s->B,s->C,s->D, 2, 3); ROUND2(s->D,s->A,s->B,s->C, 6, 5); + ROUND2(s->C,s->D,s->A,s->B, 10, 9); ROUND2(s->B,s->C,s->D,s->A, 14, 13); + ROUND2(s->A,s->B,s->C,s->D, 3, 3); ROUND2(s->D,s->A,s->B,s->C, 7, 5); + ROUND2(s->C,s->D,s->A,s->B, 11, 9); ROUND2(s->B,s->C,s->D,s->A, 15, 13); + + ROUND3(s->A,s->B,s->C,s->D, 0, 3); ROUND3(s->D,s->A,s->B,s->C, 8, 9); + ROUND3(s->C,s->D,s->A,s->B, 4, 11); ROUND3(s->B,s->C,s->D,s->A, 12, 15); + ROUND3(s->A,s->B,s->C,s->D, 2, 3); ROUND3(s->D,s->A,s->B,s->C, 10, 9); + ROUND3(s->C,s->D,s->A,s->B, 6, 11); ROUND3(s->B,s->C,s->D,s->A, 14, 15); + ROUND3(s->A,s->B,s->C,s->D, 1, 3); ROUND3(s->D,s->A,s->B,s->C, 9, 9); + ROUND3(s->C,s->D,s->A,s->B, 5, 11); ROUND3(s->B,s->C,s->D,s->A, 13, 15); + ROUND3(s->A,s->B,s->C,s->D, 3, 3); ROUND3(s->D,s->A,s->B,s->C, 11, 9); + ROUND3(s->C,s->D,s->A,s->B, 7, 11); ROUND3(s->B,s->C,s->D,s->A, 15, 15); + + s->A += AA; + s->B += BB; + s->C += CC; + s->D += DD; + + s->A &= 0xFFFFFFFF; + s->B &= 0xFFFFFFFF; + s->C &= 0xFFFFFFFF; + s->D &= 0xFFFFFFFF; + + for (j=0;j<16;j++) + X[j] = 0; +} + +static void copy64(uint32 *M, const unsigned char *in) +{ + int i; + + for (i=0;i<16;i++) + M[i] = (in[i*4+3]<<24) | (in[i*4+2]<<16) | + (in[i*4+1]<<8) | (in[i*4+0]<<0); +} + +static void copy4(unsigned char *out, uint32 x) +{ + out[0] = x&0xFF; + out[1] = (x>>8)&0xFF; + out[2] = (x>>16)&0xFF; + out[3] = (x>>24)&0xFF; +} + +/* produce a md4 message digest from data of length n bytes */ +void mdfour(unsigned char *out, const unsigned char *in, int n) +{ + unsigned char buf[128]; + uint32 M[16]; + uint32 b = n * 8; + int i; + struct mdfour_state state; + + state.A = 0x67452301; + state.B = 0xefcdab89; + state.C = 0x98badcfe; + state.D = 0x10325476; + + while (n > 64) { + copy64(M, in); + mdfour64(&state, M); + in += 64; + n -= 64; + } + + for (i=0;i<128;i++) + buf[i] = 0; + memcpy(buf, in, n); + buf[n] = 0x80; + + if (n <= 55) { + copy4(buf+56, b); + copy64(M, buf); + mdfour64(&state, M); + } else { + copy4(buf+120, b); + copy64(M, buf); + mdfour64(&state, M); + copy64(M, buf+64); + mdfour64(&state, M); + } + + for (i=0;i<128;i++) + buf[i] = 0; + copy64(M, buf); + + copy4(out, state.A); + copy4(out+4, state.B); + copy4(out+8, state.C); + copy4(out+12, state.D); +} + + diff --git a/source4/lib/md5.c b/source4/lib/md5.c new file mode 100644 index 0000000000..2121b17047 --- /dev/null +++ b/source4/lib/md5.c @@ -0,0 +1,247 @@ +/* + * This code implements the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + */ + +/* This code slightly modified to fit into Samba by + abartlet@samba.org Jun 2001 */ + +#include "includes.h" + +#include "md5.h" + +static void MD5Transform(uint32 buf[4], uint32 const in[16]); + +/* + * Note: this code is harmless on little-endian machines. + */ +static void byteReverse(unsigned char *buf, unsigned longs) +{ + uint32 t; + do { + t = (uint32) ((unsigned) buf[3] << 8 | buf[2]) << 16 | + ((unsigned) buf[1] << 8 | buf[0]); + *(uint32 *) buf = t; + buf += 4; + } while (--longs); +} + +/* + * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious + * initialization constants. + */ +void MD5Init(struct MD5Context *ctx) +{ + ctx->buf[0] = 0x67452301; + ctx->buf[1] = 0xefcdab89; + ctx->buf[2] = 0x98badcfe; + ctx->buf[3] = 0x10325476; + + ctx->bits[0] = 0; + ctx->bits[1] = 0; +} + +/* + * Update context to reflect the concatenation of another buffer full + * of bytes. + */ +void MD5Update(struct MD5Context *ctx, unsigned char const *buf, unsigned len) +{ + register uint32 t; + + /* Update bitcount */ + + t = ctx->bits[0]; + if ((ctx->bits[0] = t + ((uint32) len << 3)) < t) + ctx->bits[1]++; /* Carry from low to high */ + ctx->bits[1] += len >> 29; + + t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ + + /* Handle any leading odd-sized chunks */ + + if (t) { + unsigned char *p = (unsigned char *) ctx->in + t; + + t = 64 - t; + if (len < t) { + memmove(p, buf, len); + return; + } + memmove(p, buf, t); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (uint32 *) ctx->in); + buf += t; + len -= t; + } + /* Process data in 64-byte chunks */ + + while (len >= 64) { + memmove(ctx->in, buf, 64); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (uint32 *) ctx->in); + buf += 64; + len -= 64; + } + + /* Handle any remaining bytes of data. */ + + memmove(ctx->in, buf, len); +} + +/* + * Final wrapup - pad to 64-byte boundary with the bit pattern + * 1 0* (64-bit count of bits processed, MSB-first) + */ +void MD5Final(unsigned char digest[16], struct MD5Context *ctx) +{ + unsigned int count; + unsigned char *p; + + /* Compute number of bytes mod 64 */ + count = (ctx->bits[0] >> 3) & 0x3F; + + /* Set the first char of padding to 0x80. This is safe since there is + always at least one byte free */ + p = ctx->in + count; + *p++ = 0x80; + + /* Bytes of padding needed to make 64 bytes */ + count = 64 - 1 - count; + + /* Pad out to 56 mod 64 */ + if (count < 8) { + /* Two lots of padding: Pad the first block to 64 bytes */ + memset(p, 0, count); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (uint32 *) ctx->in); + + /* Now fill the next block with 56 bytes */ + memset(ctx->in, 0, 56); + } else { + /* Pad block to 56 bytes */ + memset(p, 0, count - 8); + } + byteReverse(ctx->in, 14); + + /* Append length in bits and transform */ + ((uint32 *) ctx->in)[14] = ctx->bits[0]; + ((uint32 *) ctx->in)[15] = ctx->bits[1]; + + MD5Transform(ctx->buf, (uint32 *) ctx->in); + byteReverse((unsigned char *) ctx->buf, 4); + memmove(digest, ctx->buf, 16); + memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */ +} + +/* The four core functions - F1 is optimized somewhat */ + +/* #define F1(x, y, z) (x & y | ~x & z) */ +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +/* This is the central step in the MD5 algorithm. */ +#define MD5STEP(f, w, x, y, z, data, s) \ + ( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x ) + +/* + * The core of the MD5 algorithm, this alters an existing MD5 hash to + * reflect the addition of 16 longwords of new data. MD5Update blocks + * the data and converts bytes into longwords for this routine. + */ +static void MD5Transform(uint32 buf[4], uint32 const in[16]) +{ + register uint32 a, b, c, d; + + a = buf[0]; + b = buf[1]; + c = buf[2]; + d = buf[3]; + + MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); + MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); + MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); + MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); + MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); + MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); + MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); + MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); + MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); + MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); + MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); + MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); + MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); + MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); + MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); + MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); + + MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); + MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); + MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); + MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); + MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); + MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); + MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); + MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); + MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); + MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); + MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); + MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); + MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); + MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); + MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); + MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); + + MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); + MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); + MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); + MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); + MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); + MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); + MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); + MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); + MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); + MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); + MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); + MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); + MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); + MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); + MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); + MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); + + MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); + MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); + MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); + MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); + MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); + MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); + MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); + MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); + MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); + MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); + MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); + MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); + MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); + MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); + MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); + MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} diff --git a/source4/lib/messages.c b/source4/lib/messages.c new file mode 100644 index 0000000000..cb26b356bd --- /dev/null +++ b/source4/lib/messages.c @@ -0,0 +1,566 @@ +/* + Unix SMB/CIFS implementation. + Samba internal messaging functions + Copyright (C) Andrew Tridgell 2000 + Copyright (C) 2001 by Martin Pool + Copyright (C) 2002 by Jeremy Allison + + 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. +*/ + +/** + @defgroup messages Internal messaging framework + @{ + @file messages.c + + @brief Module for internal messaging between Samba daemons. + + The idea is that if a part of Samba wants to do communication with + another Samba process then it will do a message_register() of a + dispatch function, and use message_send_pid() to send messages to + that process. + + The dispatch function is given the pid of the sender, and it can + use that to reply by message_send_pid(). See ping_message() for a + simple example. + + @caution Dispatch functions must be able to cope with incoming + messages on an *odd* byte boundary. + + This system doesn't have any inherent size limitations but is not + very efficient for large messages or when messages are sent in very + quick succession. + +*/ + +#include "includes.h" + +/* the locking database handle */ +static TDB_CONTEXT *tdb; +static int received_signal; + +/* change the message version with any incompatible changes in the protocol */ +#define MESSAGE_VERSION 1 + +struct message_rec { + int msg_version; + int msg_type; + pid_t dest; + pid_t src; + size_t len; +}; + +/* we have a linked list of dispatch handlers */ +static struct dispatch_fns { + struct dispatch_fns *next, *prev; + int msg_type; + void (*fn)(int msg_type, pid_t pid, void *buf, size_t len); +} *dispatch_fns; + +/**************************************************************************** + Notifications come in as signals. +****************************************************************************/ + +static void sig_usr1(void) +{ + received_signal = 1; + sys_select_signal(); +} + +/**************************************************************************** + A useful function for testing the message system. +****************************************************************************/ + +static void ping_message(int msg_type, pid_t src, void *buf, size_t len) +{ + const char *msg = buf ? buf : "none"; + DEBUG(1,("INFO: Received PING message from PID %u [%s]\n",(unsigned int)src, msg)); + message_send_pid(src, MSG_PONG, buf, len, True); +} + +/**************************************************************************** + Initialise the messaging functions. +****************************************************************************/ + +BOOL message_init(void) +{ + TALLOC_CTX *mem_ctx; + + if (tdb) return True; + + mem_ctx = talloc_init("message_init"); + if (!mem_ctx) { + DEBUG(0,("ERROR: No memory to initialise messages database\n")); + return False; + } + tdb = tdb_open_log(lock_path(mem_ctx, "messages.tdb"), + 0, TDB_CLEAR_IF_FIRST|TDB_DEFAULT, + O_RDWR|O_CREAT,0600); + talloc_destroy(mem_ctx); + + if (!tdb) { + DEBUG(0,("ERROR: Failed to initialise messages database\n")); + return False; + } + + CatchSignal(SIGUSR1, SIGNAL_CAST sig_usr1); + + message_register(MSG_PING, ping_message); + + return True; +} + +/******************************************************************* + Form a static tdb key from a pid. +******************************************************************/ + +static TDB_DATA message_key_pid(pid_t pid) +{ + static char key[20]; + TDB_DATA kbuf; + + slprintf(key, sizeof(key)-1, "PID/%d", (int)pid); + + kbuf.dptr = (char *)key; + kbuf.dsize = strlen(key)+1; + return kbuf; +} + +/**************************************************************************** + Notify a process that it has a message. If the process doesn't exist + then delete its record in the database. +****************************************************************************/ + +static BOOL message_notify(pid_t pid) +{ + /* + * Doing kill with a non-positive pid causes messages to be + * sent to places we don't want. + */ + + SMB_ASSERT(pid > 0); + + if (kill(pid, SIGUSR1) == -1) { + if (errno == ESRCH) { + DEBUG(2,("pid %d doesn't exist - deleting messages record\n", (int)pid)); + tdb_delete(tdb, message_key_pid(pid)); + } else { + DEBUG(2,("message to process %d failed - %s\n", (int)pid, strerror(errno))); + } + return False; + } + return True; +} + +/**************************************************************************** + Send a message to a particular pid. +****************************************************************************/ + +static BOOL message_send_pid_internal(pid_t pid, int msg_type, const void *buf, size_t len, + BOOL duplicates_allowed, unsigned int timeout) +{ + TDB_DATA kbuf; + TDB_DATA dbuf; + TDB_DATA old_dbuf; + struct message_rec rec; + char *ptr; + struct message_rec prec; + + /* + * Doing kill with a non-positive pid causes messages to be + * sent to places we don't want. + */ + + SMB_ASSERT(pid > 0); + + rec.msg_version = MESSAGE_VERSION; + rec.msg_type = msg_type; + rec.dest = pid; + rec.src = getpid(); + rec.len = len; + + kbuf = message_key_pid(pid); + + dbuf.dptr = (void *)malloc(len + sizeof(rec)); + if (!dbuf.dptr) + return False; + + memcpy(dbuf.dptr, &rec, sizeof(rec)); + if (len > 0) + memcpy((void *)((char*)dbuf.dptr+sizeof(rec)), buf, len); + + dbuf.dsize = len + sizeof(rec); + + if (duplicates_allowed) { + + /* If duplicates are allowed we can just append the message and return. */ + + /* lock the record for the destination */ + if (timeout) { + if (tdb_chainlock_with_timeout(tdb, kbuf, timeout) == -1) { + DEBUG(0,("message_send_pid_internal: failed to get chainlock with timeout %ul.\n", timeout)); + return False; + } + } else { + if (tdb_chainlock(tdb, kbuf) == -1) { + DEBUG(0,("message_send_pid_internal: failed to get chainlock.\n")); + return False; + } + } + tdb_append(tdb, kbuf, dbuf); + tdb_chainunlock(tdb, kbuf); + + SAFE_FREE(dbuf.dptr); + errno = 0; /* paranoia */ + return message_notify(pid); + } + + /* lock the record for the destination */ + if (timeout) { + if (tdb_chainlock_with_timeout(tdb, kbuf, timeout) == -1) { + DEBUG(0,("message_send_pid_internal: failed to get chainlock with timeout %ul.\n", timeout)); + return False; + } + } else { + if (tdb_chainlock(tdb, kbuf) == -1) { + DEBUG(0,("message_send_pid_internal: failed to get chainlock.\n")); + return False; + } + } + + old_dbuf = tdb_fetch(tdb, kbuf); + + if (!old_dbuf.dptr) { + /* its a new record */ + + tdb_store(tdb, kbuf, dbuf, TDB_REPLACE); + tdb_chainunlock(tdb, kbuf); + + SAFE_FREE(dbuf.dptr); + errno = 0; /* paranoia */ + return message_notify(pid); + } + + /* Not a new record. Check for duplicates. */ + + for(ptr = (char *)old_dbuf.dptr; ptr < old_dbuf.dptr + old_dbuf.dsize; ) { + /* + * First check if the message header matches, then, if it's a non-zero + * sized message, check if the data matches. If so it's a duplicate and + * we can discard it. JRA. + */ + + if (!memcmp(ptr, &rec, sizeof(rec))) { + if (!len || (len && !memcmp( ptr + sizeof(rec), buf, len))) { + tdb_chainunlock(tdb, kbuf); + DEBUG(10,("message_send_pid_internal: discarding duplicate message.\n")); + SAFE_FREE(dbuf.dptr); + SAFE_FREE(old_dbuf.dptr); + return True; + } + } + memcpy(&prec, ptr, sizeof(prec)); + ptr += sizeof(rec) + prec.len; + } + + /* we're adding to an existing entry */ + + tdb_append(tdb, kbuf, dbuf); + tdb_chainunlock(tdb, kbuf); + + SAFE_FREE(old_dbuf.dptr); + SAFE_FREE(dbuf.dptr); + + errno = 0; /* paranoia */ + return message_notify(pid); +} + +/**************************************************************************** + Send a message to a particular pid - no timeout. +****************************************************************************/ + +BOOL message_send_pid(pid_t pid, int msg_type, const void *buf, size_t len, BOOL duplicates_allowed) +{ + return message_send_pid_internal(pid, msg_type, buf, len, duplicates_allowed, 0); +} + +/**************************************************************************** + Send a message to a particular pid, with timeout in seconds. +****************************************************************************/ + +BOOL message_send_pid_with_timeout(pid_t pid, int msg_type, const void *buf, size_t len, + BOOL duplicates_allowed, unsigned int timeout) +{ + return message_send_pid_internal(pid, msg_type, buf, len, duplicates_allowed, timeout); +} + +/**************************************************************************** + Retrieve all messages for the current process. +****************************************************************************/ + +static BOOL retrieve_all_messages(char **msgs_buf, size_t *total_len) +{ + TDB_DATA kbuf; + TDB_DATA dbuf; + TDB_DATA null_dbuf; + + ZERO_STRUCT(null_dbuf); + + *msgs_buf = NULL; + *total_len = 0; + + kbuf = message_key_pid(getpid()); + + tdb_chainlock(tdb, kbuf); + dbuf = tdb_fetch(tdb, kbuf); + /* + * Replace with an empty record to keep the allocated + * space in the tdb. + */ + tdb_store(tdb, kbuf, null_dbuf, TDB_REPLACE); + tdb_chainunlock(tdb, kbuf); + + if (dbuf.dptr == NULL || dbuf.dsize == 0) { + SAFE_FREE(dbuf.dptr); + return False; + } + + *msgs_buf = dbuf.dptr; + *total_len = dbuf.dsize; + + return True; +} + +/**************************************************************************** + Parse out the next message for the current process. +****************************************************************************/ + +static BOOL message_recv(char *msgs_buf, size_t total_len, int *msg_type, pid_t *src, char **buf, size_t *len) +{ + struct message_rec rec; + char *ret_buf = *buf; + + *buf = NULL; + *len = 0; + + if (total_len - (ret_buf - msgs_buf) < sizeof(rec)) + return False; + + memcpy(&rec, ret_buf, sizeof(rec)); + ret_buf += sizeof(rec); + + if (rec.msg_version != MESSAGE_VERSION) { + DEBUG(0,("message version %d received (expected %d)\n", rec.msg_version, MESSAGE_VERSION)); + return False; + } + + if (rec.len > 0) { + if (total_len - (ret_buf - msgs_buf) < rec.len) + return False; + } + + *len = rec.len; + *msg_type = rec.msg_type; + *src = rec.src; + *buf = ret_buf; + + return True; +} + +/**************************************************************************** + Receive and dispatch any messages pending for this process. + Notice that all dispatch handlers for a particular msg_type get called, + so you can register multiple handlers for a message. + *NOTE*: Dispatch functions must be able to cope with incoming + messages on an *odd* byte boundary. +****************************************************************************/ + +void message_dispatch(void) +{ + int msg_type; + pid_t src; + char *buf; + char *msgs_buf; + size_t len, total_len; + struct dispatch_fns *dfn; + int n_handled; + + if (!received_signal) + return; + + DEBUG(10,("message_dispatch: received_signal = %d\n", received_signal)); + + received_signal = 0; + + if (!retrieve_all_messages(&msgs_buf, &total_len)) + return; + + for (buf = msgs_buf; message_recv(msgs_buf, total_len, &msg_type, &src, &buf, &len); buf += len) { + DEBUG(10,("message_dispatch: received msg_type=%d src_pid=%u\n", + msg_type, (unsigned int) src)); + n_handled = 0; + for (dfn = dispatch_fns; dfn; dfn = dfn->next) { + if (dfn->msg_type == msg_type) { + DEBUG(10,("message_dispatch: processing message of type %d.\n", msg_type)); + dfn->fn(msg_type, src, len ? (void *)buf : NULL, len); + n_handled++; + } + } + if (!n_handled) { + DEBUG(5,("message_dispatch: warning: no handlers registed for " + "msg_type %d in pid %u\n", + msg_type, (unsigned int)getpid())); + } + } + SAFE_FREE(msgs_buf); +} + +/**************************************************************************** + Register a dispatch function for a particular message type. + *NOTE*: Dispatch functions must be able to cope with incoming + messages on an *odd* byte boundary. +****************************************************************************/ + +void message_register(int msg_type, + void (*fn)(int msg_type, pid_t pid, void *buf, size_t len)) +{ + struct dispatch_fns *dfn; + + dfn = (struct dispatch_fns *)malloc(sizeof(*dfn)); + + if (dfn != NULL) { + + ZERO_STRUCTPN(dfn); + + dfn->msg_type = msg_type; + dfn->fn = fn; + + DLIST_ADD(dispatch_fns, dfn); + } + else { + + DEBUG(0,("message_register: Not enough memory. malloc failed!\n")); + } +} + +/**************************************************************************** + De-register the function for a particular message type. +****************************************************************************/ + +void message_deregister(int msg_type) +{ + struct dispatch_fns *dfn, *next; + + for (dfn = dispatch_fns; dfn; dfn = next) { + next = dfn->next; + if (dfn->msg_type == msg_type) { + DLIST_REMOVE(dispatch_fns, dfn); + SAFE_FREE(dfn); + } + } +} + +struct msg_all { + int msg_type; + uint32 msg_flag; + const void *buf; + size_t len; + BOOL duplicates; + int n_sent; +}; + +/**************************************************************************** + Send one of the messages for the broadcast. +****************************************************************************/ + +static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state) +{ + struct connections_data crec; + struct msg_all *msg_all = (struct msg_all *)state; + + if (dbuf.dsize != sizeof(crec)) + return 0; + + memcpy(&crec, dbuf.dptr, sizeof(crec)); + + if (crec.cnum != -1) + return 0; + + /* Don't send if the receiver hasn't registered an interest. */ + + if(!(crec.bcast_msg_flags & msg_all->msg_flag)) + return 0; + + /* If the msg send fails because the pid was not found (i.e. smbd died), + * the msg has already been deleted from the messages.tdb.*/ + + if (!message_send_pid(crec.pid, msg_all->msg_type, + msg_all->buf, msg_all->len, + msg_all->duplicates)) { + + /* If the pid was not found delete the entry from connections.tdb */ + + if (errno == ESRCH) { + DEBUG(2,("pid %u doesn't exist - deleting connections %d [%s]\n", + (unsigned int)crec.pid, crec.cnum, crec.name)); + tdb_delete(the_tdb, kbuf); + } + } + msg_all->n_sent++; + return 0; +} + +/** + * Send a message to all smbd processes. + * + * It isn't very efficient, but should be OK for the sorts of + * applications that use it. When we need efficient broadcast we can add + * it. + * + * @param n_sent Set to the number of messages sent. This should be + * equal to the number of processes, but be careful for races. + * + * @retval True for success. + **/ +BOOL message_send_all(TDB_CONTEXT *conn_tdb, int msg_type, + const void *buf, size_t len, + BOOL duplicates_allowed, + int *n_sent) +{ + struct msg_all msg_all; + + msg_all.msg_type = msg_type; + if (msg_type < 1000) + msg_all.msg_flag = FLAG_MSG_GENERAL; + else if (msg_type > 1000 && msg_type < 2000) + msg_all.msg_flag = FLAG_MSG_NMBD; + else if (msg_type > 2000 && msg_type < 3000) + msg_all.msg_flag = FLAG_MSG_PRINTING; + else if (msg_type > 3000 && msg_type < 4000) + msg_all.msg_flag = FLAG_MSG_SMBD; + else + return False; + + msg_all.buf = buf; + msg_all.len = len; + msg_all.duplicates = duplicates_allowed; + msg_all.n_sent = 0; + + tdb_traverse(conn_tdb, traverse_fn, &msg_all); + if (n_sent) + *n_sent = msg_all.n_sent; + return True; +} +/** @} **/ diff --git a/source4/lib/module.c b/source4/lib/module.c new file mode 100644 index 0000000000..152e893100 --- /dev/null +++ b/source4/lib/module.c @@ -0,0 +1,128 @@ +/* + Unix SMB/CIFS implementation. + module loading system + + Copyright (C) Jelmer Vernooij 2002 + + 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" + +#ifdef HAVE_DLOPEN +int smb_load_module(const char *module_name) +{ + void *handle; + init_module_function *init; + int status; + const char *error; + + /* Always try to use LAZY symbol resolving; if the plugin has + * backwards compatibility, there might be symbols in the + * plugin referencing to old (removed) functions + */ + handle = sys_dlopen(module_name, RTLD_LAZY); + + if(!handle) { + DEBUG(0, ("Error loading module '%s': %s\n", module_name, sys_dlerror())); + return False; + } + + init = sys_dlsym(handle, "init_module"); + + /* we must check sys_dlerror() to determine if it worked, because + sys_dlsym() can validly return NULL */ + error = sys_dlerror(); + if (error) { + DEBUG(0, ("Error trying to resolve symbol 'init_module' in %s: %s\n", module_name, error)); + return False; + } + + status = init(); + + DEBUG(2, ("Module '%s' loaded\n", module_name)); + + return status; +} + +/* Load all modules in list and return number of + * modules that has been successfully loaded */ +int smb_load_modules(const char **modules) +{ + int i; + int success = 0; + + for(i = 0; modules[i]; i++){ + if(smb_load_module(modules[i])) { + success++; + } + } + + DEBUG(2, ("%d modules successfully loaded\n", success)); + + return success; +} + +int smb_probe_module(const char *subsystem, const char *module) +{ + char *full_path; + int rc; + TALLOC_CTX *mem_ctx; + + /* Check for absolute path */ + if(module[0] == '/')return smb_load_module(module); + + mem_ctx = talloc_init("smb_probe_module"); + if (!mem_ctx) { + DEBUG(0,("No memory for loading modules\n")); + return False; + } + full_path = talloc_strdup(mem_ctx, lib_path(mem_ctx, subsystem)); + full_path = talloc_asprintf(mem_ctx, "%s/%s.%s", + full_path, module, shlib_ext()); + + rc = smb_load_module(full_path); + talloc_destroy(mem_ctx); + return rc; +} + +#else /* HAVE_DLOPEN */ + +int smb_load_module(const char *module_name) +{ + DEBUG(0,("This samba executable has not been built with plugin support")); + return False; +} + +int smb_load_modules(const char **modules) +{ + DEBUG(0,("This samba executable has not been built with plugin support")); + return False; +} + +int smb_probe_module(const char *subsystem, const char *module) +{ + DEBUG(0,("This samba executable has not been built with plugin support, not probing")); + return False; +} + +#endif /* HAVE_DLOPEN */ + +void init_modules(void) +{ + if(lp_preload_modules()) + smb_load_modules(lp_preload_modules()); + /* FIXME: load static modules */ +} diff --git a/source4/lib/ms_fnmatch.c b/source4/lib/ms_fnmatch.c new file mode 100644 index 0000000000..dd015a0ac8 --- /dev/null +++ b/source4/lib/ms_fnmatch.c @@ -0,0 +1,226 @@ +/* + Unix SMB/CIFS implementation. + filename matching routine + Copyright (C) Andrew Tridgell 1992-1998 + + 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. */ + +/* + This module was originally based on fnmatch.c copyright by the Free + Software Foundation. It bears little resemblence to that code now +*/ + + +#if FNMATCH_TEST +#include <stdio.h> +#include <stdlib.h> +#else +#include "includes.h" +#endif + +/* + bugger. we need a separate wildcard routine for older versions + of the protocol. This is not yet perfect, but its a lot + better than what we had */ +static int ms_fnmatch_lanman_core(const smb_ucs2_t *pattern, + const smb_ucs2_t *string) +{ + const smb_ucs2_t *p = pattern, *n = string; + smb_ucs2_t c; + + if (strcmp_wa(p, "?")==0 && strcmp_wa(n, ".")) goto match; + + while ((c = *p++)) { + switch (c) { + case UCS2_CHAR('.'): + if (! *n) goto next; + if (*n != UCS2_CHAR('.')) goto nomatch; + n++; + break; + + case UCS2_CHAR('?'): + if (! *n) goto next; + if ((*n == UCS2_CHAR('.') && + n[1] != UCS2_CHAR('.')) || ! *n) + goto next; + n++; + break; + + case UCS2_CHAR('>'): + if (! *n) goto next; + if (n[0] == UCS2_CHAR('.')) { + if (! n[1] && ms_fnmatch_lanman_core(p, n+1) == 0) goto match; + if (ms_fnmatch_lanman_core(p, n) == 0) goto match; + goto nomatch; + } + n++; + break; + + case UCS2_CHAR('*'): + if (! *n) goto next; + if (! *p) goto match; + for (; *n; n++) { + if (ms_fnmatch_lanman_core(p, n) == 0) goto match; + } + break; + + case UCS2_CHAR('<'): + for (; *n; n++) { + if (ms_fnmatch_lanman_core(p, n) == 0) goto match; + if (*n == UCS2_CHAR('.') && + !strchr_w(n+1,UCS2_CHAR('.'))) { + n++; + break; + } + } + break; + + case UCS2_CHAR('"'): + if (*n == 0 && ms_fnmatch_lanman_core(p, n) == 0) goto match; + if (*n != UCS2_CHAR('.')) goto nomatch; + n++; + break; + + default: + if (c != *n) goto nomatch; + n++; + } + } + + if (! *n) goto match; + + nomatch: + /* + if (verbose) printf("NOMATCH pattern=[%s] string=[%s]\n", pattern, string); + */ + return -1; + +next: + if (ms_fnmatch_lanman_core(p, n) == 0) goto match; + goto nomatch; + + match: + /* + if (verbose) printf("MATCH pattern=[%s] string=[%s]\n", pattern, string); + */ + return 0; +} + +static int ms_fnmatch_lanman1(const smb_ucs2_t *pattern, const smb_ucs2_t *string) +{ + if (!strpbrk_wa(pattern, "?*<>\"")) { + smb_ucs2_t s[] = {UCS2_CHAR('.'), 0}; + if (strcmp_wa(string,"..") == 0) string = s; + return strcasecmp_w(pattern, string); + } + + if (strcmp_wa(string,"..") == 0 || strcmp_wa(string,".") == 0) { + smb_ucs2_t dot[] = {UCS2_CHAR('.'), 0}; + smb_ucs2_t dotdot[] = {UCS2_CHAR('.'), UCS2_CHAR('.'), 0}; + return ms_fnmatch_lanman_core(pattern, dotdot) && + ms_fnmatch_lanman_core(pattern, dot); + } + + return ms_fnmatch_lanman_core(pattern, string); +} + + +/* the following function was derived using the masktest utility - + after years of effort we finally have a perfect MS wildcard + matching routine! + + NOTE: this matches only filenames with no directory component + + Returns 0 on match, -1 on fail. +*/ +static int ms_fnmatch_w(const smb_ucs2_t *pattern, const smb_ucs2_t *string, + enum protocol_types protocol) +{ + const smb_ucs2_t *p = pattern, *n = string; + smb_ucs2_t c; + + if (protocol <= PROTOCOL_LANMAN2) { + return ms_fnmatch_lanman1(pattern, string); + } + + while ((c = *p++)) { + switch (c) { + case UCS2_CHAR('?'): + if (! *n) return -1; + n++; + break; + + case UCS2_CHAR('>'): + if (n[0] == UCS2_CHAR('.')) { + if (! n[1] && ms_fnmatch_w(p, n+1, protocol) == 0) return 0; + if (ms_fnmatch_w(p, n, protocol) == 0) return 0; + return -1; + } + if (! *n) return ms_fnmatch_w(p, n, protocol); + n++; + break; + + case UCS2_CHAR('*'): + for (; *n; n++) { + if (ms_fnmatch_w(p, n, protocol) == 0) return 0; + } + break; + + case UCS2_CHAR('<'): + for (; *n; n++) { + if (ms_fnmatch_w(p, n, protocol) == 0) return 0; + if (*n == UCS2_CHAR('.') && !strchr_wa(n+1,'.')) { + n++; + break; + } + } + break; + + case UCS2_CHAR('"'): + if (*n == 0 && ms_fnmatch_w(p, n, protocol) == 0) return 0; + if (*n != UCS2_CHAR('.')) return -1; + n++; + break; + + default: + if (c != *n) return -1; + n++; + } + } + + if (! *n) return 0; + + return -1; +} + + +int ms_fnmatch(const char *pattern, const char *string, enum protocol_types protocol) +{ + wpstring p, s; + int ret; + + pstrcpy_wa(p, pattern); + pstrcpy_wa(s, string); + + ret = ms_fnmatch_w(p, s, protocol); +/* DEBUG(0,("ms_fnmatch(%s,%s) -> %d\n", pattern, string, ret)); */ + return ret; +} + +/* a generic fnmatch function - uses for non-CIFS pattern matching */ +int gen_fnmatch(const char *pattern, const char *string) +{ + return ms_fnmatch(pattern, string, PROTOCOL_NT1); +} diff --git a/source4/lib/mutex.c b/source4/lib/mutex.c new file mode 100644 index 0000000000..1be23a52ba --- /dev/null +++ b/source4/lib/mutex.c @@ -0,0 +1,142 @@ +/* + Unix SMB/CIFS implementation. + Samba mutex/lock functions + Copyright (C) Andrew Tridgell 2003 + Copyright (C) James J Myers 2003 + + 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" + +static mutex_t mutex_list[MUTEX_MAX]; + +/* the registered mutex handlers */ +static struct { + const char *name; + struct mutex_ops ops; +} mutex_handlers; + +int mutex_lock_by_id(enum mutex_id id, const char *name) +{ + return mutex_lock(&mutex_list[id], name); +} + +int mutex_unlock_by_id(enum mutex_id id, const char *name) +{ + return mutex_unlock(&mutex_list[id], name); +} + +int mutex_init(mutex_t *mutex, const char *name) +{ + if (mutex_handlers.ops.mutex_init) { + return mutex_handlers.ops.mutex_init(mutex, name); + } + return 0; +} + +int mutex_destroy(mutex_t *mutex, const char *name) +{ + if (mutex_handlers.ops.mutex_destroy) { + return mutex_handlers.ops.mutex_destroy(mutex, name); + } + return 0; +} + +int mutex_lock(mutex_t *mutex, const char *name) +{ + if (mutex_handlers.ops.mutex_lock) { + return mutex_handlers.ops.mutex_lock(mutex, name); + } + return 0; +} + +int mutex_unlock(mutex_t *mutex, const char *name) +{ + if (mutex_handlers.ops.mutex_unlock) { + return mutex_handlers.ops.mutex_unlock(mutex, name); + } + return 0; +} + +/* read/write lock routines */ + +int rwlock_init(rwlock_t *rwlock, const char *name) +{ + if (mutex_handlers.ops.rwlock_init) { + return mutex_handlers.ops.rwlock_init(rwlock, name); + } + return 0; +} + +int rwlock_destroy(rwlock_t *rwlock, const char *name) +{ + if (mutex_handlers.ops.rwlock_destroy) { + return mutex_handlers.ops.rwlock_destroy(rwlock, name); + } + return 0; +} + +int rwlock_lock_write(rwlock_t *rwlock, const char *name) +{ + if (mutex_handlers.ops.rwlock_lock_write) { + return mutex_handlers.ops.rwlock_lock_write(rwlock, name); + } + return 0; +} + +int rwlock_lock_read(rwlock_t *rwlock, const char *name) +{ + if (mutex_handlers.ops.rwlock_lock_read) { + return mutex_handlers.ops.rwlock_lock_read(rwlock, name); + } + return 0; +} + +int rwlock_unlock(rwlock_t *rwlock, const char *name) +{ + if (mutex_handlers.ops.rwlock_unlock) { + return mutex_handlers.ops.rwlock_unlock(rwlock, name); + } + return 0; +} + + +/* + register a set of mutex/rwlock handlers. + Should only be called once in the execution of smbd. +*/ +BOOL register_mutex_handlers(const char *name, struct mutex_ops *ops) +{ + if (mutex_handlers.name != NULL) { + /* it's already registered! */ + DEBUG(2,("mutex handler '%s' already registered - failed '%s'\n", + mutex_handlers.name, name)); + return False; + } + + mutex_handlers.name = name; + mutex_handlers.ops = *ops; + + if (mutex_handlers.ops.mutex_init) { + enum mutex_id id; + for (id=0; id < MUTEX_MAX; id++) { + mutex_handlers.ops.mutex_init(&mutex_list[id], "mutex_list"); + } + } + + DEBUG(2,("mutex handler '%s' registered\n", name)); + return True; +} + diff --git a/source4/lib/pam_errors.c b/source4/lib/pam_errors.c new file mode 100644 index 0000000000..925441fb1d --- /dev/null +++ b/source4/lib/pam_errors.c @@ -0,0 +1,126 @@ +/* + * Unix SMB/CIFS implementation. + * PAM error mapping functions + * Copyright (C) Andrew Bartlett 2002 + * + * 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" + +#ifdef WITH_PAM +#include <security/pam_appl.h> + +#if defined(PAM_AUTHTOK_RECOVERY_ERR) && !defined(PAM_AUTHTOK_RECOVER_ERR) +#define PAM_AUTHTOK_RECOVER_ERR PAM_AUTHTOK_RECOVERY_ERR +#endif + +/* PAM -> NT_STATUS map */ +static const struct { + int pam_code; + NTSTATUS ntstatus; +} pam_to_nt_status_map[] = { + {PAM_OPEN_ERR, NT_STATUS_UNSUCCESSFUL}, + {PAM_SYMBOL_ERR, NT_STATUS_UNSUCCESSFUL}, + {PAM_SERVICE_ERR, NT_STATUS_UNSUCCESSFUL}, + {PAM_SYSTEM_ERR, NT_STATUS_UNSUCCESSFUL}, + {PAM_BUF_ERR, NT_STATUS_UNSUCCESSFUL}, + {PAM_PERM_DENIED, NT_STATUS_ACCESS_DENIED}, + {PAM_AUTH_ERR, NT_STATUS_WRONG_PASSWORD}, + {PAM_CRED_INSUFFICIENT, NT_STATUS_INSUFFICIENT_LOGON_INFO}, /* FIXME: Is this correct? */ + {PAM_AUTHINFO_UNAVAIL, NT_STATUS_LOGON_FAILURE}, + {PAM_USER_UNKNOWN, NT_STATUS_NO_SUCH_USER}, + {PAM_MAXTRIES, NT_STATUS_REMOTE_SESSION_LIMIT}, /* FIXME: Is this correct? */ + {PAM_NEW_AUTHTOK_REQD, NT_STATUS_PASSWORD_MUST_CHANGE}, + {PAM_ACCT_EXPIRED, NT_STATUS_ACCOUNT_EXPIRED}, + {PAM_SESSION_ERR, NT_STATUS_INSUFFICIENT_RESOURCES}, + {PAM_CRED_UNAVAIL, NT_STATUS_NO_TOKEN}, /* FIXME: Is this correct? */ + {PAM_CRED_EXPIRED, NT_STATUS_PASSWORD_EXPIRED}, /* FIXME: Is this correct? */ + {PAM_CRED_ERR, NT_STATUS_UNSUCCESSFUL}, + {PAM_AUTHTOK_ERR, NT_STATUS_UNSUCCESSFUL}, +#ifdef PAM_AUTHTOK_RECOVER_ERR + {PAM_AUTHTOK_RECOVER_ERR, NT_STATUS_UNSUCCESSFUL}, +#endif + {PAM_AUTHTOK_EXPIRED, NT_STATUS_PASSWORD_EXPIRED}, + {PAM_SUCCESS, NT_STATUS_OK} +}; + +/* NT_STATUS -> PAM map */ +static const struct { + NTSTATUS ntstatus; + int pam_code; +} nt_status_to_pam_map[] = { + {NT_STATUS_UNSUCCESSFUL, PAM_SYSTEM_ERR}, + {NT_STATUS_NO_SUCH_USER, PAM_USER_UNKNOWN}, + {NT_STATUS_WRONG_PASSWORD, PAM_AUTH_ERR}, + {NT_STATUS_LOGON_FAILURE, PAM_AUTH_ERR}, + {NT_STATUS_ACCOUNT_EXPIRED, PAM_ACCT_EXPIRED}, + {NT_STATUS_PASSWORD_EXPIRED, PAM_AUTHTOK_EXPIRED}, + {NT_STATUS_PASSWORD_MUST_CHANGE, PAM_NEW_AUTHTOK_REQD}, + {NT_STATUS_OK, PAM_SUCCESS} +}; + +/***************************************************************************** +convert a PAM error to a NT status32 code + *****************************************************************************/ +NTSTATUS pam_to_nt_status(int pam_error) +{ + int i; + if (pam_error == 0) return NT_STATUS_OK; + + for (i=0; NT_STATUS_V(pam_to_nt_status_map[i].ntstatus); i++) { + if (pam_error == pam_to_nt_status_map[i].pam_code) + return pam_to_nt_status_map[i].ntstatus; + } + return NT_STATUS_UNSUCCESSFUL; +} + +/***************************************************************************** +convert an NT status32 code to a PAM error + *****************************************************************************/ +int nt_status_to_pam(NTSTATUS nt_status) +{ + int i; + if NT_STATUS_IS_OK(nt_status) return PAM_SUCCESS; + + for (i=0; NT_STATUS_V(nt_status_to_pam_map[i].ntstatus); i++) { + if (NT_STATUS_EQUAL(nt_status,nt_status_to_pam_map[i].ntstatus)) + return nt_status_to_pam_map[i].pam_code; + } + return PAM_SYSTEM_ERR; +} + +#else + +/***************************************************************************** +convert a PAM error to a NT status32 code + *****************************************************************************/ +NTSTATUS pam_to_nt_status(int pam_error) +{ + if (pam_error == 0) return NT_STATUS_OK; + return NT_STATUS_UNSUCCESSFUL; +} + +/***************************************************************************** +convert an NT status32 code to a PAM error + *****************************************************************************/ +int nt_status_to_pam(NTSTATUS nt_status) +{ + if (NT_STATUS_EQUAL(nt_status, NT_STATUS_OK)) return 0; + return 4; /* PAM_SYSTEM_ERR */ +} + +#endif + diff --git a/source4/lib/pidfile.c b/source4/lib/pidfile.c new file mode 100644 index 0000000000..3471f27b8e --- /dev/null +++ b/source4/lib/pidfile.c @@ -0,0 +1,109 @@ +/* this code is broken - there is a race condition with the unlink (tridge) */ + +/* + Unix SMB/CIFS implementation. + pidfile handling + Copyright (C) Andrew Tridgell 1998 + + 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" + +#ifndef O_NONBLOCK +#define O_NONBLOCK +#endif + +/* return the pid in a pidfile. return 0 if the process (or pidfile) + does not exist */ +pid_t pidfile_pid(const char *name) +{ + int fd; + char pidstr[20]; + unsigned ret; + pstring pidFile; + + slprintf(pidFile, sizeof(pidFile)-1, "%s/%s.pid", lp_piddir(), name); + + fd = sys_open(pidFile, O_NONBLOCK | O_RDONLY, 0644); + if (fd == -1) { + return 0; + } + + ZERO_ARRAY(pidstr); + + if (read(fd, pidstr, sizeof(pidstr)-1) <= 0) { + goto noproc; + } + + ret = atoi(pidstr); + + if (!process_exists((pid_t)ret)) { + goto noproc; + } + + if (fcntl_lock(fd,SMB_F_SETLK,0,1,F_RDLCK)) { + /* we could get the lock - it can't be a Samba process */ + goto noproc; + } + + close(fd); + return (pid_t)ret; + + noproc: + close(fd); + unlink(pidFile); + return 0; +} + +/* create a pid file in the pid directory. open it and leave it locked */ +void pidfile_create(const char *name) +{ + int fd; + char buf[20]; + pstring pidFile; + pid_t pid; + + slprintf(pidFile, sizeof(pidFile)-1, "%s/%s.pid", lp_piddir(), name); + + pid = pidfile_pid(name); + if (pid != 0) { + DEBUG(0,("ERROR: %s is already running. File %s exists and process id %d is running.\n", + name, pidFile, (int)pid)); + exit(1); + } + + fd = sys_open(pidFile, O_NONBLOCK | O_CREAT | O_WRONLY | O_EXCL, 0644); + if (fd == -1) { + DEBUG(0,("ERROR: can't open %s: Error was %s\n", pidFile, + strerror(errno))); + exit(1); + } + + if (fcntl_lock(fd,SMB_F_SETLK,0,1,F_WRLCK)==False) { + DEBUG(0,("ERROR: %s : fcntl lock of file %s failed. Error was %s\n", + name, pidFile, strerror(errno))); + exit(1); + } + + memset(buf, 0, sizeof(buf)); + slprintf(buf, sizeof(buf) - 1, "%u\n", (unsigned int) getpid()); + if (write(fd, buf, strlen(buf)) != (ssize_t)strlen(buf)) { + DEBUG(0,("ERROR: can't write to file %s: %s\n", + pidFile, strerror(errno))); + exit(1); + } + /* Leave pid file open & locked for the duration... */ +} diff --git a/source4/lib/popt/CHANGES b/source4/lib/popt/CHANGES new file mode 100644 index 0000000000..b6ab2aa308 --- /dev/null +++ b/source4/lib/popt/CHANGES @@ -0,0 +1,43 @@ +1.3 -> + - heavy dose of const's + - poptParseArgvString() now NULL terminates the list + +1.2.3 -> 1.3 + - added support for single - + - misc bug fixes + - portability improvements + +1.2.2 -> 1.2.3 + - fixed memset() in help message generation (Dale Hawkins) + - added extern "C" stuff to popt.h for C++ compilers (Dale Hawkins) + - const'ified poptParseArgvString (Jeff Garzik) + +1.2.1 -> 1.2.2 + - fixed bug in chaind alias happens which seems to have only + affected --triggers in rpm + - added POPT_ARG_VAL + - popt.3 installed by default + +1.2 -> 1.2.1 + - added POPT_ARG_INTL_DOMAIN (Elliot Lee) + - updated Makefile's to be more GNUish (Elliot Lee) + +1.1 -> 1.2 + - added popt.3 man page (Robert Lynch) + - don't use mmap anymore (its lack of portability isn't worth the + trouble) + - added test script + - added support for exec + - removed support for *_POPT_ALIASES env variable -- it was a bad + idea + - reorganized into multiple source files + - added automatic help generation, POPT_AUTOHELP + - added table callbacks + - added table inclusion + - updated man page for new features + - added test scripts + +1.0 -> 1.1 + - moved to autoconf (Fred Fish) + - added STRERROR replacement (Norbert Warmuth) + - added const keywords (Bruce Perens) diff --git a/source4/lib/popt/COPYING b/source4/lib/popt/COPYING new file mode 100644 index 0000000000..b4c7ca876c --- /dev/null +++ b/source4/lib/popt/COPYING @@ -0,0 +1,22 @@ +Copyright (c) 1998 Red Hat Software + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of the X Consortium shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from the X Consortium. diff --git a/source4/lib/popt/README b/source4/lib/popt/README new file mode 100644 index 0000000000..7fccc836ff --- /dev/null +++ b/source4/lib/popt/README @@ -0,0 +1,18 @@ +This is the popt command line option parsing library. While it is similiar +to getopt(3), it contains a number of enhancements, including: + + 1) popt is fully reentrant + 2) popt can parse arbitrary argv[] style arrays while + getopt(2) makes this quite difficult + 3) popt allows users to alias command line arguments + 4) popt provides convience functions for parsting strings + into argv[] style arrays + +popt is used by rpm, the Red Hat install program, and many other Red Hat +utilities, all of which provide excellent examples of how to use popt. +Complete documentation on popt is available in popt.ps (included in this +tarball), which is excerpted with permission from the book "Linux +Application Development" by Michael K. Johnson and Erik Troan (availble +from Addison Wesley in May, 1998). + +Comments on popt should be addressed to ewt@redhat.com. diff --git a/source4/lib/popt/dummy.in b/source4/lib/popt/dummy.in new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/source4/lib/popt/dummy.in diff --git a/source4/lib/popt/findme.c b/source4/lib/popt/findme.c new file mode 100644 index 0000000000..f2ad05bb3f --- /dev/null +++ b/source4/lib/popt/findme.c @@ -0,0 +1,46 @@ +/* (C) 1998 Red Hat Software, Inc. -- Licensing details are in the COPYING + file accompanying popt source distributions, available from + ftp://ftp.redhat.com/pub/code/popt */ + +#include "system.h" +#include "findme.h" + +const char * findProgramPath(const char * argv0) { + char * path = getenv("PATH"); + char * pathbuf; + char * start, * chptr; + char * buf, *local = NULL; + + /* If there is a / in the argv[0], it has to be an absolute + path */ + if (strchr(argv0, '/')) + return xstrdup(argv0); + + if (!path) return NULL; + + local = start = pathbuf = malloc(strlen(path) + 1); + buf = malloc(strlen(path) + strlen(argv0) + 2); + strcpy(pathbuf, path); + + chptr = NULL; + do { + if ((chptr = strchr(start, ':'))) + *chptr = '\0'; + sprintf(buf, "%s/%s", start, argv0); + + if (!access(buf, X_OK)) { + if (local) free(local); + return buf; + } + + if (chptr) + start = chptr + 1; + else + start = NULL; + } while (start && *start); + + free(buf); + if (local) free(local); + + return NULL; +} diff --git a/source4/lib/popt/findme.h b/source4/lib/popt/findme.h new file mode 100644 index 0000000000..5e93963d60 --- /dev/null +++ b/source4/lib/popt/findme.h @@ -0,0 +1,10 @@ +/* (C) 1998 Red Hat Software, Inc. -- Licensing details are in the COPYING + file accompanying popt source distributions, available from + ftp://ftp.redhat.com/pub/code/popt */ + +#ifndef H_FINDME +#define H_FINDME + +const char * findProgramPath(const char * argv0); + +#endif diff --git a/source4/lib/popt/popt.c b/source4/lib/popt/popt.c new file mode 100644 index 0000000000..9fa8650312 --- /dev/null +++ b/source4/lib/popt/popt.c @@ -0,0 +1,782 @@ +/* (C) 1998 Red Hat Software, Inc. -- Licensing details are in the COPYING + file accompanying popt source distributions, available from + ftp://ftp.redhat.com/pub/code/popt */ + +#include "system.h" +#include "findme.h" +#include "poptint.h" + +#ifndef HAVE_STRERROR +static char * strerror(int errno) { + extern int sys_nerr; + extern char * sys_errlist[]; + + if ((0 <= errno) && (errno < sys_nerr)) + return sys_errlist[errno]; + else + return POPT_("unknown errno"); +} +#endif + +void poptSetExecPath(poptContext con, const char * path, int allowAbsolute) { + if (con->execPath) xfree(con->execPath); + con->execPath = xstrdup(path); + con->execAbsolute = allowAbsolute; +} + +static void invokeCallbacks(poptContext con, const struct poptOption * table, + int post) { + const struct poptOption * opt = table; + poptCallbackType cb; + + while (opt->longName || opt->shortName || opt->arg) { + if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) { + invokeCallbacks(con, opt->arg, post); + } else if (((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_CALLBACK) && + ((!post && (opt->argInfo & POPT_CBFLAG_PRE)) || + ( post && (opt->argInfo & POPT_CBFLAG_POST)))) { + cb = (poptCallbackType)opt->arg; + cb(con, post ? POPT_CALLBACK_REASON_POST : POPT_CALLBACK_REASON_PRE, + NULL, NULL, opt->descrip); + } + opt++; + } +} + +poptContext poptGetContext(const char * name, int argc, const char ** argv, + const struct poptOption * options, int flags) { + poptContext con = malloc(sizeof(*con)); + + memset(con, 0, sizeof(*con)); + + con->os = con->optionStack; + con->os->argc = argc; + con->os->argv = argv; + con->os->argb = NULL; + + if (!(flags & POPT_CONTEXT_KEEP_FIRST)) + con->os->next = 1; /* skip argv[0] */ + + con->leftovers = calloc( (argc + 1), sizeof(char *) ); + con->options = options; + con->aliases = NULL; + con->numAliases = 0; + con->flags = flags; + con->execs = NULL; + con->numExecs = 0; + con->finalArgvAlloced = argc * 2; + con->finalArgv = calloc( con->finalArgvAlloced, sizeof(*con->finalArgv) ); + con->execAbsolute = 1; + con->arg_strip = NULL; + + if (getenv("POSIXLY_CORRECT") || getenv("POSIX_ME_HARDER")) + con->flags |= POPT_CONTEXT_POSIXMEHARDER; + + if (name) + con->appName = strcpy(malloc(strlen(name) + 1), name); + + invokeCallbacks(con, con->options, 0); + + return con; +} + +static void cleanOSE(struct optionStackEntry *os) +{ + if (os->nextArg) { + xfree(os->nextArg); + os->nextArg = NULL; + } + if (os->argv) { + xfree(os->argv); + os->argv = NULL; + } + if (os->argb) { + PBM_FREE(os->argb); + os->argb = NULL; + } +} + +void poptResetContext(poptContext con) { + int i; + + while (con->os > con->optionStack) { + cleanOSE(con->os--); + } + if (con->os->argb) { + PBM_FREE(con->os->argb); + con->os->argb = NULL; + } + con->os->currAlias = NULL; + con->os->nextCharArg = NULL; + con->os->nextArg = NULL; + con->os->next = 1; /* skip argv[0] */ + + con->numLeftovers = 0; + con->nextLeftover = 0; + con->restLeftover = 0; + con->doExec = NULL; + + for (i = 0; i < con->finalArgvCount; i++) { + if (con->finalArgv[i]) { + xfree(con->finalArgv[i]); + con->finalArgv[i] = NULL; + } + } + + con->finalArgvCount = 0; + + if (con->arg_strip) { + PBM_FREE(con->arg_strip); + con->arg_strip = NULL; + } +} + +/* Only one of longName, shortName may be set at a time */ +static int handleExec(poptContext con, char * longName, char shortName) { + int i; + + i = con->numExecs - 1; + if (longName) { + while (i >= 0 && (!con->execs[i].longName || + strcmp(con->execs[i].longName, longName))) i--; + } else { + while (i >= 0 && + con->execs[i].shortName != shortName) i--; + } + + if (i < 0) return 0; + + if (con->flags & POPT_CONTEXT_NO_EXEC) + return 1; + + if (con->doExec == NULL) { + con->doExec = con->execs + i; + return 1; + } + + /* We already have an exec to do; remember this option for next + time 'round */ + if ((con->finalArgvCount + 1) >= (con->finalArgvAlloced)) { + con->finalArgvAlloced += 10; + con->finalArgv = realloc(con->finalArgv, + sizeof(*con->finalArgv) * con->finalArgvAlloced); + } + + i = con->finalArgvCount++; + { char *s = malloc((longName ? strlen(longName) : 0) + 3); + if (longName) + sprintf(s, "--%s", longName); + else + sprintf(s, "-%c", shortName); + con->finalArgv[i] = s; + } + + return 1; +} + +/* Only one of longName, shortName may be set at a time */ +static int handleAlias(poptContext con, const char * longName, char shortName, + /*@keep@*/ const char * nextCharArg) { + int i; + + if (con->os->currAlias && con->os->currAlias->longName && longName && + !strcmp(con->os->currAlias->longName, longName)) + return 0; + if (con->os->currAlias && shortName && + shortName == con->os->currAlias->shortName) + return 0; + + i = con->numAliases - 1; + if (longName) { + while (i >= 0 && (!con->aliases[i].longName || + strcmp(con->aliases[i].longName, longName))) i--; + } else { + while (i >= 0 && + con->aliases[i].shortName != shortName) i--; + } + + if (i < 0) return 0; + + if ((con->os - con->optionStack + 1) == POPT_OPTION_DEPTH) + return POPT_ERROR_OPTSTOODEEP; + + if (nextCharArg && *nextCharArg) + con->os->nextCharArg = nextCharArg; + + con->os++; + con->os->next = 0; + con->os->stuffed = 0; + con->os->nextArg = NULL; + con->os->nextCharArg = NULL; + con->os->currAlias = con->aliases + i; + poptDupArgv(con->os->currAlias->argc, con->os->currAlias->argv, + &con->os->argc, &con->os->argv); + con->os->argb = NULL; + + return 1; +} + +static void execCommand(poptContext con) { + const char ** argv; + int pos = 0; + const char * script = con->doExec->script; + + argv = malloc(sizeof(*argv) * + (6 + con->numLeftovers + con->finalArgvCount)); + + if (!con->execAbsolute && strchr(script, '/')) return; + + if (!strchr(script, '/') && con->execPath) { + char *s = malloc(strlen(con->execPath) + strlen(script) + 2); + sprintf(s, "%s/%s", con->execPath, script); + argv[pos] = s; + } else { + argv[pos] = script; + } + pos++; + + argv[pos] = findProgramPath(con->os->argv[0]); + if (argv[pos]) pos++; + argv[pos++] = ";"; + + memcpy(argv + pos, con->finalArgv, sizeof(*argv) * con->finalArgvCount); + pos += con->finalArgvCount; + + if (con->numLeftovers) { + argv[pos++] = "--"; + memcpy(argv + pos, con->leftovers, sizeof(*argv) * con->numLeftovers); + pos += con->numLeftovers; + } + + argv[pos++] = NULL; + +#ifdef __hpux + setresuid(getuid(), getuid(),-1); +#else +/* + * XXX " ... on BSD systems setuid() should be preferred over setreuid()" + * XXX sez' Timur Bakeyev <mc@bat.ru> + * XXX from Norbert Warmuth <nwarmuth@privat.circular.de> + */ +#if defined(HAVE_SETUID) + setuid(getuid()); +#elif defined (HAVE_SETREUID) + setreuid(getuid(), getuid()); /*hlauer: not portable to hpux9.01 */ +#else + ; /* Can't drop privileges */ +#endif +#endif + + execvp(argv[0], (char *const *)argv); +} + +/*@observer@*/ static const struct poptOption * +findOption(const struct poptOption * table, const char * longName, + char shortName, + /*@out@*/ poptCallbackType * callback, /*@out@*/ const void ** callbackData, + int singleDash) +{ + const struct poptOption * opt = table; + const struct poptOption * opt2; + const struct poptOption * cb = NULL; + + /* This happens when a single - is given */ + if (singleDash && !shortName && !*longName) + shortName = '-'; + + while (opt->longName || opt->shortName || opt->arg) { + if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) { + opt2 = findOption(opt->arg, longName, shortName, callback, + callbackData, singleDash); + if (opt2) { + if (*callback && !*callbackData) + *callbackData = opt->descrip; + return opt2; + } + } else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_CALLBACK) { + cb = opt; + } else if (longName && opt->longName && + (!singleDash || (opt->argInfo & POPT_ARGFLAG_ONEDASH)) && + !strcmp(longName, opt->longName)) { + break; + } else if (shortName && shortName == opt->shortName) { + break; + } + opt++; + } + + if (!opt->longName && !opt->shortName) return NULL; + *callbackData = NULL; + *callback = NULL; + if (cb) { + *callback = (poptCallbackType)cb->arg; + if (!(cb->argInfo & POPT_CBFLAG_INC_DATA)) + *callbackData = cb->descrip; + } + + return opt; +} + +static const char *findNextArg(poptContext con, unsigned argx, int delete) +{ + struct optionStackEntry * os = con->os; + const char * arg; + + do { + int i; + arg = NULL; + while (os->next == os->argc && os > con->optionStack) os--; + if (os->next == os->argc && os == con->optionStack) break; + for (i = os->next; i < os->argc; i++) { + if (os->argb && PBM_ISSET(i, os->argb)) continue; + if (*os->argv[i] == '-') continue; + if (--argx > 0) continue; + arg = os->argv[i]; + if (delete) { + if (os->argb == NULL) os->argb = PBM_ALLOC(os->argc); + PBM_SET(i, os->argb); + } + break; + } + if (os > con->optionStack) os--; + } while (arg == NULL); + return arg; +} + +static /*@only@*/ const char * expandNextArg(poptContext con, const char * s) +{ + const char *a; + size_t alen; + char *t, *te; + size_t tn = strlen(s) + 1; + char c; + + te = t = malloc(tn);; + while ((c = *s++) != '\0') { + switch (c) { +#if 0 /* XXX can't do this */ + case '\\': /* escape */ + c = *s++; + break; +#endif + case '!': + if (!(s[0] == '#' && s[1] == ':' && s[2] == '+')) + break; + if ((a = findNextArg(con, 1, 1)) == NULL) + break; + s += 3; + + alen = strlen(a); + tn += alen; + *te = '\0'; + t = realloc(t, tn); + te = t + strlen(t); + strncpy(te, a, alen); te += alen; + continue; + /*@notreached@*/ break; + default: + break; + } + *te++ = c; + } + *te = '\0'; + t = realloc(t, strlen(t)+1); /* XXX memory leak, hard to plug */ + return t; +} + +static void poptStripArg(poptContext con, int which) +{ + if(con->arg_strip == NULL) { + con->arg_strip = PBM_ALLOC(con->optionStack[0].argc); + } + PBM_SET(which, con->arg_strip); +} + +/* returns 'val' element, -1 on last item, POPT_ERROR_* on error */ +int poptGetNextOpt(poptContext con) +{ + const struct poptOption * opt = NULL; + int done = 0; + + /* looks a bit tricky to get rid of alloca properly in this fn */ +#if HAVE_ALLOCA_H +#define ALLOCA(x) alloca(x) +#else +#define ALLOCA(x) malloc(x) +#endif + + + while (!done) { + const char * origOptString = NULL; + poptCallbackType cb = NULL; + const void * cbData = NULL; + const char * longArg = NULL; + int canstrip = 0; + + while (!con->os->nextCharArg && con->os->next == con->os->argc + && con->os > con->optionStack) { + cleanOSE(con->os--); + } + if (!con->os->nextCharArg && con->os->next == con->os->argc) { + invokeCallbacks(con, con->options, 1); + if (con->doExec) execCommand(con); + return -1; + } + + /* Process next long option */ + if (!con->os->nextCharArg) { + char * localOptString, * optString; + int thisopt; + + if (con->os->argb && PBM_ISSET(con->os->next, con->os->argb)) { + con->os->next++; + continue; + } + thisopt=con->os->next; + origOptString = con->os->argv[con->os->next++]; + + if (con->restLeftover || *origOptString != '-') { + con->leftovers[con->numLeftovers++] = origOptString; + if (con->flags & POPT_CONTEXT_POSIXMEHARDER) + con->restLeftover = 1; + continue; + } + + /* Make a copy we can hack at */ + localOptString = optString = + strcpy(ALLOCA(strlen(origOptString) + 1), + origOptString); + + if (!optString[0]) + return POPT_ERROR_BADOPT; + + if (optString[1] == '-' && !optString[2]) { + con->restLeftover = 1; + continue; + } else { + char *oe; + int singleDash; + + optString++; + if (*optString == '-') + singleDash = 0, optString++; + else + singleDash = 1; + + /* XXX aliases with arg substitution need "--alias=arg" */ + if (handleAlias(con, optString, '\0', NULL)) + continue; + if (handleExec(con, optString, '\0')) + continue; + + /* Check for "--long=arg" option. */ + for (oe = optString; *oe && *oe != '='; oe++) + ; + if (*oe == '=') { + *oe++ = '\0'; + /* XXX longArg is mapped back to persistent storage. */ + longArg = origOptString + (oe - localOptString); + } + + opt = findOption(con->options, optString, '\0', &cb, &cbData, + singleDash); + if (!opt && !singleDash) + return POPT_ERROR_BADOPT; + } + + if (!opt) { + con->os->nextCharArg = origOptString + 1; + } else { + if(con->os == con->optionStack && + opt->argInfo & POPT_ARGFLAG_STRIP) { + canstrip = 1; + poptStripArg(con, thisopt); + } + } + } + + /* Process next short option */ + if (con->os->nextCharArg) { + origOptString = con->os->nextCharArg; + + con->os->nextCharArg = NULL; + + if (handleAlias(con, NULL, *origOptString, + origOptString + 1)) { + origOptString++; + continue; + } + if (handleExec(con, NULL, *origOptString)) + continue; + + opt = findOption(con->options, NULL, *origOptString, &cb, + &cbData, 0); + if (!opt) + return POPT_ERROR_BADOPT; + + origOptString++; + if (*origOptString) + con->os->nextCharArg = origOptString; + } + + if (opt->arg && (opt->argInfo & POPT_ARG_MASK) == POPT_ARG_NONE) { + *((int *)opt->arg) = 1; + } else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_VAL) { + if (opt->arg) + *((int *) opt->arg) = opt->val; + } else if ((opt->argInfo & POPT_ARG_MASK) != POPT_ARG_NONE) { + if (con->os->nextArg) { + xfree(con->os->nextArg); + con->os->nextArg = NULL; + } + if (longArg) { + con->os->nextArg = expandNextArg(con, longArg); + } else if (con->os->nextCharArg) { + con->os->nextArg = expandNextArg(con, con->os->nextCharArg); + con->os->nextCharArg = NULL; + } else { + while (con->os->next == con->os->argc && + con->os > con->optionStack) { + cleanOSE(con->os--); + } + if (con->os->next == con->os->argc) + return POPT_ERROR_NOARG; + + /* make sure this isn't part of a short arg or the + result of an alias expansion */ + if(con->os == con->optionStack && + opt->argInfo & POPT_ARGFLAG_STRIP && + canstrip) { + poptStripArg(con, con->os->next); + } + + con->os->nextArg = expandNextArg(con, con->os->argv[con->os->next++]); + } + + if (opt->arg) { + long aLong; + char *end; + + switch (opt->argInfo & POPT_ARG_MASK) { + case POPT_ARG_STRING: + /* XXX memory leak, hard to plug */ + *((const char **) opt->arg) = xstrdup(con->os->nextArg); + break; + + case POPT_ARG_INT: + case POPT_ARG_LONG: + aLong = strtol(con->os->nextArg, &end, 0); + if (!(end && *end == '\0')) + return POPT_ERROR_BADNUMBER; + + if (aLong == LONG_MIN || aLong == LONG_MAX) + return POPT_ERROR_OVERFLOW; + if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_LONG) { + *((long *) opt->arg) = aLong; + } else { + if (aLong > INT_MAX || aLong < INT_MIN) + return POPT_ERROR_OVERFLOW; + *((int *) opt->arg) = aLong; + } + break; + + default: + fprintf(stdout, POPT_("option type (%d) not implemented in popt\n"), + opt->argInfo & POPT_ARG_MASK); + exit(EXIT_FAILURE); + } + } + } + + if (cb) + cb(con, POPT_CALLBACK_REASON_OPTION, opt, con->os->nextArg, cbData); + else if (opt->val && ((opt->argInfo & POPT_ARG_MASK) != POPT_ARG_VAL)) + done = 1; + + if ((con->finalArgvCount + 2) >= (con->finalArgvAlloced)) { + con->finalArgvAlloced += 10; + con->finalArgv = realloc(con->finalArgv, + sizeof(*con->finalArgv) * con->finalArgvAlloced); + } + + { char *s = malloc((opt->longName ? strlen(opt->longName) : 0) + 3); + if (opt->longName) + sprintf(s, "--%s", opt->longName); + else + sprintf(s, "-%c", opt->shortName); + con->finalArgv[con->finalArgvCount++] = s; + } + + if (opt->arg && (opt->argInfo & POPT_ARG_MASK) != POPT_ARG_NONE + && (opt->argInfo & POPT_ARG_MASK) != POPT_ARG_VAL) { + con->finalArgv[con->finalArgvCount++] = xstrdup(con->os->nextArg); + } + } + + return opt->val; +} + +const char * poptGetOptArg(poptContext con) { + const char * ret = con->os->nextArg; + con->os->nextArg = NULL; + return ret; +} + +const char * poptGetArg(poptContext con) { + if (con->numLeftovers == con->nextLeftover) return NULL; + return con->leftovers[con->nextLeftover++]; +} + +const char * poptPeekArg(poptContext con) { + if (con->numLeftovers == con->nextLeftover) return NULL; + return con->leftovers[con->nextLeftover]; +} + +const char ** poptGetArgs(poptContext con) { + if (con->numLeftovers == con->nextLeftover) return NULL; + + /* some apps like [like RPM ;-) ] need this NULL terminated */ + con->leftovers[con->numLeftovers] = NULL; + + return (con->leftovers + con->nextLeftover); +} + +void poptFreeContext(poptContext con) { + int i; + + poptResetContext(con); + if (con->os->argb) free(con->os->argb); + + for (i = 0; i < con->numAliases; i++) { + if (con->aliases[i].longName) xfree(con->aliases[i].longName); + free(con->aliases[i].argv); + } + + for (i = 0; i < con->numExecs; i++) { + if (con->execs[i].longName) xfree(con->execs[i].longName); + xfree(con->execs[i].script); + } + if (con->execs) xfree(con->execs); + + free(con->leftovers); + free(con->finalArgv); + if (con->appName) xfree(con->appName); + if (con->aliases) free(con->aliases); + if (con->otherHelp) xfree(con->otherHelp); + if (con->execPath) xfree(con->execPath); + if (con->arg_strip) PBM_FREE(con->arg_strip); + + free(con); +} + +int poptAddAlias(poptContext con, struct poptAlias newAlias, + /*@unused@*/ int flags) +{ + int aliasNum = con->numAliases++; + struct poptAlias * alias; + + /* SunOS won't realloc(NULL, ...) */ + if (!con->aliases) + con->aliases = malloc(sizeof(newAlias) * con->numAliases); + else + con->aliases = realloc(con->aliases, + sizeof(newAlias) * con->numAliases); + alias = con->aliases + aliasNum; + + alias->longName = (newAlias.longName) + ? strcpy(malloc(strlen(newAlias.longName) + 1), newAlias.longName) + : NULL; + alias->shortName = newAlias.shortName; + alias->argc = newAlias.argc; + alias->argv = newAlias.argv; + + return 0; +} + +const char * poptBadOption(poptContext con, int flags) { + struct optionStackEntry * os; + + if (flags & POPT_BADOPTION_NOALIAS) + os = con->optionStack; + else + os = con->os; + + return os->argv[os->next - 1]; +} + +#define POPT_ERROR_NOARG -10 +#define POPT_ERROR_BADOPT -11 +#define POPT_ERROR_OPTSTOODEEP -13 +#define POPT_ERROR_BADQUOTE -15 /* only from poptParseArgString() */ +#define POPT_ERROR_ERRNO -16 /* only from poptParseArgString() */ + +const char *poptStrerror(const int error) { + switch (error) { + case POPT_ERROR_NOARG: + return POPT_("missing argument"); + case POPT_ERROR_BADOPT: + return POPT_("unknown option"); + case POPT_ERROR_OPTSTOODEEP: + return POPT_("aliases nested too deeply"); + case POPT_ERROR_BADQUOTE: + return POPT_("error in paramter quoting"); + case POPT_ERROR_BADNUMBER: + return POPT_("invalid numeric value"); + case POPT_ERROR_OVERFLOW: + return POPT_("number too large or too small"); + case POPT_ERROR_ERRNO: + return strerror(errno); + default: + return POPT_("unknown error"); + } +} + +int poptStuffArgs(poptContext con, const char ** argv) { + int argc; + + if ((con->os - con->optionStack) == POPT_OPTION_DEPTH) + return POPT_ERROR_OPTSTOODEEP; + + for (argc = 0; argv[argc]; argc++) + ; + + con->os++; + con->os->next = 0; + con->os->nextArg = NULL; + con->os->nextCharArg = NULL; + con->os->currAlias = NULL; + poptDupArgv(argc, argv, &con->os->argc, &con->os->argv); + con->os->argb = NULL; + con->os->stuffed = 1; + + return 0; +} + +const char * poptGetInvocationName(poptContext con) { + return con->os->argv[0]; +} + +int poptStrippedArgv(poptContext con, int argc, char **argv) +{ + int i,j=1, numargs=argc; + + for(i=1; i<argc; i++) { + if(PBM_ISSET(i, con->arg_strip)) { + numargs--; + } + } + + for(i=1; i<argc; i++) { + if(PBM_ISSET(i, con->arg_strip)) { + continue; + } else { + if(j<numargs) { + argv[j++]=argv[i]; + } else { + argv[j++]='\0'; + } + } + } + + return(numargs); +} diff --git a/source4/lib/popt/popt.h b/source4/lib/popt/popt.h new file mode 100644 index 0000000000..c33cedaec9 --- /dev/null +++ b/source4/lib/popt/popt.h @@ -0,0 +1,130 @@ +/* (C) 1998 Red Hat Software, Inc. -- Licensing details are in the COPYING + file accompanying popt source distributions, available from + ftp://ftp.redhat.com/pub/code/popt */ + +#ifndef H_POPT +#define H_POPT + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdio.h> /* for FILE * */ + +#define POPT_OPTION_DEPTH 10 + +#define POPT_ARG_NONE 0 +#define POPT_ARG_STRING 1 +#define POPT_ARG_INT 2 +#define POPT_ARG_LONG 3 +#define POPT_ARG_INCLUDE_TABLE 4 /* arg points to table */ +#define POPT_ARG_CALLBACK 5 /* table-wide callback... must be + set first in table; arg points + to callback, descrip points to + callback data to pass */ +#define POPT_ARG_INTL_DOMAIN 6 /* set the translation domain + for this table and any + included tables; arg points + to the domain string */ +#define POPT_ARG_VAL 7 /* arg should take value val */ +#define POPT_ARG_MASK 0x0000FFFF +#define POPT_ARGFLAG_ONEDASH 0x80000000 /* allow -longoption */ +#define POPT_ARGFLAG_DOC_HIDDEN 0x40000000 /* don't show in help/usage */ +#define POPT_ARGFLAG_STRIP 0x20000000 /* strip this arg from argv (only applies to long args) */ +#define POPT_CBFLAG_PRE 0x80000000 /* call the callback before parse */ +#define POPT_CBFLAG_POST 0x40000000 /* call the callback after parse */ +#define POPT_CBFLAG_INC_DATA 0x20000000 /* use data from the include line, + not the subtable */ + +#define POPT_ERROR_NOARG -10 +#define POPT_ERROR_BADOPT -11 +#define POPT_ERROR_OPTSTOODEEP -13 +#define POPT_ERROR_BADQUOTE -15 /* only from poptParseArgString() */ +#define POPT_ERROR_ERRNO -16 /* only from poptParseArgString() */ +#define POPT_ERROR_BADNUMBER -17 +#define POPT_ERROR_OVERFLOW -18 + +/* poptBadOption() flags */ +#define POPT_BADOPTION_NOALIAS (1 << 0) /* don't go into an alias */ + +/* poptGetContext() flags */ +#define POPT_CONTEXT_NO_EXEC (1 << 0) /* ignore exec expansions */ +#define POPT_CONTEXT_KEEP_FIRST (1 << 1) /* pay attention to argv[0] */ +#define POPT_CONTEXT_POSIXMEHARDER (1 << 2) /* options can't follow args */ + +struct poptOption { + /*@observer@*/ /*@null@*/ const char * longName; /* may be NULL */ + char shortName; /* may be '\0' */ + int argInfo; + /*@shared@*/ /*@null@*/ void * arg; /* depends on argInfo */ + int val; /* 0 means don't return, just update flag */ + /*@shared@*/ /*@null@*/ const char * descrip; /* description for autohelp -- may be NULL */ + /*@shared@*/ /*@null@*/ const char * argDescrip; /* argument description for autohelp */ +}; + +struct poptAlias { + /*@owned@*/ /*@null@*/ const char * longName; /* may be NULL */ + char shortName; /* may be '\0' */ + int argc; + /*@owned@*/ const char ** argv; /* must be free()able */ +}; + +extern struct poptOption poptHelpOptions[]; +#define POPT_AUTOHELP { NULL, '\0', POPT_ARG_INCLUDE_TABLE, poptHelpOptions, \ + 0, "Help options", NULL }, + +typedef struct poptContext_s * poptContext; +#ifndef __cplusplus +typedef struct poptOption * poptOption; +#endif + +enum poptCallbackReason { POPT_CALLBACK_REASON_PRE, + POPT_CALLBACK_REASON_POST, + POPT_CALLBACK_REASON_OPTION }; +typedef void (*poptCallbackType)(poptContext con, + enum poptCallbackReason reason, + const struct poptOption * opt, + const char * arg, const void * data); + +/*@only@*/ poptContext poptGetContext(/*@keep@*/ const char * name, + int argc, /*@keep@*/ const char ** argv, + /*@keep@*/ const struct poptOption * options, int flags); +void poptResetContext(poptContext con); + +/* returns 'val' element, -1 on last item, POPT_ERROR_* on error */ +int poptGetNextOpt(poptContext con); +/* returns NULL if no argument is available */ +/*@observer@*/ /*@null@*/ const char * poptGetOptArg(poptContext con); +/* returns NULL if no more options are available */ +/*@observer@*/ /*@null@*/ const char * poptGetArg(poptContext con); +/*@observer@*/ /*@null@*/ const char * poptPeekArg(poptContext con); +/*@observer@*/ /*@null@*/ const char ** poptGetArgs(poptContext con); +/* returns the option which caused the most recent error */ +/*@observer@*/ const char * poptBadOption(poptContext con, int flags); +void poptFreeContext( /*@only@*/ poptContext con); +int poptStuffArgs(poptContext con, /*@keep@*/ const char ** argv); +int poptAddAlias(poptContext con, struct poptAlias alias, int flags); +int poptReadConfigFile(poptContext con, const char * fn); +/* like above, but reads /etc/popt and $HOME/.popt along with environment + vars */ +int poptReadDefaultConfig(poptContext con, int useEnv); +/* argv should be freed -- this allows ', ", and \ quoting, but ' is treated + the same as " and both may include \ quotes */ +int poptDupArgv(int argc, const char **argv, + /*@out@*/ int * argcPtr, /*@out@*/ const char *** argvPtr); +int poptParseArgvString(const char * s, + /*@out@*/ int * argcPtr, /*@out@*/ const char *** argvPtr); +/*@observer@*/ const char *poptStrerror(const int error); +void poptSetExecPath(poptContext con, const char * path, int allowAbsolute); +void poptPrintHelp(poptContext con, FILE * f, int flags); +void poptPrintUsage(poptContext con, FILE * f, int flags); +void poptSetOtherOptionHelp(poptContext con, const char * text); +/*@observer@*/ const char * poptGetInvocationName(poptContext con); +/* shuffles argv pointers to remove stripped args, returns new argc */ +int poptStrippedArgv(poptContext con, int argc, char **argv); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/source4/lib/popt/poptconfig.c b/source4/lib/popt/poptconfig.c new file mode 100644 index 0000000000..eb76941363 --- /dev/null +++ b/source4/lib/popt/poptconfig.c @@ -0,0 +1,142 @@ +/* (C) 1998 Red Hat Software, Inc. -- Licensing details are in the COPYING + file accompanying popt source distributions, available from + ftp://ftp.redhat.com/pub/code/popt */ + +#include "system.h" +#include "poptint.h" + +static void configLine(poptContext con, char * line) { + int nameLength = strlen(con->appName); + char * opt; + struct poptAlias alias; + char * entryType; + char * longName = NULL; + char shortName = '\0'; + + if (strncmp(line, con->appName, nameLength)) return; + line += nameLength; + if (!*line || !isspace(*line)) return; + while (*line && isspace(*line)) line++; + entryType = line; + + while (!*line || !isspace(*line)) line++; + *line++ = '\0'; + while (*line && isspace(*line)) line++; + if (!*line) return; + opt = line; + + while (!*line || !isspace(*line)) line++; + *line++ = '\0'; + while (*line && isspace(*line)) line++; + if (!*line) return; + + if (opt[0] == '-' && opt[1] == '-') + longName = opt + 2; + else if (opt[0] == '-' && !opt[2]) + shortName = opt[1]; + + if (!strcmp(entryType, "alias")) { + if (poptParseArgvString(line, &alias.argc, &alias.argv)) return; + alias.longName = longName, alias.shortName = shortName; + poptAddAlias(con, alias, 0); + } else if (!strcmp(entryType, "exec")) { + con->execs = realloc(con->execs, + sizeof(*con->execs) * (con->numExecs + 1)); + if (longName) + con->execs[con->numExecs].longName = xstrdup(longName); + else + con->execs[con->numExecs].longName = NULL; + + con->execs[con->numExecs].shortName = shortName; + con->execs[con->numExecs].script = xstrdup(line); + + con->numExecs++; + } +} + +int poptReadConfigFile(poptContext con, const char * fn) { + char * file=NULL, * chptr, * end; + char * buf=NULL, * dst; + int fd, rc; + int fileLength; + + fd = open(fn, O_RDONLY); + if (fd < 0) { + if (errno == ENOENT) + return 0; + else + return POPT_ERROR_ERRNO; + } + + fileLength = lseek(fd, 0, SEEK_END); + (void) lseek(fd, 0, 0); + + file = malloc(fileLength + 1); + if (read(fd, file, fileLength) != fileLength) { + rc = errno; + close(fd); + errno = rc; + if (file) free(file); + return POPT_ERROR_ERRNO; + } + close(fd); + + dst = buf = malloc(fileLength + 1); + + chptr = file; + end = (file + fileLength); + while (chptr < end) { + switch (*chptr) { + case '\n': + *dst = '\0'; + dst = buf; + while (*dst && isspace(*dst)) dst++; + if (*dst && *dst != '#') { + configLine(con, dst); + } + chptr++; + break; + case '\\': + *dst++ = *chptr++; + if (chptr < end) { + if (*chptr == '\n') + dst--, chptr++; + /* \ at the end of a line does not insert a \n */ + else + *dst++ = *chptr++; + } + break; + default: + *dst++ = *chptr++; + break; + } + } + + free(file); + free(buf); + + return 0; +} + +int poptReadDefaultConfig(poptContext con, /*@unused@*/ int useEnv) { + char * fn, * home; + int rc; + + if (!con->appName) return 0; + + rc = poptReadConfigFile(con, "/etc/popt"); + if (rc) return rc; + if (getuid() != geteuid()) return 0; + + if ((home = getenv("HOME"))) { + fn = malloc(strlen(home) + 20); + strcpy(fn, home); + strcat(fn, "/.popt"); + rc = poptReadConfigFile(con, fn); + free(fn); + if (rc) return rc; + } + + return 0; +} + diff --git a/source4/lib/popt/popthelp.c b/source4/lib/popt/popthelp.c new file mode 100644 index 0000000000..6b790a63e7 --- /dev/null +++ b/source4/lib/popt/popthelp.c @@ -0,0 +1,301 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 4 -*- */ + +/* (C) 1998 Red Hat Software, Inc. -- Licensing details are in the COPYING + file accompanying popt source distributions, available from + ftp://ftp.redhat.com/pub/code/popt */ + +#include "system.h" +#include "poptint.h" + +static void displayArgs(poptContext con, + /*@unused@*/ enum poptCallbackReason foo, + struct poptOption * key, + /*@unused@*/ const char * arg, /*@unused@*/ void * data) { + if (key->shortName== '?') + poptPrintHelp(con, stdout, 0); + else + poptPrintUsage(con, stdout, 0); + exit(0); +} + +struct poptOption poptHelpOptions[] = { + { NULL, '\0', POPT_ARG_CALLBACK, (void *)&displayArgs, '\0', NULL, NULL }, + { "help", '?', 0, NULL, '?', N_("Show this help message"), NULL }, + { "usage", '\0', 0, NULL, 'u', N_("Display brief usage message"), NULL }, + { NULL, '\0', 0, NULL, 0, NULL, NULL } +} ; + + +/*@observer@*/ /*@null@*/ static const char * +getTableTranslationDomain(const struct poptOption *table) +{ + const struct poptOption *opt; + + for(opt = table; + opt->longName || opt->shortName || opt->arg; + opt++) { + if(opt->argInfo == POPT_ARG_INTL_DOMAIN) + return opt->arg; + } + + return NULL; +} + +/*@observer@*/ /*@null@*/ static const char * +getArgDescrip(const struct poptOption * opt, const char *translation_domain) +{ + if (!(opt->argInfo & POPT_ARG_MASK)) return NULL; + + if (opt == (poptHelpOptions + 1) || opt == (poptHelpOptions + 2)) + if (opt->argDescrip) return POPT_(opt->argDescrip); + + if (opt->argDescrip) return D_(translation_domain, opt->argDescrip); + return POPT_("ARG"); +} + +static void singleOptionHelp(FILE * f, int maxLeftCol, + const struct poptOption * opt, + const char *translation_domain) { + int indentLength = maxLeftCol + 5; + int lineLength = 79 - indentLength; + const char * help = D_(translation_domain, opt->descrip); + int helpLength; + const char * ch; + char format[10]; + char * left; + const char * argDescrip = getArgDescrip(opt, translation_domain); + + left = malloc(maxLeftCol + 1); + *left = '\0'; + + if (opt->longName && opt->shortName) + sprintf(left, "-%c, --%s", opt->shortName, opt->longName); + else if (opt->shortName) + sprintf(left, "-%c", opt->shortName); + else if (opt->longName) + sprintf(left, "--%s", opt->longName); + if (!*left) return ; + if (argDescrip) { + strcat(left, "="); + strcat(left, argDescrip); + } + + if (help) + fprintf(f," %-*s ", maxLeftCol, left); + else { + fprintf(f," %s\n", left); + goto out; + } + + helpLength = strlen(help); + while (helpLength > lineLength) { + ch = help + lineLength - 1; + while (ch > help && !isspace(*ch)) ch--; + if (ch == help) break; /* give up */ + while (ch > (help + 1) && isspace(*ch)) ch--; + ch++; + + sprintf(format, "%%.%ds\n%%%ds", (int) (ch - help), indentLength); + fprintf(f, format, help, " "); + help = ch; + while (isspace(*help) && *help) help++; + helpLength = strlen(help); + } + + if (helpLength) fprintf(f, "%s\n", help); + +out: + free(left); +} + +static int maxArgWidth(const struct poptOption * opt, + const char * translation_domain) { + int max = 0; + int this; + const char * s; + + while (opt->longName || opt->shortName || opt->arg) { + if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) { + this = maxArgWidth(opt->arg, translation_domain); + if (this > max) max = this; + } else if (!(opt->argInfo & POPT_ARGFLAG_DOC_HIDDEN)) { + this = opt->shortName ? 2 : 0; + if (opt->longName) { + if (this) this += 2; + this += strlen(opt->longName) + 2; + } + + s = getArgDescrip(opt, translation_domain); + if (s) + this += strlen(s) + 1; + if (this > max) max = this; + } + + opt++; + } + + return max; +} + +static void singleTableHelp(FILE * f, const struct poptOption * table, + int left, + const char *translation_domain) { + const struct poptOption * opt; + const char *sub_transdom; + + opt = table; + while (opt->longName || opt->shortName || opt->arg) { + if ((opt->longName || opt->shortName) && + !(opt->argInfo & POPT_ARGFLAG_DOC_HIDDEN)) + singleOptionHelp(f, left, opt, translation_domain); + opt++; + } + + opt = table; + while (opt->longName || opt->shortName || opt->arg) { + if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) { + sub_transdom = getTableTranslationDomain(opt->arg); + if(!sub_transdom) + sub_transdom = translation_domain; + + if (opt->descrip) + fprintf(f, "\n%s\n", D_(sub_transdom, opt->descrip)); + + singleTableHelp(f, opt->arg, left, sub_transdom); + } + opt++; + } +} + +static int showHelpIntro(poptContext con, FILE * f) { + int len = 6; + const char * fn; + + fprintf(f, POPT_("Usage:")); + if (!(con->flags & POPT_CONTEXT_KEEP_FIRST)) { + fn = con->optionStack->argv[0]; + if (strchr(fn, '/')) fn = strchr(fn, '/') + 1; + fprintf(f, " %s", fn); + len += strlen(fn) + 1; + } + + return len; +} + +void poptPrintHelp(poptContext con, FILE * f, /*@unused@*/ int flags) { + int leftColWidth; + + showHelpIntro(con, f); + if (con->otherHelp) + fprintf(f, " %s\n", con->otherHelp); + else + fprintf(f, " %s\n", POPT_("[OPTION...]")); + + leftColWidth = maxArgWidth(con->options, NULL); + singleTableHelp(f, con->options, leftColWidth, NULL); +} + +static int singleOptionUsage(FILE * f, int cursor, + const struct poptOption * opt, + const char *translation_domain) { + int len = 3; + char shortStr[2] = { '\0', '\0' }; + const char * item = shortStr; + const char * argDescrip = getArgDescrip(opt, translation_domain); + + if (opt->shortName) { + if (!(opt->argInfo & POPT_ARG_MASK)) + return cursor; /* we did these already */ + len++; + *shortStr = opt->shortName; + shortStr[1] = '\0'; + } else if (opt->longName) { + len += 1 + strlen(opt->longName); + item = opt->longName; + } + + if (len == 3) return cursor; + + if (argDescrip) + len += strlen(argDescrip) + 1; + + if ((cursor + len) > 79) { + fprintf(f, "\n "); + cursor = 7; + } + + fprintf(f, " [-%s%s%s%s]", opt->shortName ? "" : "-", item, + argDescrip ? (opt->shortName ? " " : "=") : "", + argDescrip ? argDescrip : ""); + + return cursor + len + 1; +} + +static int singleTableUsage(FILE * f, int cursor, const struct poptOption * table, + const char *translation_domain) { + const struct poptOption * opt; + + opt = table; + while (opt->longName || opt->shortName || opt->arg) { + if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INTL_DOMAIN) + translation_domain = (const char *)opt->arg; + else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) + cursor = singleTableUsage(f, cursor, opt->arg, + translation_domain); + else if ((opt->longName || opt->shortName) && + !(opt->argInfo & POPT_ARGFLAG_DOC_HIDDEN)) + cursor = singleOptionUsage(f, cursor, opt, translation_domain); + + opt++; + } + + return cursor; +} + +static int showShortOptions(const struct poptOption * opt, FILE * f, + char * str) { + char s[300]; /* this is larger then the ascii set, so + it should do just fine */ + + s[0] = '\0'; + if (str == NULL) { + memset(s, 0, sizeof(s)); + str = s; + } + + while (opt->longName || opt->shortName || opt->arg) { + if (opt->shortName && !(opt->argInfo & POPT_ARG_MASK)) + str[strlen(str)] = opt->shortName; + else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) + showShortOptions(opt->arg, f, str); + + opt++; + } + + if (s != str || !*s) + return 0; + + fprintf(f, " [-%s]", s); + return strlen(s) + 4; +} + +void poptPrintUsage(poptContext con, FILE * f, /*@unused@*/ int flags) { + int cursor; + + cursor = showHelpIntro(con, f); + cursor += showShortOptions(con->options, f, NULL); + singleTableUsage(f, cursor, con->options, NULL); + + if (con->otherHelp) { + cursor += strlen(con->otherHelp) + 1; + if (cursor > 79) fprintf(f, "\n "); + fprintf(f, " %s", con->otherHelp); + } + + fprintf(f, "\n"); +} + +void poptSetOtherOptionHelp(poptContext con, const char * text) { + if (con->otherHelp) xfree(con->otherHelp); + con->otherHelp = xstrdup(text); +} diff --git a/source4/lib/popt/poptint.h b/source4/lib/popt/poptint.h new file mode 100644 index 0000000000..1847ffafe6 --- /dev/null +++ b/source4/lib/popt/poptint.h @@ -0,0 +1,71 @@ +/* (C) 1998 Red Hat Software, Inc. -- Licensing details are in the COPYING + file accompanying popt source distributions, available from + ftp://ftp.redhat.com/pub/code/popt */ + +#ifndef H_POPTINT +#define H_POPTINT + +/* Bit mask macros. */ +typedef unsigned int __pbm_bits; +#define __PBM_NBITS (8 * sizeof (__pbm_bits)) +#define __PBM_IX(d) ((d) / __PBM_NBITS) +#define __PBM_MASK(d) ((__pbm_bits) 1 << ((d) % __PBM_NBITS)) +typedef struct { + __pbm_bits bits[1]; +} pbm_set; +#define __PBM_BITS(set) ((set)->bits) + +#define PBM_ALLOC(d) calloc(__PBM_IX (d) + 1, sizeof(__pbm_bits)) +#define PBM_FREE(s) free(s); +#define PBM_SET(d, s) (__PBM_BITS (s)[__PBM_IX (d)] |= __PBM_MASK (d)) +#define PBM_CLR(d, s) (__PBM_BITS (s)[__PBM_IX (d)] &= ~__PBM_MASK (d)) +#define PBM_ISSET(d, s) ((__PBM_BITS (s)[__PBM_IX (d)] & __PBM_MASK (d)) != 0) + +struct optionStackEntry { + int argc; + /*@only@*/ const char ** argv; + /*@only@*/ pbm_set * argb; + int next; + /*@only@*/ const char * nextArg; + /*@keep@*/ const char * nextCharArg; + /*@dependent@*/ struct poptAlias * currAlias; + int stuffed; +}; + +struct execEntry { + const char * longName; + char shortName; + const char * script; +}; + +struct poptContext_s { + struct optionStackEntry optionStack[POPT_OPTION_DEPTH]; + /*@dependent@*/ struct optionStackEntry * os; + /*@owned@*/ const char ** leftovers; + int numLeftovers; + int nextLeftover; + /*@keep@*/ const struct poptOption * options; + int restLeftover; + /*@only@*/ const char * appName; + /*@only@*/ struct poptAlias * aliases; + int numAliases; + int flags; + struct execEntry * execs; + int numExecs; + /*@only@*/ const char ** finalArgv; + int finalArgvCount; + int finalArgvAlloced; + /*@dependent@*/ struct execEntry * doExec; + /*@only@*/ const char * execPath; + int execAbsolute; + /*@only@*/ const char * otherHelp; + pbm_set * arg_strip; +}; + +#define xfree(_a) free((void *)_a) + +#define POPT_(foo) (foo) +#define D_(dom, str) (str) +#define N_(foo) (foo) + +#endif diff --git a/source4/lib/popt/poptparse.c b/source4/lib/popt/poptparse.c new file mode 100644 index 0000000000..8f00769be9 --- /dev/null +++ b/source4/lib/popt/poptparse.c @@ -0,0 +1,102 @@ +/* (C) 1998 Red Hat Software, Inc. -- Licensing details are in the COPYING + file accompanying popt source distributions, available from + ftp://ftp.redhat.com/pub/code/popt */ + +#include "system.h" + +#define POPT_ARGV_ARRAY_GROW_DELTA 5 + +int poptDupArgv(int argc, const char **argv, + int * argcPtr, const char *** argvPtr) +{ + size_t nb = (argc + 1) * sizeof(*argv); + const char ** argv2; + char * dst; + int i; + + for (i = 0; i < argc; i++) { + if (argv[i] == NULL) + return POPT_ERROR_NOARG; + nb += strlen(argv[i]) + 1; + } + + dst = malloc(nb); + argv2 = (void *) dst; + dst += (argc + 1) * sizeof(*argv); + + for (i = 0; i < argc; i++) { + argv2[i] = dst; + dst += strlen(strcpy(dst, argv[i])) + 1; + } + argv2[argc] = NULL; + + *argvPtr = argv2; + *argcPtr = argc; + return 0; +} + +int poptParseArgvString(const char * s, int * argcPtr, const char *** argvPtr) +{ + const char * src; + char quote = '\0'; + int argvAlloced = POPT_ARGV_ARRAY_GROW_DELTA; + const char ** argv = malloc(sizeof(*argv) * argvAlloced); + int argc = 0; + int buflen = strlen(s) + 1; + char *buf0 = calloc(buflen, 1); + char *buf = buf0; + + argv[argc] = buf; + + for (src = s; *src; src++) { + if (quote == *src) { + quote = '\0'; + } else if (quote) { + if (*src == '\\') { + src++; + if (!*src) { + free(argv); + free(buf0); + return POPT_ERROR_BADQUOTE; + } + if (*src != quote) *buf++ = '\\'; + } + *buf++ = *src; + } else if (isspace(*src)) { + if (*argv[argc]) { + buf++, argc++; + if (argc == argvAlloced) { + argvAlloced += POPT_ARGV_ARRAY_GROW_DELTA; + argv = realloc(argv, sizeof(*argv) * argvAlloced); + } + argv[argc] = buf; + } + } else switch (*src) { + case '"': + case '\'': + quote = *src; + break; + case '\\': + src++; + if (!*src) { + free(argv); + free(buf0); + return POPT_ERROR_BADQUOTE; + } + /*@fallthrough@*/ + default: + *buf++ = *src; + break; + } + } + + if (strlen(argv[argc])) { + argc++, buf++; + } + + (void) poptDupArgv(argc, argv, argcPtr, argvPtr); + + free(argv); + free(buf0); + return 0; +} diff --git a/source4/lib/popt/system.h b/source4/lib/popt/system.h new file mode 100644 index 0000000000..059c045817 --- /dev/null +++ b/source4/lib/popt/system.h @@ -0,0 +1,53 @@ +#include "config.h" + +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <limits.h> + +#if HAVE_MCHECK_H +#include <mcheck.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#if HAVE_UNISTD_H +#include <unistd.h> +#endif + +#ifdef __NeXT +/* access macros are not declared in non posix mode in unistd.h - + don't try to use posix on NeXTstep 3.3 ! */ +#include <libc.h> +#endif + +/* AIX requires this to be the first thing in the file. */ +#ifndef __GNUC__ +# if HAVE_ALLOCA_H +# include <alloca.h> +# else +# ifdef _AIX +#pragma alloca +# else +# ifndef alloca /* predefined by HP cc +Olibcalls */ +char *alloca (); +# endif +# endif +# endif +#elif defined(__GNUC__) && defined(__STRICT_ANSI__) +#define alloca __builtin_alloca +#endif + +/*@only@*/ char * xstrdup (const char *str); + +#if HAVE_MCHECK_H && defined(__GNUC__) +#define vmefail() (fprintf(stderr, "virtual memory exhausted.\n"), exit(EXIT_FAILURE), NULL) +#define xstrdup(_str) (strcpy((malloc(strlen(_str)+1) ? : vmefail()), (_str))) +#else +#define xstrdup(_str) strdup(_str) +#endif /* HAVE_MCHECK_H && defined(__GNUC__) */ + + +#include "popt.h" diff --git a/source4/lib/popt_common.c b/source4/lib/popt_common.c new file mode 100644 index 0000000000..ccdecc91cc --- /dev/null +++ b/source4/lib/popt_common.c @@ -0,0 +1,327 @@ +/* + Unix SMB/CIFS implementation. + Common popt routines + + Copyright (C) Tim Potter 2001,2002 + Copyright (C) Jelmer Vernooij 2002,2003 + + 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" + +/* Handle command line options: + * -d,--debuglevel + * -s,--configfile + * -O,--socket-options + * -V,--version + * -l,--log-base + * -n,--netbios-name + * -W,--workgroup + * -i,--scope + */ + +extern pstring user_socket_options; +extern BOOL AllowDebugChange; + +struct user_auth_info cmdline_auth_info; + +static void popt_common_callback(poptContext con, + enum poptCallbackReason reason, + const struct poptOption *opt, + const char *arg, const void *data) +{ + pstring logfile; + const char *pname; + + /* Find out basename of current program */ + pname = strrchr_m(poptGetInvocationName(con),'/'); + + if (!pname) + pname = poptGetInvocationName(con); + else + pname++; + + if (reason == POPT_CALLBACK_REASON_PRE) { + pstr_sprintf(logfile, "%s/log.%s", dyn_LOGFILEBASE, pname); + lp_set_cmdline("log file", logfile); + return; + } + + switch(opt->val) { + case 'd': + lp_set_cmdline("log level", arg); + break; + + case 'V': + printf( "Version %s\n", SAMBA_VERSION ); + exit(0); + break; + + case 's': + if (arg) { + pstrcpy(dyn_CONFIGFILE, arg); + } + break; + + case 'l': + if (arg) { + pstr_sprintf(logfile, "%s/log.%s", arg, pname); + lp_set_cmdline("log file", logfile); + } + break; + + case 'W': + lp_set_cmdline("workgroup", arg); + break; + + case 'n': + lp_set_cmdline("netbios name", arg); + break; + + case 'i': + lp_set_cmdline("netbios scope", arg); + break; + } +} + +struct poptOption popt_common_connection[] = { + { NULL, 0, POPT_ARG_CALLBACK, popt_common_callback }, + { "socket-options", 'O', POPT_ARG_STRING, NULL, 'O', "socket options to use", + "SOCKETOPTIONS" }, + { "netbiosname", 'n', POPT_ARG_STRING, NULL, 'n', "Primary netbios name", "NETBIOSNAME" }, + { "workgroup", 'W', POPT_ARG_STRING, NULL, 'W', "Set the workgroup name", "WORKGROUP" }, + { "scope", 'i', POPT_ARG_STRING, NULL, 'i', "Use this Netbios scope", "SCOPE" }, + POPT_TABLEEND +}; + +struct poptOption popt_common_samba[] = { + { NULL, 0, POPT_ARG_CALLBACK|POPT_CBFLAG_PRE, popt_common_callback }, + { "debuglevel", 'd', POPT_ARG_STRING, NULL, 'd', "Set debug level", "DEBUGLEVEL" }, + { "configfile", 's', POPT_ARG_STRING, NULL, 's', "Use alternative configuration file", "CONFIGFILE" }, + { "log-basename", 'l', POPT_ARG_STRING, NULL, 'l', "Basename for log/debug files", "LOGFILEBASE" }, + { "version", 'V', POPT_ARG_NONE, NULL, 'V', "Print version" }, + POPT_TABLEEND +}; + +struct poptOption popt_common_version[] = { + { NULL, 0, POPT_ARG_CALLBACK, popt_common_callback }, + { "version", 'V', POPT_ARG_NONE, NULL, 'V', "Print version" }, + POPT_TABLEEND +}; + + + +/**************************************************************************** + * get a password from a a file or file descriptor + * exit on failure + * ****************************************************************************/ +static void get_password_file(struct user_auth_info *a) +{ + int fd = -1; + char *p; + BOOL close_it = False; + pstring spec; + char pass[128]; + + if ((p = getenv("PASSWD_FD")) != NULL) { + pstrcpy(spec, "descriptor "); + pstrcat(spec, p); + sscanf(p, "%d", &fd); + close_it = False; + } else if ((p = getenv("PASSWD_FILE")) != NULL) { + fd = sys_open(p, O_RDONLY, 0); + pstrcpy(spec, p); + if (fd < 0) { + fprintf(stderr, "Error opening PASSWD_FILE %s: %s\n", + spec, strerror(errno)); + exit(1); + } + close_it = True; + } + + for(p = pass, *p = '\0'; /* ensure that pass is null-terminated */ + p && p - pass < sizeof(pass);) { + switch (read(fd, p, 1)) { + case 1: + if (*p != '\n' && *p != '\0') { + *++p = '\0'; /* advance p, and null-terminate pass */ + break; + } + case 0: + if (p - pass) { + *p = '\0'; /* null-terminate it, just in case... */ + p = NULL; /* then force the loop condition to become false */ + break; + } else { + fprintf(stderr, "Error reading password from file %s: %s\n", + spec, "empty password\n"); + exit(1); + } + + default: + fprintf(stderr, "Error reading password from file %s: %s\n", + spec, strerror(errno)); + exit(1); + } + } + pstrcpy(a->password, pass); + if (close_it) + close(fd); +} + +static void get_credentials_file(const char *file, struct user_auth_info *info) +{ + XFILE *auth; + fstring buf; + uint16 len = 0; + char *ptr, *val, *param; + + if ((auth=x_fopen(file, O_RDONLY, 0)) == NULL) + { + /* fail if we can't open the credentials file */ + d_printf("ERROR: Unable to open credentials file!\n"); + exit(-1); + } + + while (!x_feof(auth)) + { + /* get a line from the file */ + if (!x_fgets(buf, sizeof(buf), auth)) + continue; + len = strlen(buf); + + if ((len) && (buf[len-1]=='\n')) + { + buf[len-1] = '\0'; + len--; + } + if (len == 0) + continue; + + /* break up the line into parameter & value. + * will need to eat a little whitespace possibly */ + param = buf; + if (!(ptr = strchr_m (buf, '='))) + continue; + + val = ptr+1; + *ptr = '\0'; + + /* eat leading white space */ + while ((*val!='\0') && ((*val==' ') || (*val=='\t'))) + val++; + + if (strwicmp("password", param) == 0) + { + pstrcpy(info->password, val); + info->got_pass = True; + } + else if (strwicmp("username", param) == 0) + pstrcpy(info->username, val); + //else if (strwicmp("domain", param) == 0) + // set_global_myworkgroup(val); + memset(buf, 0, sizeof(buf)); + } + x_fclose(auth); +} + +/* Handle command line options: + * -U,--user + * -A,--authentication-file + * -k,--use-kerberos + * -N,--no-pass + */ + + +static void popt_common_credentials_callback(poptContext con, + enum poptCallbackReason reason, + const struct poptOption *opt, + const char *arg, const void *data) +{ + char *p; + + if (reason == POPT_CALLBACK_REASON_PRE) { + cmdline_auth_info.use_kerberos = False; + cmdline_auth_info.got_pass = False; + pstrcpy(cmdline_auth_info.username, "GUEST"); + + if (getenv("LOGNAME"))pstrcpy(cmdline_auth_info.username,getenv("LOGNAME")); + + if (getenv("USER")) { + pstrcpy(cmdline_auth_info.username,getenv("USER")); + + if ((p = strchr_m(cmdline_auth_info.username,'%'))) { + *p = 0; + pstrcpy(cmdline_auth_info.password,p+1); + cmdline_auth_info.got_pass = True; + memset(strchr_m(getenv("USER"),'%')+1,'X',strlen(cmdline_auth_info.password)); + } + } + + if (getenv("PASSWD")) { + pstrcpy(cmdline_auth_info.password,getenv("PASSWD")); + cmdline_auth_info.got_pass = True; + } + + if (getenv("PASSWD_FD") || getenv("PASSWD_FILE")) { + get_password_file(&cmdline_auth_info); + cmdline_auth_info.got_pass = True; + } + + return; + } + + switch(opt->val) { + case 'U': + { + char *lp; + + pstrcpy(cmdline_auth_info.username,arg); + if ((lp=strchr_m(cmdline_auth_info.username,'%'))) { + *lp = 0; + pstrcpy(cmdline_auth_info.password,lp+1); + cmdline_auth_info.got_pass = True; + memset(strchr_m(arg,'%')+1,'X',strlen(cmdline_auth_info.password)); + } + } + break; + + case 'A': + get_credentials_file(arg, &cmdline_auth_info); + break; + + case 'k': +#ifndef HAVE_KRB5 + d_printf("No kerberos support compiled in\n"); + exit(1); +#else + cmdline_auth_info.use_kerberos = True; + cmdline_auth_info.got_pass = True; +#endif + break; + } +} + + + +struct poptOption popt_common_credentials[] = { + { NULL, 0, POPT_ARG_CALLBACK|POPT_CBFLAG_PRE, popt_common_credentials_callback }, + { "user", 'U', POPT_ARG_STRING, NULL, 'U', "Set the network username", "USERNAME" }, + { "no-pass", 'N', POPT_ARG_NONE, &cmdline_auth_info.got_pass, True, "Don't ask for a password" }, + { "kerberos", 'k', POPT_ARG_NONE, &cmdline_auth_info.use_kerberos, True, "Use kerberos (active directory) authentication" }, + { "authentication-file", 'A', POPT_ARG_STRING, NULL, 'A', "Get the credentials from a file", "FILE" }, + POPT_TABLEEND +}; diff --git a/source4/lib/readline.c b/source4/lib/readline.c new file mode 100644 index 0000000000..c5da88b3e0 --- /dev/null +++ b/source4/lib/readline.c @@ -0,0 +1,159 @@ +/* + Unix SMB/CIFS implementation. + Samba readline wrapper implementation + Copyright (C) Simo Sorce 2001 + Copyright (C) Andrew Tridgell 2001 + + 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" + +#ifdef HAVE_LIBREADLINE +# ifdef HAVE_READLINE_READLINE_H +# include <readline/readline.h> +# ifdef HAVE_READLINE_HISTORY_H +# include <readline/history.h> +# endif +# else +# ifdef HAVE_READLINE_H +# include <readline.h> +# ifdef HAVE_HISTORY_H +# include <history.h> +# endif +# else +# undef HAVE_LIBREADLINE +# endif +# endif +#endif + +#ifdef HAVE_NEW_LIBREADLINE +# define RL_COMPLETION_CAST (rl_completion_func_t *) +#else +/* This type is missing from libreadline<4.0 (approximately) */ +# define RL_COMPLETION_CAST +#endif /* HAVE_NEW_LIBREADLINE */ + +/**************************************************************************** + Display the prompt and wait for input. Call callback() regularly +****************************************************************************/ + +static char *smb_readline_replacement(const char *prompt, void (*callback)(void), + char **(completion_fn)(const char *text, int start, int end)) +{ + fd_set fds; + static pstring line; + struct timeval timeout; + int fd = x_fileno(x_stdin); + char *ret; + + do_debug("%s", prompt); + + while (1) { + timeout.tv_sec = 5; + timeout.tv_usec = 0; + + FD_ZERO(&fds); + FD_SET(fd,&fds); + + if (sys_select_intr(fd+1,&fds,NULL,NULL,&timeout) == 1) { + ret = x_fgets(line, sizeof(line), x_stdin); + return ret; + } + if (callback) + callback(); + } +} + +/**************************************************************************** + Display the prompt and wait for input. Call callback() regularly. +****************************************************************************/ + +char *smb_readline(const char *prompt, void (*callback)(void), + char **(completion_fn)(const char *text, int start, int end)) +{ +#if HAVE_LIBREADLINE + if (isatty(x_fileno(x_stdin))) { + char *ret; + + /* Aargh! Readline does bizzare things with the terminal width + that mucks up expect(1). Set CLI_NO_READLINE in the environment + to force readline not to be used. */ + + if (getenv("CLI_NO_READLINE")) + return smb_readline_replacement(prompt, callback, completion_fn); + + if (completion_fn) { + /* The callback prototype has changed slightly between + different versions of Readline, so the same function + works in all of them to date, but we get compiler + warnings in some. */ + rl_attempted_completion_function = RL_COMPLETION_CAST completion_fn; + } + + if (callback) + rl_event_hook = (Function *)callback; + ret = readline(prompt); + if (ret && *ret) + add_history(ret); + return ret; + } else +#endif + return smb_readline_replacement(prompt, callback, completion_fn); +} + +/**************************************************************************** + * return line buffer text + ****************************************************************************/ +const char *smb_readline_get_line_buffer(void) +{ +#if defined(HAVE_LIBREADLINE) + return rl_line_buffer; +#else + return NULL; +#endif +} + +/**************************************************************************** + * set completion append character + ***************************************************************************/ +void smb_readline_ca_char(char c) +{ +#if defined(HAVE_LIBREADLINE) + rl_completion_append_character = c; +#endif +} + + +/**************************************************************************** +history +****************************************************************************/ +int cmd_history(void) +{ +#if defined(HAVE_LIBREADLINE) + HIST_ENTRY **hlist; + int i; + + hlist = history_list(); + + for (i = 0; hlist && hlist[i]; i++) { + DEBUG(0, ("%d: %s\n", i, hlist[i]->line)); + } +#else + DEBUG(0,("no history without readline support\n")); +#endif + + return 0; +} diff --git a/source4/lib/replace.c b/source4/lib/replace.c new file mode 100644 index 0000000000..cda379c63f --- /dev/null +++ b/source4/lib/replace.c @@ -0,0 +1,467 @@ +/* + Unix SMB/CIFS implementation. + replacement routines for broken systems + Copyright (C) Andrew Tridgell 1992-1998 + + 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" + + void replace_dummy(void); + void replace_dummy(void) {} + +#ifndef HAVE_FTRUNCATE + /******************************************************************* +ftruncate for operating systems that don't have it +********************************************************************/ + int ftruncate(int f,SMB_OFF_T l) +{ + struct flock fl; + + fl.l_whence = 0; + fl.l_len = 0; + fl.l_start = l; + fl.l_type = F_WRLCK; + return fcntl(f, F_FREESP, &fl); +} +#endif /* HAVE_FTRUNCATE */ + + +#ifndef HAVE_STRLCPY +/* like strncpy but does not 0 fill the buffer and always null + terminates. bufsize is the size of the destination buffer */ + size_t strlcpy(char *d, const char *s, size_t bufsize) +{ + size_t len = strlen(s); + size_t ret = len; + if (bufsize <= 0) return 0; + if (len >= bufsize) len = bufsize-1; + memcpy(d, s, len); + d[len] = 0; + return ret; +} +#endif + +#ifndef HAVE_STRLCAT +/* like strncat but does not 0 fill the buffer and always null + terminates. bufsize is the length of the buffer, which should + be one more than the maximum resulting string length */ + size_t strlcat(char *d, const char *s, size_t bufsize) +{ + size_t len1 = strlen(d); + size_t len2 = strlen(s); + size_t ret = len1 + len2; + + if (len1+len2 >= bufsize) { + len2 = bufsize - (len1+1); + } + if (len2 > 0) { + memcpy(d+len1, s, len2); + d[len1+len2] = 0; + } + return ret; +} +#endif + +#ifndef HAVE_MKTIME +/******************************************************************* +a mktime() replacement for those who don't have it - contributed by +C.A. Lademann <cal@zls.com> +Corrections by richard.kettlewell@kewill.com +********************************************************************/ + +#define MINUTE 60 +#define HOUR 60*MINUTE +#define DAY 24*HOUR +#define YEAR 365*DAY + time_t mktime(struct tm *t) +{ + struct tm *u; + time_t epoch = 0; + int n; + int mon [] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, + y, m, i; + + if(t->tm_year < 70) + return((time_t)-1); + + n = t->tm_year + 1900 - 1; + epoch = (t->tm_year - 70) * YEAR + + ((n / 4 - n / 100 + n / 400) - (1969 / 4 - 1969 / 100 + 1969 / 400)) * DAY; + + y = t->tm_year + 1900; + m = 0; + + for(i = 0; i < t->tm_mon; i++) { + epoch += mon [m] * DAY; + if(m == 1 && y % 4 == 0 && (y % 100 != 0 || y % 400 == 0)) + epoch += DAY; + + if(++m > 11) { + m = 0; + y++; + } + } + + epoch += (t->tm_mday - 1) * DAY; + epoch += t->tm_hour * HOUR + t->tm_min * MINUTE + t->tm_sec; + + if((u = localtime(&epoch)) != NULL) { + t->tm_sec = u->tm_sec; + t->tm_min = u->tm_min; + t->tm_hour = u->tm_hour; + t->tm_mday = u->tm_mday; + t->tm_mon = u->tm_mon; + t->tm_year = u->tm_year; + t->tm_wday = u->tm_wday; + t->tm_yday = u->tm_yday; + t->tm_isdst = u->tm_isdst; + } + + return(epoch); +} +#endif /* !HAVE_MKTIME */ + + + +#ifndef HAVE_RENAME +/* Rename a file. (from libiberty in GNU binutils) */ + int rename(const char *zfrom, const char *zto) +{ + if (link (zfrom, zto) < 0) + { + if (errno != EEXIST) + return -1; + if (unlink (zto) < 0 + || link (zfrom, zto) < 0) + return -1; + } + return unlink (zfrom); +} +#endif /* HAVE_RENAME */ + + +#ifndef HAVE_INNETGR +#if defined(HAVE_SETNETGRENT) && defined(HAVE_GETNETGRENT) && defined(HAVE_ENDNETGRENT) +/* + * Search for a match in a netgroup. This replaces it on broken systems. + */ + int innetgr(const char *group,const char *host,const char *user,const char *dom) +{ + char *hst, *usr, *dm; + + setnetgrent(group); + while (getnetgrent(&hst, &usr, &dm)) { + if (((host == 0) || (hst == 0) || !strcmp(host, hst)) && + ((user == 0) || (usr == 0) || !strcmp(user, usr)) && + ((dom == 0) || (dm == 0) || !strcmp(dom, dm))) { + endnetgrent(); + return (1); + } + } + endnetgrent(); + return (0); +} +#endif /* HAVE_SETNETGRENT HAVE_GETNETGRENT HAVE_ENDNETGRENT */ +#endif /* HAVE_INNETGR */ + + + +#ifndef HAVE_INITGROUPS +/**************************************************************************** + some systems don't have an initgroups call +****************************************************************************/ + int initgroups(char *name,gid_t id) +{ +#ifndef HAVE_SETGROUPS + static int done; + if (!done) { + DEBUG(1,("WARNING: running without setgroups\n")); + done=1; + } + /* yikes! no SETGROUPS or INITGROUPS? how can this work? */ + return(0); +#else /* HAVE_SETGROUPS */ + gid_t *grouplst = NULL; + int max_gr = groups_max(); + int ret; + int i,j; + struct group *g; + char *gr; + + if((grouplst = (gid_t *)malloc(sizeof(gid_t) * max_gr)) == NULL) { + DEBUG(0,("initgroups: malloc fail !\n")); + return -1; + } + + grouplst[0] = id; + i = 1; + while (i < max_gr && ((g = (struct group *)getgrent()) != (struct group *)NULL)) { + if (g->gr_gid == id) + continue; + j = 0; + gr = g->gr_mem[0]; + while (gr && (*gr != (char)NULL)) { + if (strcmp(name,gr) == 0) { + grouplst[i] = g->gr_gid; + i++; + gr = (char *)NULL; + break; + } + gr = g->gr_mem[++j]; + } + } + endgrent(); + ret = sys_setgroups(i,grouplst); + SAFE_FREE(grouplst); + return ret; +#endif /* HAVE_SETGROUPS */ +} +#endif /* HAVE_INITGROUPS */ + + +#if (defined(SecureWare) && defined(SCO)) +/* This is needed due to needing the nap() function but we don't want + to include the Xenix libraries since that will break other things... + BTW: system call # 0x0c28 is the same as calling nap() */ + long nap(long milliseconds) { + return syscall(0x0c28, milliseconds); + } +#endif + + +#ifndef HAVE_MEMMOVE +/******************************************************************* +safely copies memory, ensuring no overlap problems. +this is only used if the machine does not have it's own memmove(). +this is not the fastest algorithm in town, but it will do for our +needs. +********************************************************************/ + void *memmove(void *dest,const void *src,int size) +{ + unsigned long d,s; + int i; + if (dest==src || !size) return(dest); + + d = (unsigned long)dest; + s = (unsigned long)src; + + if ((d >= (s+size)) || (s >= (d+size))) { + /* no overlap */ + memcpy(dest,src,size); + return(dest); + } + + if (d < s) { + /* we can forward copy */ + if (s-d >= sizeof(int) && + !(s%sizeof(int)) && + !(d%sizeof(int)) && + !(size%sizeof(int))) { + /* do it all as words */ + int *idest = (int *)dest; + int *isrc = (int *)src; + size /= sizeof(int); + for (i=0;i<size;i++) idest[i] = isrc[i]; + } else { + /* simplest */ + char *cdest = (char *)dest; + char *csrc = (char *)src; + for (i=0;i<size;i++) cdest[i] = csrc[i]; + } + } else { + /* must backward copy */ + if (d-s >= sizeof(int) && + !(s%sizeof(int)) && + !(d%sizeof(int)) && + !(size%sizeof(int))) { + /* do it all as words */ + int *idest = (int *)dest; + int *isrc = (int *)src; + size /= sizeof(int); + for (i=size-1;i>=0;i--) idest[i] = isrc[i]; + } else { + /* simplest */ + char *cdest = (char *)dest; + char *csrc = (char *)src; + for (i=size-1;i>=0;i--) cdest[i] = csrc[i]; + } + } + return(dest); +} +#endif /* HAVE_MEMMOVE */ + +#ifndef HAVE_STRDUP +/**************************************************************************** +duplicate a string +****************************************************************************/ + char *strdup(const char *s) +{ + size_t len; + char *ret; + + if (!s) return(NULL); + + len = strlen(s)+1; + ret = (char *)malloc(len); + if (!ret) return(NULL); + memcpy(ret,s,len); + return(ret); +} +#endif /* HAVE_STRDUP */ +#if 0 /* REWRITE: not thread safe */ +#ifdef REPLACE_INET_NTOA +char *rep_inet_ntoa(struct in_addr ip) +{ + unsigned char *p = (unsigned char *)&ip.s_addr; + static char buf[18]; + slprintf(buf, 17, "%d.%d.%d.%d", + (int)p[0], (int)p[1], (int)p[2], (int)p[3]); + return buf; +} +#endif /* REPLACE_INET_NTOA */ +#endif +#ifndef HAVE_STRTOUL +#ifndef ULONG_MAX +#define ULONG_MAX ((unsigned long)(~0L)) /* 0xFFFFFFFF */ +#endif + +/* + * Convert a string to an unsigned long integer. + * Taken from libg++ - libiberty code. + * + * Ignores `locale' stuff. Assumes that the upper and lower case + * alphabets and digits are each contiguous. + */ + unsigned long strtoul(const char *nptr, char **endptr, int base) +{ + const char *s = nptr; + unsigned long acc; + int c; + unsigned long cutoff; + int neg = 0, any, cutlim; + + /* + * See strtol for comments as to the logic used. + */ + do { + c = *s++; + } while (isspace(c)); + if (c == '-') { + neg = 1; + c = *s++; + } else if (c == '+') + c = *s++; + if ((base == 0 || base == 16) && + c == '0' && (*s == 'x' || *s == 'X')) { + c = s[1]; + s += 2; + base = 16; + } + if (base == 0) + base = c == '0' ? 8 : 10; + cutoff = (unsigned long)ULONG_MAX / (unsigned long)base; + cutlim = (int)((unsigned long)ULONG_MAX % (unsigned long)base); + for (acc = 0, any = 0;; c = *s++) { + if (isdigit(c)) + c -= '0'; + else if (isalpha(c)) + c -= isupper(c) ? 'A' - 10 : 'a' - 10; + else + break; + if (c >= base) + break; + if (any < 0 || acc > cutoff || acc == cutoff && c > cutlim) + any = -1; + else { + any = 1; + acc *= base; + acc += c; + } + } + if (any < 0) { + acc = ULONG_MAX; + errno = ERANGE; + } else if (neg) + acc = -acc; + if (endptr != 0) + *endptr = (char *) (any ? s - 1 : nptr); + return (acc); +} +#endif /* HAVE_STRTOUL */ + +#ifndef HAVE_SETLINEBUF + int setlinebuf(FILE *stream) +{ + return setvbuf(stream, (char *)NULL, _IOLBF, 0); +} +#endif /* HAVE_SETLINEBUF */ + +#ifndef HAVE_VSYSLOG +#ifdef HAVE_SYSLOG + void vsyslog (int facility_priority, char *format, va_list arglist) +{ + char *msg = NULL; + vasprintf(&msg, format, arglist); + if (!msg) + return; + syslog(facility_priority, "%s", msg); + SAFE_FREE(msg); +} +#endif /* HAVE_SYSLOG */ +#endif /* HAVE_VSYSLOG */ + + +#ifndef HAVE_TIMEGM +/* + yes, I know this looks insane, but its really needed. The function in the + Linux timegm() manpage does not work on solaris. +*/ + time_t timegm(struct tm *tm) +{ + struct tm tm2, tm3; + time_t t; + + tm2 = *tm; + + t = mktime(&tm2); + tm3 = *localtime(&t); + tm2 = *tm; + tm2.tm_isdst = tm3.tm_isdst; + t = mktime(&tm2); + t -= TimeDiff(t); + + return t; +} +#endif + +#ifndef HAVE_SETENV + int setenv(const char *name, const char *value, int overwrite) +{ + char *p = NULL; + int ret = -1; + + asprintf(&p, "%s=%s", name, value); + + if (overwrite || getenv(name)) { + if (p) ret = putenv(p); + } else { + ret = 0; + } + + return ret; +} +#endif diff --git a/source4/lib/select.c b/source4/lib/select.c new file mode 100644 index 0000000000..5d7e4a0ad2 --- /dev/null +++ b/source4/lib/select.c @@ -0,0 +1,159 @@ +/* + Unix SMB/Netbios implementation. + Version 3.0 + Samba select/poll implementation + Copyright (C) Andrew Tridgell 1992-1998 + + 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" + +/* This is here because it allows us to avoid a nasty race in signal handling. + We need to guarantee that when we get a signal we get out of a select immediately + but doing that involves a race condition. We can avoid the race by getting the + signal handler to write to a pipe that is in the select/poll list + + This means all Samba signal handlers should call sys_select_signal(). +*/ + +static pid_t initialised; +static int select_pipe[2]; +static VOLATILE unsigned pipe_written, pipe_read; + +/******************************************************************* + Call this from all Samba signal handlers if you want to avoid a + nasty signal race condition. +********************************************************************/ + +void sys_select_signal(void) +{ + char c = 1; + if (!initialised) return; + + if (pipe_written > pipe_read+256) return; + + if (write(select_pipe[1], &c, 1) == 1) pipe_written++; +} + +/******************************************************************* + Like select() but avoids the signal race using a pipe + it also guuarantees that fds on return only ever contains bits set + for file descriptors that were readable. +********************************************************************/ + +int sys_select(int maxfd, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *tval) +{ + int ret, saved_errno; + fd_set *readfds2, readfds_buf; + + if (initialised != getpid()) { + pipe(select_pipe); + + /* + * These next two lines seem to fix a bug with the Linux + * 2.0.x kernel (and probably other UNIXes as well) where + * the one byte read below can block even though the + * select returned that there is data in the pipe and + * the pipe_written variable was incremented. Thanks to + * HP for finding this one. JRA. + */ + + if(set_blocking(select_pipe[0],0)==-1) + smb_panic("select_pipe[0]: O_NONBLOCK failed.\n"); + if(set_blocking(select_pipe[1],0)==-1) + smb_panic("select_pipe[1]: O_NONBLOCK failed.\n"); + + initialised = getpid(); + } + + maxfd = MAX(select_pipe[0]+1, maxfd); + + /* If readfds is NULL we need to provide our own set. */ + if (readfds) { + readfds2 = readfds; + } else { + readfds2 = &readfds_buf; + FD_ZERO(readfds2); + } + FD_SET(select_pipe[0], readfds2); + + errno = 0; + ret = select(maxfd,readfds2,writefds,errorfds,tval); + + if (ret <= 0) { + FD_ZERO(readfds2); + if (writefds) + FD_ZERO(writefds); + if (errorfds) + FD_ZERO(errorfds); + } + + if (FD_ISSET(select_pipe[0], readfds2)) { + char c; + saved_errno = errno; + if (read(select_pipe[0], &c, 1) == 1) { + pipe_read++; + } + errno = saved_errno; + FD_CLR(select_pipe[0], readfds2); + ret--; + if (ret == 0) { + ret = -1; + errno = EINTR; + } + } + + return ret; +} + +/******************************************************************* + Similar to sys_select() but catch EINTR and continue. + This is what sys_select() used to do in Samba. +********************************************************************/ + +int sys_select_intr(int maxfd, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *tval) +{ + int ret; + fd_set *readfds2, readfds_buf, *writefds2, writefds_buf, *errorfds2, errorfds_buf; + struct timeval tval2, *ptval; + + readfds2 = (readfds ? &readfds_buf : NULL); + writefds2 = (writefds ? &writefds_buf : NULL); + errorfds2 = (errorfds ? &errorfds_buf : NULL); + ptval = (tval ? &tval2 : NULL); + + do { + if (readfds) + readfds_buf = *readfds; + if (writefds) + writefds_buf = *writefds; + if (errorfds) + errorfds_buf = *errorfds; + if (tval) + tval2 = *tval; + + ret = sys_select(maxfd, readfds2, writefds2, errorfds2, ptval); + } while (ret == -1 && errno == EINTR); + + if (readfds) + *readfds = readfds_buf; + if (writefds) + *writefds = writefds_buf; + if (errorfds) + *errorfds = errorfds_buf; + + return ret; +} diff --git a/source4/lib/sendfile.c b/source4/lib/sendfile.c new file mode 100644 index 0000000000..bcc8cb08ca --- /dev/null +++ b/source4/lib/sendfile.c @@ -0,0 +1,382 @@ +/* + Unix SMB/Netbios implementation. + Version 2.2.x / 3.0.x + sendfile implementations. + Copyright (C) Jeremy Allison 2002. + + 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. +*/ + +/* + * This file handles the OS dependent sendfile implementations. + * The API is such that it returns -1 on error, else returns the + * number of bytes written. + */ + +#include "includes.h" + +#if defined(LINUX_SENDFILE_API) + +#include <sys/sendfile.h> + +#ifndef MSG_MORE +#define MSG_MORE 0x8000 +#endif + +ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count) +{ + size_t total=0; + ssize_t ret; + size_t hdr_len = 0; + + /* + * Send the header first. + * Use MSG_MORE to cork the TCP output until sendfile is called. + */ + + if (header) { + hdr_len = header->length; + while (total < hdr_len) { + ret = sys_send(tofd, header->data + total,hdr_len - total, MSG_MORE); + if (ret == -1) + return -1; + total += ret; + } + } + + total = count; + while (total) { + ssize_t nwritten; + do { +#if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_OFF64_T) && defined(HAVE_SENDFILE64) + nwritten = sendfile64(tofd, fromfd, &offset, total); +#else + nwritten = sendfile(tofd, fromfd, &offset, total); +#endif + } while (nwritten == -1 && errno == EINTR); + if (nwritten == -1) + return -1; + if (nwritten == 0) + return -1; /* I think we're at EOF here... */ + total -= nwritten; + } + return count + hdr_len; +} + +#elif defined(LINUX_BROKEN_SENDFILE_API) + +/* + * We must use explicit 32 bit types here. This code path means Linux + * won't do proper 64-bit sendfile. JRA. + */ + +extern int32 sendfile (int out_fd, int in_fd, int32 *offset, uint32 count); + + +#ifndef MSG_MORE +#define MSG_MORE 0x8000 +#endif + +ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count) +{ + size_t total=0; + ssize_t ret; + ssize_t hdr_len = 0; + uint32 small_total = 0; + int32 small_offset; + + /* + * Fix for broken Linux 2.4 systems with no working sendfile64(). + * If the offset+count > 2 GB then pretend we don't have the + * system call sendfile at all. The upper layer catches this + * and uses a normal read. JRA. + */ + + if ((sizeof(SMB_OFF_T) >= 8) && (offset + count > (SMB_OFF_T)0x7FFFFFFF)) { + errno = ENOSYS; + return -1; + } + + /* + * Send the header first. + * Use MSG_MORE to cork the TCP output until sendfile is called. + */ + + if (header) { + hdr_len = header->length; + while (total < hdr_len) { + ret = sys_send(tofd, header->data + total,hdr_len - total, MSG_MORE); + if (ret == -1) + return -1; + total += ret; + } + } + + small_total = (uint32)count; + small_offset = (int32)offset; + + while (small_total) { + int32 nwritten; + do { + nwritten = sendfile(tofd, fromfd, &small_offset, small_total); + } while (nwritten == -1 && errno == EINTR); + if (nwritten == -1) + return -1; + if (nwritten == 0) + return -1; /* I think we're at EOF here... */ + small_total -= nwritten; + } + return count + hdr_len; +} + + +#elif defined(SOLARIS_SENDFILE_API) + +/* + * Solaris sendfile code written by Pierre Belanger <belanger@pobox.com>. + */ + +#include <sys/sendfile.h> + +ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count) +{ + int sfvcnt; + size_t total, xferred; + struct sendfilevec vec[2]; + ssize_t hdr_len = 0; + + if (header) { + sfvcnt = 2; + + vec[0].sfv_fd = SFV_FD_SELF; + vec[0].sfv_flag = 0; + vec[0].sfv_off = header->data; + vec[0].sfv_len = hdr_len = header->length; + + vec[1].sfv_fd = fromfd; + vec[1].sfv_flag = 0; + vec[1].sfv_off = offset; + vec[1].sfv_len = count; + + } else { + sfvcnt = 1; + + vec[0].sfv_fd = fromfd; + vec[0].sfv_flag = 0; + vec[0].sfv_off = offset; + vec[0].sfv_len = count; + } + + total = count + hdr_len; + + while (total) { + ssize_t nwritten; + + /* + * Although not listed in the API error returns, this is almost certainly + * a slow system call and will be interrupted by a signal with EINTR. JRA. + */ + + xferred = 0; + +#if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_OFF64_T) && defined(HAVE_SENDFILEV64) + nwritten = sendfilev64(tofd, vec, sfvcnt, &xferred); +#else + nwritten = sendfilev(tofd, vec, sfvcnt, &xferred); +#endif + if (nwritten == -1 && errno == EINTR) { + if (xferred == 0) + continue; /* Nothing written yet. */ + else + nwritten = xferred; + } + + if (nwritten == -1) + return -1; + if (nwritten == 0) + return -1; /* I think we're at EOF here... */ + + /* + * If this was a short (signal interrupted) write we may need + * to subtract it from the header data, or null out the header + * data altogether if we wrote more than vec[0].sfv_len bytes. + * We move vec[1].* to vec[0].* and set sfvcnt to 1 + */ + + if (sfvcnt == 2 && nwritten >= vec[0].sfv_len) { + vec[1].sfv_off += nwritten - vec[0].sfv_len; + vec[1].sfv_len -= nwritten - vec[0].sfv_len; + + /* Move vec[1].* to vec[0].* and set sfvcnt to 1 */ + vec[0] = vec[1]; + sfvcnt = 1; + } else { + vec[0].sfv_off += nwritten; + vec[0].sfv_len -= nwritten; + } + total -= nwritten; + } + return count + hdr_len; +} + +#elif defined(HPUX_SENDFILE_API) + +#include <sys/socket.h> +#include <sys/uio.h> + +ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count) +{ + size_t total=0; + struct iovec hdtrl[2]; + size_t hdr_len = 0; + + if (header) { + /* Set up the header/trailer iovec. */ + hdtrl[0].iov_base = header->data; + hdtrl[0].iov_len = hdr_len = header->length; + } else { + hdtrl[0].iov_base = NULL; + hdtrl[0].iov_len = hdr_len = 0; + } + hdtrl[1].iov_base = NULL; + hdtrl[1].iov_base = 0; + + total = count; + while (total + hdtrl[0].iov_len) { + ssize_t nwritten; + + /* + * HPUX guarantees that if any data was written before + * a signal interrupt then sendfile returns the number of + * bytes written (which may be less than requested) not -1. + * nwritten includes the header data sent. + */ + + do { +#if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_OFF64_T) && defined(HAVE_SENDFILE64) + nwritten = sendfile64(tofd, fromfd, offset, total, &hdtrl[0], 0); +#else + nwritten = sendfile(tofd, fromfd, offset, total, &hdtrl[0], 0); +#endif + } while (nwritten == -1 && errno == EINTR); + if (nwritten == -1) + return -1; + if (nwritten == 0) + return -1; /* I think we're at EOF here... */ + + /* + * If this was a short (signal interrupted) write we may need + * to subtract it from the header data, or null out the header + * data altogether if we wrote more than hdtrl[0].iov_len bytes. + * We change nwritten to be the number of file bytes written. + */ + + if (hdtrl[0].iov_base && hdtrl[0].iov_len) { + if (nwritten >= hdtrl[0].iov_len) { + nwritten -= hdtrl[0].iov_len; + hdtrl[0].iov_base = NULL; + hdtrl[0].iov_len = 0; + } else { + /* iov_base is defined as a void *... */ + hdtrl[0].iov_base = ((char *)hdtrl[0].iov_base) + nwritten; + hdtrl[0].iov_len -= nwritten; + nwritten = 0; + } + } + total -= nwritten; + offset += nwritten; + } + return count + hdr_len; +} + +#elif defined(FREEBSD_SENDFILE_API) + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/uio.h> + +ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count) +{ + size_t total=0; + struct sf_hdtr hdr; + struct iovec hdtrl; + size_t hdr_len = 0; + + hdr.headers = &hdtrl; + hdr.hdr_cnt = 1; + hdr.trailers = NULL; + hdr.trl_cnt = 0; + + /* Set up the header iovec. */ + if (header) { + hdtrl.iov_base = header->data; + hdtrl.iov_len = hdr_len = header->length; + } else { + hdtrl.iov_base = NULL; + hdtrl.iov_len = 0; + } + + total = count; + while (total + hdtrl.iov_len) { + SMB_OFF_T nwritten; + int ret; + + /* + * FreeBSD sendfile returns 0 on success, -1 on error. + * Remember, the tofd and fromfd are reversed..... :-). + * nwritten includes the header data sent. + */ + + do { + ret = sendfile(fromfd, tofd, offset, total, &hdr, &nwritten, 0); + } while (ret == -1 && errno == EINTR); + if (ret == -1) + return -1; + + if (nwritten == 0) + return -1; /* I think we're at EOF here... */ + + /* + * If this was a short (signal interrupted) write we may need + * to subtract it from the header data, or null out the header + * data altogether if we wrote more than hdtrl.iov_len bytes. + * We change nwritten to be the number of file bytes written. + */ + + if (hdtrl.iov_base && hdtrl.iov_len) { + if (nwritten >= hdtrl.iov_len) { + nwritten -= hdtrl.iov_len; + hdtrl.iov_base = NULL; + hdtrl.iov_len = 0; + } else { + hdtrl.iov_base += nwritten; + hdtrl.iov_len -= nwritten; + nwritten = 0; + } + } + total -= nwritten; + offset += nwritten; + } + return count + hdr_len; +} + +#else /* No sendfile implementation. Return error. */ + +ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count) +{ + /* No sendfile syscall. */ + errno = ENOSYS; + return -1; +} +#endif diff --git a/source4/lib/server_mutex.c b/source4/lib/server_mutex.c new file mode 100644 index 0000000000..878e5497d8 --- /dev/null +++ b/source4/lib/server_mutex.c @@ -0,0 +1,58 @@ +/* + Unix SMB/CIFS implementation. + Authenticate against a remote domain + Copyright (C) Andrew Tridgell 1992-2002 + Copyright (C) Andrew Bartlett 2002 + + 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" + +/* For reasons known only to MS, many of their NT/Win2k versions + need serialised access only. Two connections at the same time + may (in certain situations) cause connections to be reset, + or access to be denied. + + This locking allows smbd's mutlithread architecture to look + like the single-connection that NT makes. */ + +static char *mutex_server_name; +/* FIXME. ref_count should be allocated per name... JRA. */ +size_t ref_count; + +BOOL grab_server_mutex(const char *name) +{ + mutex_server_name = strdup(name); + if (!mutex_server_name) { + DEBUG(0,("grab_server_mutex: malloc failed for %s\n", name)); + return False; + } + if (!secrets_named_mutex(mutex_server_name, 10, &ref_count)) { + DEBUG(10,("grab_server_mutex: failed for %s\n", name)); + SAFE_FREE(mutex_server_name); + return False; + } + + return True; +} + +void release_server_mutex(void) +{ + if (mutex_server_name) { + secrets_named_mutex_release(mutex_server_name, &ref_count); + SAFE_FREE(mutex_server_name); + } +} diff --git a/source4/lib/signal.c b/source4/lib/signal.c new file mode 100644 index 0000000000..bff4b91c1a --- /dev/null +++ b/source4/lib/signal.c @@ -0,0 +1,139 @@ +/* + Unix SMB/CIFS implementation. + signal handling functions + + Copyright (C) Andrew Tridgell 1998 + + 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" + +/**************************************************************************** + Catch child exits and reap the child zombie status. +****************************************************************************/ + +static void sig_cld(int signum) +{ + while (sys_waitpid((pid_t)-1,(int *)NULL, WNOHANG) > 0) + ; + + /* + * Turns out it's *really* important not to + * restore the signal handler here if we have real POSIX + * signal handling. If we do, then we get the signal re-delivered + * immediately - hey presto - instant loop ! JRA. + */ + +#if !defined(HAVE_SIGACTION) + CatchSignal(SIGCLD, sig_cld); +#endif +} + +/**************************************************************************** +catch child exits - leave status; +****************************************************************************/ + +static void sig_cld_leave_status(int signum) +{ + /* + * Turns out it's *really* important not to + * restore the signal handler here if we have real POSIX + * signal handling. If we do, then we get the signal re-delivered + * immediately - hey presto - instant loop ! JRA. + */ + +#if !defined(HAVE_SIGACTION) + CatchSignal(SIGCLD, sig_cld_leave_status); +#endif +} + +/******************************************************************* + Block sigs. +********************************************************************/ + +void BlockSignals(BOOL block,int signum) +{ +#ifdef HAVE_SIGPROCMASK + sigset_t set; + sigemptyset(&set); + sigaddset(&set,signum); + sigprocmask(block?SIG_BLOCK:SIG_UNBLOCK,&set,NULL); +#elif defined(HAVE_SIGBLOCK) + if (block) { + sigblock(sigmask(signum)); + } else { + sigsetmask(siggetmask() & ~sigmask(signum)); + } +#else + /* yikes! This platform can't block signals? */ + static int done; + if (!done) { + DEBUG(0,("WARNING: No signal blocking available\n")); + done=1; + } +#endif +} + +/******************************************************************* + Catch a signal. This should implement the following semantics: + + 1) The handler remains installed after being called. + 2) The signal should be blocked during handler execution. +********************************************************************/ + +void (*CatchSignal(int signum,void (*handler)(int )))(int) +{ +#ifdef HAVE_SIGACTION + struct sigaction act; + struct sigaction oldact; + + ZERO_STRUCT(act); + + act.sa_handler = handler; +#ifdef SA_RESTART + /* + * We *want* SIGALRM to interrupt a system call. + */ + if(signum != SIGALRM) + act.sa_flags = SA_RESTART; +#endif + sigemptyset(&act.sa_mask); + sigaddset(&act.sa_mask,signum); + sigaction(signum,&act,&oldact); + return oldact.sa_handler; +#else /* !HAVE_SIGACTION */ + /* FIXME: need to handle sigvec and systems with broken signal() */ + return signal(signum, handler); +#endif +} + +/******************************************************************* + Ignore SIGCLD via whatever means is necessary for this OS. +********************************************************************/ + +void CatchChild(void) +{ + CatchSignal(SIGCLD, sig_cld); +} + +/******************************************************************* + Catch SIGCLD but leave the child around so it's status can be reaped. +********************************************************************/ + +void CatchChildLeaveStatus(void) +{ + CatchSignal(SIGCLD, sig_cld_leave_status); +} diff --git a/source4/lib/smbpasswd.c b/source4/lib/smbpasswd.c new file mode 100644 index 0000000000..92ae1ffea2 --- /dev/null +++ b/source4/lib/smbpasswd.c @@ -0,0 +1,200 @@ +/* + Unix SMB/CIFS implementation. + + smbpasswd file format routines + + Copyright (C) Andrew Tridgell 1992-1998 + Modified by Jeremy Allison 1995. + Modified by Gerald (Jerry) Carter 2000-2001 + Copyright (C) Tim Potter 2001 + + 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. +*/ + +/*! \file lib/smbpasswd.c + + The smbpasswd file is used to store encrypted passwords in a similar + fashion to the /etc/passwd file. The format is colon separated fields + with one user per line like so: + + <username>:<uid>:<lanman hash>:<nt hash>:<acb info>:<last change time> + + The username and uid must correspond to an entry in the /etc/passwd + file. The lanman and nt password hashes are 32 hex digits corresponding + to the 16-byte lanman and nt hashes respectively. + + The password last change time is stored as a string of the format + LCD-<change time> where the change time is expressed as an + + 'N' No password + 'D' Disabled + 'H' Homedir required + 'T' Temp account. + 'U' User account (normal) + 'M' MNS logon user account - what is this ? + 'W' Workstation account + 'S' Server account + 'L' Locked account + 'X' No Xpiry on password + 'I' Interdomain trust account + +*/ + +#include "includes.h" + +/*! Convert 32 hex characters into a 16 byte array. */ + +BOOL smbpasswd_gethexpwd(char *p, unsigned char *pwd) +{ + int i; + unsigned char lonybble, hinybble; + const char *hexchars = "0123456789ABCDEF"; + char *p1, *p2; + + if (!p) return (False); + + for (i = 0; i < 32; i += 2) + { + hinybble = toupper(p[i]); + lonybble = toupper(p[i + 1]); + + p1 = strchr_m(hexchars, hinybble); + p2 = strchr_m(hexchars, lonybble); + + if (!p1 || !p2) + { + return (False); + } + + hinybble = PTR_DIFF(p1, hexchars); + lonybble = PTR_DIFF(p2, hexchars); + + pwd[i / 2] = (hinybble << 4) | lonybble; + } + return (True); +} + +/*! Convert a 16-byte array into 32 hex characters. */ + +void smbpasswd_sethexpwd(fstring p, unsigned char *pwd, uint16 acb_info) +{ + if (pwd != NULL) { + int i; + for (i = 0; i < 16; i++) + slprintf(&p[i*2], 3, "%02X", pwd[i]); + } else { + if (acb_info & ACB_PWNOTREQ) + safe_strcpy(p, "NO PASSWORDXXXXXXXXXXXXXXXXXXXXX", 33); + else + safe_strcpy(p, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", 33); + } +} + +/*! Decode the account control bits (ACB) info from a string. */ + +uint16 smbpasswd_decode_acb_info(const char *p) +{ + uint16 acb_info = 0; + BOOL finished = False; + + /* + * Check if the account type bits have been encoded after the + * NT password (in the form [NDHTUWSLXI]). + */ + + if (*p != '[') return 0; + + for (p++; *p && !finished; p++) + { + switch (*p) { + case 'N': /* 'N'o password. */ + acb_info |= ACB_PWNOTREQ; + break; + case 'D': /* 'D'isabled. */ + acb_info |= ACB_DISABLED; + break; + case 'H': /* 'H'omedir required. */ + acb_info |= ACB_HOMDIRREQ; + break; + case 'T': /* 'T'emp account. */ + acb_info |= ACB_TEMPDUP; + break; + case 'U': /* 'U'ser account (normal). */ + acb_info |= ACB_NORMAL; + break; + case 'M': /* 'M'NS logon user account. What is this ? */ + acb_info |= ACB_MNS; + break; + case 'W': /* 'W'orkstation account. */ + acb_info |= ACB_WSTRUST; + break; + case 'S': /* 'S'erver account. */ + acb_info |= ACB_SVRTRUST; + break; + case 'L': /* 'L'ocked account. */ + acb_info |= ACB_AUTOLOCK; + break; + case 'X': /* No 'X'piry on password */ + acb_info |= ACB_PWNOEXP; + break; + case 'I': /* 'I'nterdomain trust account. */ + acb_info |= ACB_DOMTRUST; + break; + + case ' ': + break; + case ':': + case '\n': + case '\0': + case ']': + default: + finished = True; + break; + } + } + + return acb_info; +} + +/*! Encode account control bits (ACBs) into a string. */ + +char *smbpasswd_encode_acb_info(uint16 acb_info) +{ + static fstring acct_str; + size_t i = 0; + + acct_str[i++] = '['; + + if (acb_info & ACB_PWNOTREQ ) acct_str[i++] = 'N'; + if (acb_info & ACB_DISABLED ) acct_str[i++] = 'D'; + if (acb_info & ACB_HOMDIRREQ) acct_str[i++] = 'H'; + if (acb_info & ACB_TEMPDUP ) acct_str[i++] = 'T'; + if (acb_info & ACB_NORMAL ) acct_str[i++] = 'U'; + if (acb_info & ACB_MNS ) acct_str[i++] = 'M'; + if (acb_info & ACB_WSTRUST ) acct_str[i++] = 'W'; + if (acb_info & ACB_SVRTRUST ) acct_str[i++] = 'S'; + if (acb_info & ACB_AUTOLOCK ) acct_str[i++] = 'L'; + if (acb_info & ACB_PWNOEXP ) acct_str[i++] = 'X'; + if (acb_info & ACB_DOMTRUST ) acct_str[i++] = 'I'; + + for ( ; i < NEW_PW_FORMAT_SPACE_PADDED_LEN - 2 ; i++ ) + acct_str[i] = ' '; + + i = NEW_PW_FORMAT_SPACE_PADDED_LEN - 2; + acct_str[i++] = ']'; + acct_str[i++] = '\0'; + + return acct_str; +} diff --git a/source4/lib/smbrun.c b/source4/lib/smbrun.c new file mode 100644 index 0000000000..acb836ba7a --- /dev/null +++ b/source4/lib/smbrun.c @@ -0,0 +1,171 @@ +/* + Unix SMB/CIFS implementation. + run a command as a specified user + Copyright (C) Andrew Tridgell 1992-1998 + + 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" + +/* need to move this from here!! need some sleep ... */ +struct current_user current_user; + +/**************************************************************************** +This is a utility function of smbrun(). +****************************************************************************/ + +static int setup_out_fd(void) +{ + int fd; + pstring path; + + slprintf(path, sizeof(path)-1, "%s/smb.XXXXXX", tmpdir()); + + /* now create the file */ + fd = smb_mkstemp(path); + + if (fd == -1) { + DEBUG(0,("setup_out_fd: Failed to create file %s. (%s)\n", + path, strerror(errno) )); + return -1; + } + + DEBUG(10,("setup_out_fd: Created tmp file %s\n", path )); + + /* Ensure file only kept around by open fd. */ + unlink(path); + return fd; +} + +/**************************************************************************** +run a command being careful about uid/gid handling and putting the output in +outfd (or discard it if outfd is NULL). +****************************************************************************/ + +int smbrun(char *cmd, int *outfd) +{ + pid_t pid; + uid_t uid = current_user.uid; + gid_t gid = current_user.gid; + + /* + * Lose any kernel oplock capabilities we may have. + */ + oplock_set_capability(False, False); + + /* point our stdout at the file we want output to go into */ + + if (outfd && ((*outfd = setup_out_fd()) == -1)) { + return -1; + } + + /* in this method we will exec /bin/sh with the correct + arguments, after first setting stdout to point at the file */ + + /* + * We need to temporarily stop CatchChild from eating + * SIGCLD signals as it also eats the exit status code. JRA. + */ + + CatchChildLeaveStatus(); + + if ((pid=fork()) < 0) { + DEBUG(0,("smbrun: fork failed with error %s\n", strerror(errno) )); + CatchChild(); + if (outfd) { + close(*outfd); + *outfd = -1; + } + return errno; + } + + if (pid) { + /* + * Parent. + */ + int status=0; + pid_t wpid; + + + /* the parent just waits for the child to exit */ + while((wpid = sys_waitpid(pid,&status,0)) < 0) { + if(errno == EINTR) { + errno = 0; + continue; + } + break; + } + + CatchChild(); + + if (wpid != pid) { + DEBUG(2,("waitpid(%d) : %s\n",(int)pid,strerror(errno))); + if (outfd) { + close(*outfd); + *outfd = -1; + } + return -1; + } + + /* Reset the seek pointer. */ + if (outfd) { + sys_lseek(*outfd, 0, SEEK_SET); + } + +#if defined(WIFEXITED) && defined(WEXITSTATUS) + if (WIFEXITED(status)) { + return WEXITSTATUS(status); + } +#endif + + return status; + } + + CatchChild(); + + /* we are in the child. we exec /bin/sh to do the work for us. we + don't directly exec the command we want because it may be a + pipeline or anything else the config file specifies */ + + /* point our stdout at the file we want output to go into */ + if (outfd) { + close(1); + if (sys_dup2(*outfd,1) != 1) { + DEBUG(2,("Failed to create stdout file descriptor\n")); + close(*outfd); + exit(80); + } + } + + /* now completely lose our privileges. This is a fairly paranoid + way of doing it, but it does work on all systems that I know of */ + + become_user_permanently(uid, gid); + + if (getuid() != uid || geteuid() != uid || + getgid() != gid || getegid() != gid) { + /* we failed to lose our privileges - do not execute + the command */ + exit(81); /* we can't print stuff at this stage, + instead use exit codes for debugging */ + } + + execl("/bin/sh","sh","-c",cmd,NULL); + + /* not reached */ + exit(82); + return 1; +} diff --git a/source4/lib/snprintf.c b/source4/lib/snprintf.c new file mode 100644 index 0000000000..1eae2f09b9 --- /dev/null +++ b/source4/lib/snprintf.c @@ -0,0 +1,978 @@ +/* + * Copyright Patrick Powell 1995 + * This code is based on code written by Patrick Powell (papowell@astart.com) + * It may be used for any purpose as long as this notice remains intact + * on all source code distributions + */ + +/************************************************************** + * Original: + * Patrick Powell Tue Apr 11 09:48:21 PDT 1995 + * A bombproof version of doprnt (dopr) included. + * Sigh. This sort of thing is always nasty do deal with. Note that + * the version here does not include floating point... + * + * snprintf() is used instead of sprintf() as it does limit checks + * for string length. This covers a nasty loophole. + * + * The other functions are there to prevent NULL pointers from + * causing nast effects. + * + * More Recently: + * Brandon Long <blong@fiction.net> 9/15/96 for mutt 0.43 + * This was ugly. It is still ugly. I opted out of floating point + * numbers, but the formatter understands just about everything + * from the normal C string format, at least as far as I can tell from + * the Solaris 2.5 printf(3S) man page. + * + * Brandon Long <blong@fiction.net> 10/22/97 for mutt 0.87.1 + * Ok, added some minimal floating point support, which means this + * probably requires libm on most operating systems. Don't yet + * support the exponent (e,E) and sigfig (g,G). Also, fmtint() + * was pretty badly broken, it just wasn't being exercised in ways + * which showed it, so that's been fixed. Also, formated the code + * to mutt conventions, and removed dead code left over from the + * original. Also, there is now a builtin-test, just compile with: + * gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm + * and run snprintf for results. + * + * Thomas Roessler <roessler@guug.de> 01/27/98 for mutt 0.89i + * The PGP code was using unsigned hexadecimal formats. + * Unfortunately, unsigned formats simply didn't work. + * + * Michael Elkins <me@cs.hmc.edu> 03/05/98 for mutt 0.90.8 + * The original code assumed that both snprintf() and vsnprintf() were + * missing. Some systems only have snprintf() but not vsnprintf(), so + * the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF. + * + * Andrew Tridgell (tridge@samba.org) Oct 1998 + * fixed handling of %.0f + * added test for HAVE_LONG_DOUBLE + * + * tridge@samba.org, idra@samba.org, April 2001 + * got rid of fcvt code (twas buggy and made testing harder) + * added C99 semantics + * + **************************************************************/ + +#ifndef NO_CONFIG_H /* for some tests */ +#include "config.h" +#else +#define NULL 0 +#endif + +#ifdef TEST_SNPRINTF /* need math library headers for testing */ +#include <math.h> +#endif + +#ifdef HAVE_STRING_H +#include <string.h> +#endif + +#ifdef HAVE_STRINGS_H +#include <strings.h> +#endif +#ifdef HAVE_CTYPE_H +#include <ctype.h> +#endif +#include <sys/types.h> +#include <stdarg.h> +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif + +#if defined(HAVE_SNPRINTF) && defined(HAVE_VSNPRINTF) && defined(HAVE_C99_VSNPRINTF) +/* only include stdio.h if we are not re-defining snprintf or vsnprintf */ +#include <stdio.h> + /* make the compiler happy with an empty file */ + void dummy_snprintf(void) {} +#else + +#ifdef HAVE_LONG_DOUBLE +#define LDOUBLE long double +#else +#define LDOUBLE double +#endif + +#ifdef HAVE_LONG_LONG +#define LLONG long long +#else +#define LLONG long +#endif + +/* free memory if the pointer is valid and zero the pointer */ +#ifndef SAFE_FREE +#define SAFE_FREE(x) do { if ((x) != NULL) {free((x)); (x)=NULL;} } while(0) +#endif + +#ifndef VA_COPY +#ifdef HAVE_VA_COPY +#define VA_COPY(dest, src) __va_copy(dest, src) +#else +#define VA_COPY(dest, src) (dest) = (src) +#endif +#endif + +static size_t dopr(char *buffer, size_t maxlen, const char *format, + va_list args_in); +static void fmtstr(char *buffer, size_t *currlen, size_t maxlen, + char *value, int flags, int min, int max); +static void fmtint(char *buffer, size_t *currlen, size_t maxlen, + long value, int base, int min, int max, int flags); +static void fmtfp(char *buffer, size_t *currlen, size_t maxlen, + LDOUBLE fvalue, int min, int max, int flags); +static void dopr_outch(char *buffer, size_t *currlen, size_t maxlen, char c); + +/* + * dopr(): poor man's version of doprintf + */ + +/* format read states */ +#define DP_S_DEFAULT 0 +#define DP_S_FLAGS 1 +#define DP_S_MIN 2 +#define DP_S_DOT 3 +#define DP_S_MAX 4 +#define DP_S_MOD 5 +#define DP_S_CONV 6 +#define DP_S_DONE 7 + +/* format flags - Bits */ +#define DP_F_MINUS (1 << 0) +#define DP_F_PLUS (1 << 1) +#define DP_F_SPACE (1 << 2) +#define DP_F_NUM (1 << 3) +#define DP_F_ZERO (1 << 4) +#define DP_F_UP (1 << 5) +#define DP_F_UNSIGNED (1 << 6) + +/* Conversion Flags */ +#define DP_C_SHORT 1 +#define DP_C_LONG 2 +#define DP_C_LDOUBLE 3 +#define DP_C_LLONG 4 + +#define char_to_int(p) ((p)- '0') +#ifndef MAX +#define MAX(p,q) (((p) >= (q)) ? (p) : (q)) +#endif + +static size_t dopr(char *buffer, size_t maxlen, const char *format, va_list args_in) +{ + char ch; + LLONG value; + LDOUBLE fvalue; + char *strvalue; + int min; + int max; + int state; + int flags; + int cflags; + size_t currlen; + va_list args; + + VA_COPY(args, args_in); + + state = DP_S_DEFAULT; + currlen = flags = cflags = min = 0; + max = -1; + ch = *format++; + + while (state != DP_S_DONE) { + if (ch == '\0') + state = DP_S_DONE; + + switch(state) { + case DP_S_DEFAULT: + if (ch == '%') + state = DP_S_FLAGS; + else + dopr_outch (buffer, &currlen, maxlen, ch); + ch = *format++; + break; + case DP_S_FLAGS: + switch (ch) { + case '-': + flags |= DP_F_MINUS; + ch = *format++; + break; + case '+': + flags |= DP_F_PLUS; + ch = *format++; + break; + case ' ': + flags |= DP_F_SPACE; + ch = *format++; + break; + case '#': + flags |= DP_F_NUM; + ch = *format++; + break; + case '0': + flags |= DP_F_ZERO; + ch = *format++; + break; + default: + state = DP_S_MIN; + break; + } + break; + case DP_S_MIN: + if (isdigit((unsigned char)ch)) { + min = 10*min + char_to_int (ch); + ch = *format++; + } else if (ch == '*') { + min = va_arg (args, int); + ch = *format++; + state = DP_S_DOT; + } else { + state = DP_S_DOT; + } + break; + case DP_S_DOT: + if (ch == '.') { + state = DP_S_MAX; + ch = *format++; + } else { + state = DP_S_MOD; + } + break; + case DP_S_MAX: + if (isdigit((unsigned char)ch)) { + if (max < 0) + max = 0; + max = 10*max + char_to_int (ch); + ch = *format++; + } else if (ch == '*') { + max = va_arg (args, int); + ch = *format++; + state = DP_S_MOD; + } else { + state = DP_S_MOD; + } + break; + case DP_S_MOD: + switch (ch) { + case 'h': + cflags = DP_C_SHORT; + ch = *format++; + break; + case 'l': + cflags = DP_C_LONG; + ch = *format++; + if (ch == 'l') { /* It's a long long */ + cflags = DP_C_LLONG; + ch = *format++; + } + break; + case 'L': + cflags = DP_C_LDOUBLE; + ch = *format++; + break; + default: + break; + } + state = DP_S_CONV; + break; + case DP_S_CONV: + switch (ch) { + case 'd': + case 'i': + if (cflags == DP_C_SHORT) + value = va_arg (args, int); + else if (cflags == DP_C_LONG) + value = va_arg (args, long int); + else if (cflags == DP_C_LLONG) + value = va_arg (args, LLONG); + else + value = va_arg (args, int); + fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags); + break; + case 'o': + flags |= DP_F_UNSIGNED; + if (cflags == DP_C_SHORT) + value = va_arg (args, unsigned int); + else if (cflags == DP_C_LONG) + value = (long)va_arg (args, unsigned long int); + else if (cflags == DP_C_LLONG) + value = (long)va_arg (args, unsigned LLONG); + else + value = (long)va_arg (args, unsigned int); + fmtint (buffer, &currlen, maxlen, value, 8, min, max, flags); + break; + case 'u': + flags |= DP_F_UNSIGNED; + if (cflags == DP_C_SHORT) + value = va_arg (args, unsigned int); + else if (cflags == DP_C_LONG) + value = (long)va_arg (args, unsigned long int); + else if (cflags == DP_C_LLONG) + value = (LLONG)va_arg (args, unsigned LLONG); + else + value = (long)va_arg (args, unsigned int); + fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags); + break; + case 'X': + flags |= DP_F_UP; + case 'x': + flags |= DP_F_UNSIGNED; + if (cflags == DP_C_SHORT) + value = va_arg (args, unsigned int); + else if (cflags == DP_C_LONG) + value = (long)va_arg (args, unsigned long int); + else if (cflags == DP_C_LLONG) + value = (LLONG)va_arg (args, unsigned LLONG); + else + value = (long)va_arg (args, unsigned int); + fmtint (buffer, &currlen, maxlen, value, 16, min, max, flags); + break; + case 'f': + if (cflags == DP_C_LDOUBLE) + fvalue = va_arg (args, LDOUBLE); + else + fvalue = va_arg (args, double); + /* um, floating point? */ + fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags); + break; + case 'E': + flags |= DP_F_UP; + case 'e': + if (cflags == DP_C_LDOUBLE) + fvalue = va_arg (args, LDOUBLE); + else + fvalue = va_arg (args, double); + fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags); + break; + case 'G': + flags |= DP_F_UP; + case 'g': + if (cflags == DP_C_LDOUBLE) + fvalue = va_arg (args, LDOUBLE); + else + fvalue = va_arg (args, double); + fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags); + break; + case 'c': + dopr_outch (buffer, &currlen, maxlen, va_arg (args, int)); + break; + case 's': + strvalue = va_arg (args, char *); + if (!strvalue) strvalue = "(NULL)"; + if (max == -1) { + max = strlen(strvalue); + } + if (min > 0 && max >= 0 && min > max) max = min; + fmtstr (buffer, &currlen, maxlen, strvalue, flags, min, max); + break; + case 'p': + strvalue = va_arg (args, void *); + fmtint (buffer, &currlen, maxlen, (long) strvalue, 16, min, max, flags); + break; + case 'n': + if (cflags == DP_C_SHORT) { + short int *num; + num = va_arg (args, short int *); + *num = currlen; + } else if (cflags == DP_C_LONG) { + long int *num; + num = va_arg (args, long int *); + *num = (long int)currlen; + } else if (cflags == DP_C_LLONG) { + LLONG *num; + num = va_arg (args, LLONG *); + *num = (LLONG)currlen; + } else { + int *num; + num = va_arg (args, int *); + *num = currlen; + } + break; + case '%': + dopr_outch (buffer, &currlen, maxlen, ch); + break; + case 'w': + /* not supported yet, treat as next char */ + ch = *format++; + break; + default: + /* Unknown, skip */ + break; + } + ch = *format++; + state = DP_S_DEFAULT; + flags = cflags = min = 0; + max = -1; + break; + case DP_S_DONE: + break; + default: + /* hmm? */ + break; /* some picky compilers need this */ + } + } + if (maxlen != 0) { + if (currlen < maxlen - 1) + buffer[currlen] = '\0'; + else if (maxlen > 0) + buffer[maxlen - 1] = '\0'; + } + + return currlen; +} + +static void fmtstr(char *buffer, size_t *currlen, size_t maxlen, + char *value, int flags, int min, int max) +{ + int padlen, strln; /* amount to pad */ + int cnt = 0; + +#ifdef DEBUG_SNPRINTF + printf("fmtstr min=%d max=%d s=[%s]\n", min, max, value); +#endif + if (value == 0) { + value = "<NULL>"; + } + + for (strln = 0; value[strln]; ++strln); /* strlen */ + padlen = min - strln; + if (padlen < 0) + padlen = 0; + if (flags & DP_F_MINUS) + padlen = -padlen; /* Left Justify */ + + while ((padlen > 0) && (cnt < max)) { + dopr_outch (buffer, currlen, maxlen, ' '); + --padlen; + ++cnt; + } + while (*value && (cnt < max)) { + dopr_outch (buffer, currlen, maxlen, *value++); + ++cnt; + } + while ((padlen < 0) && (cnt < max)) { + dopr_outch (buffer, currlen, maxlen, ' '); + ++padlen; + ++cnt; + } +} + +/* Have to handle DP_F_NUM (ie 0x and 0 alternates) */ + +static void fmtint(char *buffer, size_t *currlen, size_t maxlen, + long value, int base, int min, int max, int flags) +{ + int signvalue = 0; + unsigned long uvalue; + char convert[20]; + int place = 0; + int spadlen = 0; /* amount to space pad */ + int zpadlen = 0; /* amount to zero pad */ + int caps = 0; + + if (max < 0) + max = 0; + + uvalue = value; + + if(!(flags & DP_F_UNSIGNED)) { + if( value < 0 ) { + signvalue = '-'; + uvalue = -value; + } else { + if (flags & DP_F_PLUS) /* Do a sign (+/i) */ + signvalue = '+'; + else if (flags & DP_F_SPACE) + signvalue = ' '; + } + } + + if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */ + + do { + convert[place++] = + (caps? "0123456789ABCDEF":"0123456789abcdef") + [uvalue % (unsigned)base ]; + uvalue = (uvalue / (unsigned)base ); + } while(uvalue && (place < 20)); + if (place == 20) place--; + convert[place] = 0; + + zpadlen = max - place; + spadlen = min - MAX (max, place) - (signvalue ? 1 : 0); + if (zpadlen < 0) zpadlen = 0; + if (spadlen < 0) spadlen = 0; + if (flags & DP_F_ZERO) { + zpadlen = MAX(zpadlen, spadlen); + spadlen = 0; + } + if (flags & DP_F_MINUS) + spadlen = -spadlen; /* Left Justifty */ + +#ifdef DEBUG_SNPRINTF + printf("zpad: %d, spad: %d, min: %d, max: %d, place: %d\n", + zpadlen, spadlen, min, max, place); +#endif + + /* Spaces */ + while (spadlen > 0) { + dopr_outch (buffer, currlen, maxlen, ' '); + --spadlen; + } + + /* Sign */ + if (signvalue) + dopr_outch (buffer, currlen, maxlen, signvalue); + + /* Zeros */ + if (zpadlen > 0) { + while (zpadlen > 0) { + dopr_outch (buffer, currlen, maxlen, '0'); + --zpadlen; + } + } + + /* Digits */ + while (place > 0) + dopr_outch (buffer, currlen, maxlen, convert[--place]); + + /* Left Justified spaces */ + while (spadlen < 0) { + dopr_outch (buffer, currlen, maxlen, ' '); + ++spadlen; + } +} + +static LDOUBLE abs_val(LDOUBLE value) +{ + LDOUBLE result = value; + + if (value < 0) + result = -value; + + return result; +} + +static LDOUBLE POW10(int exp) +{ + LDOUBLE result = 1; + + while (exp) { + result *= 10; + exp--; + } + + return result; +} + +static LLONG ROUND(LDOUBLE value) +{ + LLONG intpart; + + intpart = (LLONG)value; + value = value - intpart; + if (value >= 0.5) intpart++; + + return intpart; +} + +/* a replacement for modf that doesn't need the math library. Should + be portable, but slow */ +static double my_modf(double x0, double *iptr) +{ + int i; + long l; + double x = x0; + double f = 1.0; + + for (i=0;i<100;i++) { + l = (long)x; + if (l <= (x+1) && l >= (x-1)) break; + x *= 0.1; + f *= 10.0; + } + + if (i == 100) { + /* yikes! the number is beyond what we can handle. What do we do? */ + (*iptr) = 0; + return 0; + } + + if (i != 0) { + double i2; + double ret; + + ret = my_modf(x0-l*f, &i2); + (*iptr) = l*f + i2; + return ret; + } + + (*iptr) = l; + return x - (*iptr); +} + + +static void fmtfp (char *buffer, size_t *currlen, size_t maxlen, + LDOUBLE fvalue, int min, int max, int flags) +{ + int signvalue = 0; + double ufvalue; + char iconvert[311]; + char fconvert[311]; + int iplace = 0; + int fplace = 0; + int padlen = 0; /* amount to pad */ + int zpadlen = 0; + int caps = 0; + int index; + double intpart; + double fracpart; + double temp; + + /* + * AIX manpage says the default is 0, but Solaris says the default + * is 6, and sprintf on AIX defaults to 6 + */ + if (max < 0) + max = 6; + + ufvalue = abs_val (fvalue); + + if (fvalue < 0) { + signvalue = '-'; + } else { + if (flags & DP_F_PLUS) { /* Do a sign (+/i) */ + signvalue = '+'; + } else { + if (flags & DP_F_SPACE) + signvalue = ' '; + } + } + +#if 0 + if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */ +#endif + +#if 0 + if (max == 0) ufvalue += 0.5; /* if max = 0 we must round */ +#endif + + /* + * Sorry, we only support 16 digits past the decimal because of our + * conversion method + */ + if (max > 16) + max = 16; + + /* We "cheat" by converting the fractional part to integer by + * multiplying by a factor of 10 + */ + + temp = ufvalue; + my_modf(temp, &intpart); + + fracpart = ROUND((POW10(max)) * (ufvalue - intpart)); + + if (fracpart >= POW10(max)) { + intpart++; + fracpart -= POW10(max); + } + + + /* Convert integer part */ + do { + temp = intpart*0.1; + my_modf(temp, &intpart); + index = (int) ((temp -intpart +0.05)* 10.0); + /* index = (int) (((double)(temp*0.1) -intpart +0.05) *10.0); */ + /* printf ("%llf, %f, %x\n", temp, intpart, index); */ + iconvert[iplace++] = + (caps? "0123456789ABCDEF":"0123456789abcdef")[index]; + } while (intpart && (iplace < 311)); + if (iplace == 311) iplace--; + iconvert[iplace] = 0; + + /* Convert fractional part */ + if (fracpart) + { + do { + temp = fracpart*0.1; + my_modf(temp, &fracpart); + index = (int) ((temp -fracpart +0.05)* 10.0); + /* index = (int) ((((temp/10) -fracpart) +0.05) *10); */ + /* printf ("%lf, %lf, %ld\n", temp, fracpart, index); */ + fconvert[fplace++] = + (caps? "0123456789ABCDEF":"0123456789abcdef")[index]; + } while(fracpart && (fplace < 311)); + if (fplace == 311) fplace--; + } + fconvert[fplace] = 0; + + /* -1 for decimal point, another -1 if we are printing a sign */ + padlen = min - iplace - max - 1 - ((signvalue) ? 1 : 0); + zpadlen = max - fplace; + if (zpadlen < 0) zpadlen = 0; + if (padlen < 0) + padlen = 0; + if (flags & DP_F_MINUS) + padlen = -padlen; /* Left Justifty */ + + if ((flags & DP_F_ZERO) && (padlen > 0)) { + if (signvalue) { + dopr_outch (buffer, currlen, maxlen, signvalue); + --padlen; + signvalue = 0; + } + while (padlen > 0) { + dopr_outch (buffer, currlen, maxlen, '0'); + --padlen; + } + } + while (padlen > 0) { + dopr_outch (buffer, currlen, maxlen, ' '); + --padlen; + } + if (signvalue) + dopr_outch (buffer, currlen, maxlen, signvalue); + + while (iplace > 0) + dopr_outch (buffer, currlen, maxlen, iconvert[--iplace]); + +#ifdef DEBUG_SNPRINTF + printf("fmtfp: fplace=%d zpadlen=%d\n", fplace, zpadlen); +#endif + + /* + * Decimal point. This should probably use locale to find the correct + * char to print out. + */ + if (max > 0) { + dopr_outch (buffer, currlen, maxlen, '.'); + + while (zpadlen > 0) { + dopr_outch (buffer, currlen, maxlen, '0'); + --zpadlen; + } + + while (fplace > 0) + dopr_outch (buffer, currlen, maxlen, fconvert[--fplace]); + } + + while (padlen < 0) { + dopr_outch (buffer, currlen, maxlen, ' '); + ++padlen; + } +} + +static void dopr_outch(char *buffer, size_t *currlen, size_t maxlen, char c) +{ + if (*currlen < maxlen) { + buffer[(*currlen)] = c; + } + (*currlen)++; +} + +/* yes this really must be a ||. Don't muck with this (tridge) */ +#if !defined(HAVE_VSNPRINTF) || !defined(HAVE_C99_VSNPRINTF) + int vsnprintf (char *str, size_t count, const char *fmt, va_list args) +{ + return dopr(str, count, fmt, args); +} +#endif + +/* yes this really must be a ||. Don't muck wiith this (tridge) + * + * The logic for these two is that we need our own definition if the + * OS *either* has no definition of *sprintf, or if it does have one + * that doesn't work properly according to the autoconf test. Perhaps + * these should really be smb_snprintf to avoid conflicts with buggy + * linkers? -- mbp + */ +#if !defined(HAVE_SNPRINTF) || !defined(HAVE_C99_SNPRINTF) + int snprintf(char *str,size_t count,const char *fmt,...) +{ + size_t ret; + va_list ap; + + va_start(ap, fmt); + ret = vsnprintf(str, count, fmt, ap); + va_end(ap); + return ret; +} +#endif + +#endif + +#ifndef HAVE_VASPRINTF + int vasprintf(char **ptr, const char *format, va_list ap) +{ + int ret; + va_list ap2; + + VA_COPY(ap2, ap); + + ret = vsnprintf(NULL, 0, format, ap2); + if (ret <= 0) return ret; + + (*ptr) = (char *)malloc(ret+1); + if (!*ptr) return -1; + + VA_COPY(ap2, ap); + + ret = vsnprintf(*ptr, ret+1, format, ap2); + + return ret; +} +#endif + + +#ifndef HAVE_ASPRINTF + int asprintf(char **ptr, const char *format, ...) +{ + va_list ap; + int ret; + + *ptr = NULL; + va_start(ap, format); + ret = vasprintf(ptr, format, ap); + va_end(ap); + + return ret; +} +#endif + +#ifdef TEST_SNPRINTF + + int sprintf(char *str,const char *fmt,...); + + int main (void) +{ + char buf1[1024]; + char buf2[1024]; + char *fp_fmt[] = { + "%1.1f", + "%-1.5f", + "%1.5f", + "%123.9f", + "%10.5f", + "% 10.5f", + "%+22.9f", + "%+4.9f", + "%01.3f", + "%4f", + "%3.1f", + "%3.2f", + "%.0f", + "%f", + "-16.16f", + NULL + }; + double fp_nums[] = { 6442452944.1234, -1.5, 134.21, 91340.2, 341.1234, 0203.9, 0.96, 0.996, + 0.9996, 1.996, 4.136, 5.030201, 0}; + char *int_fmt[] = { + "%-1.5d", + "%1.5d", + "%123.9d", + "%5.5d", + "%10.5d", + "% 10.5d", + "%+22.33d", + "%01.3d", + "%4d", + "%d", + NULL + }; + long int_nums[] = { -1, 134, 91340, 341, 0203, 0}; + char *str_fmt[] = { + "10.5s", + "5.10s", + "10.1s", + "0.10s", + "10.0s", + "1.10s", + "%s", + "%.1s", + "%.10s", + "%10s", + NULL + }; + char *str_vals[] = {"hello", "a", "", "a longer string", NULL}; + int x, y; + int fail = 0; + int num = 0; + + printf ("Testing snprintf format codes against system sprintf...\n"); + + for (x = 0; fp_fmt[x] ; x++) { + for (y = 0; fp_nums[y] != 0 ; y++) { + int l1 = snprintf(NULL, 0, fp_fmt[x], fp_nums[y]); + int l2 = snprintf(buf1, sizeof(buf1), fp_fmt[x], fp_nums[y]); + sprintf (buf2, fp_fmt[x], fp_nums[y]); + if (strcmp (buf1, buf2)) { + printf("snprintf doesn't match Format: %s\n\tsnprintf = [%s]\n\t sprintf = [%s]\n", + fp_fmt[x], buf1, buf2); + fail++; + } + if (l1 != l2) { + printf("snprintf l1 != l2 (%d %d) %s\n", l1, l2, fp_fmt[x]); + fail++; + } + num++; + } + } + + for (x = 0; int_fmt[x] ; x++) { + for (y = 0; int_nums[y] != 0 ; y++) { + int l1 = snprintf(NULL, 0, int_fmt[x], int_nums[y]); + int l2 = snprintf(buf1, sizeof(buf1), int_fmt[x], int_nums[y]); + sprintf (buf2, int_fmt[x], int_nums[y]); + if (strcmp (buf1, buf2)) { + printf("snprintf doesn't match Format: %s\n\tsnprintf = [%s]\n\t sprintf = [%s]\n", + int_fmt[x], buf1, buf2); + fail++; + } + if (l1 != l2) { + printf("snprintf l1 != l2 (%d %d) %s\n", l1, l2, int_fmt[x]); + fail++; + } + num++; + } + } + + for (x = 0; str_fmt[x] ; x++) { + for (y = 0; str_vals[y] != 0 ; y++) { + int l1 = snprintf(NULL, 0, str_fmt[x], str_vals[y]); + int l2 = snprintf(buf1, sizeof(buf1), str_fmt[x], str_vals[y]); + sprintf (buf2, str_fmt[x], str_vals[y]); + if (strcmp (buf1, buf2)) { + printf("snprintf doesn't match Format: %s\n\tsnprintf = [%s]\n\t sprintf = [%s]\n", + str_fmt[x], buf1, buf2); + fail++; + } + if (l1 != l2) { + printf("snprintf l1 != l2 (%d %d) %s\n", l1, l2, str_fmt[x]); + fail++; + } + num++; + } + } + + printf ("%d tests failed out of %d.\n", fail, num); + + printf("seeing how many digits we support\n"); + { + double v0 = 0.12345678901234567890123456789012345678901; + for (x=0; x<100; x++) { + double p = pow(10, x); + double r = v0*p; + snprintf(buf1, sizeof(buf1), "%1.1f", r); + sprintf(buf2, "%1.1f", r); + if (strcmp(buf1, buf2)) { + printf("we seem to support %d digits\n", x-1); + break; + } + } + } + + return 0; +} +#endif /* SNPRINTF_TEST */ diff --git a/source4/lib/substitute.c b/source4/lib/substitute.c new file mode 100644 index 0000000000..4e4f0bc040 --- /dev/null +++ b/source4/lib/substitute.c @@ -0,0 +1,188 @@ +/* + Unix SMB/CIFS implementation. + string substitution functions + Copyright (C) Andrew Tridgell 1992-2000 + + 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" + +/* oh bugger - I realy didn't want to have a top-level context + anywhere, but until we change all lp_*() calls to take a context + argument this is needed */ +static struct substitute_context *sub; + +void sub_set_context(struct substitute_context *subptr) +{ + sub = subptr; +} + +/* + setup a string in the negotiate structure, using alpha_strcpy with SAFE_NETBIOS_CHARS +*/ +static void setup_string(char **dest, const char *str) +{ + char *s; + + s = strdup(str); + if (!s) { + return; + } + + alpha_strcpy(s, str, SAFE_NETBIOS_CHARS, strlen(s)+1); + + trim_string(s," "," "); + strlower(s); + + SAFE_FREE(*dest); + (*dest) = s; +} + +void sub_set_local_machine(const char *local_machine) +{ + if (!sub) return; + setup_string(&sub->local_machine, local_machine); +} + +void sub_set_remote_machine(const char *remote_machine) +{ + if (!sub) return; + setup_string(&sub->remote_machine, remote_machine); +} + +void sub_set_remote_proto(const char *str) +{ + if (!sub) return; + setup_string(&sub->remote_proto, str); +} + +void sub_set_remote_arch(const char *str) +{ + if (!sub) return; + setup_string(&sub->remote_arch, str); +} + +const char *sub_get_remote_machine(void) +{ + if (!sub) return "UNKNOWN"; + return sub->remote_machine; +} + +const char *sub_get_local_machine(void) +{ + if (!sub) return "UNKNOWN"; + return sub->local_machine; +} + + +/* + setup the string used by %U substitution +*/ +void sub_set_user_name(const char *name) +{ + if (!sub) return; + setup_string(&sub->user_name, name); +} + +/**************************************************************************** +FOO +****************************************************************************/ +void standard_sub_basic(char *str,size_t len) +{ +} + +/**************************************************************************** + Do some standard substitutions in a string. + This function will return an allocated string that have to be freed. +****************************************************************************/ +char *talloc_sub_basic(TALLOC_CTX *mem_ctx, const char *smb_name, const char *str) +{ + return talloc_strdup(mem_ctx, str); +} + +char *alloc_sub_basic(const char *smb_name, const char *str) +{ + return strdup(str); +} + +/**************************************************************************** + Do some specific substitutions in a string. + This function will return an allocated string that have to be freed. +****************************************************************************/ + +char *talloc_sub_specified(TALLOC_CTX *mem_ctx, + const char *input_string, + const char *username, + const char *domain, + uid_t uid, + gid_t gid) +{ + return talloc_strdup(mem_ctx, input_string); +} + +char *alloc_sub_specified(const char *input_string, + const char *username, + const char *domain, + uid_t uid, + gid_t gid) +{ + return strdup(input_string); +} + +char *talloc_sub_advanced(TALLOC_CTX *mem_ctx, + int snum, + const char *user, + const char *connectpath, + gid_t gid, + const char *smb_name, + char *str) +{ + return talloc_strdup(mem_ctx, str); +} + +char *alloc_sub_advanced(int snum, const char *user, + const char *connectpath, gid_t gid, + const char *smb_name, char *str) +{ + return strdup(str); +} + +/**************************************************************************** + Do some standard substitutions in a string. +****************************************************************************/ + +void standard_sub_conn(struct tcon_context *conn, char *str, size_t len) +{ +} + +char *talloc_sub_conn(TALLOC_CTX *mem_ctx, struct tcon_context *conn, char *str) +{ + return talloc_strdup(mem_ctx, str); +} + +char *alloc_sub_conn(struct tcon_context *conn, char *str) +{ + return strdup(str); +} + +/**************************************************************************** + Like standard_sub but by snum. +****************************************************************************/ + +void standard_sub_snum(int snum, char *str, size_t len) +{ +} diff --git a/source4/lib/sysacls.c b/source4/lib/sysacls.c new file mode 100644 index 0000000000..fe85b9e72f --- /dev/null +++ b/source4/lib/sysacls.c @@ -0,0 +1,3198 @@ +/* + Unix SMB/CIFS implementation. + Samba system utilities for ACL support. + Copyright (C) Jeremy Allison 2000. + + 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" + +/* + This file wraps all differing system ACL interfaces into a consistent + one based on the POSIX interface. It also returns the correct errors + for older UNIX systems that don't support ACLs. + + The interfaces that each ACL implementation must support are as follows : + + int sys_acl_get_entry( SMB_ACL_T theacl, int entry_id, SMB_ACL_ENTRY_T *entry_p) + int sys_acl_get_tag_type( SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T *tag_type_p) + int sys_acl_get_permset( SMB_ACL_ENTRY_T entry_d, SMB_ACL_PERMSET_T *permset_p + void *sys_acl_get_qualifier( SMB_ACL_ENTRY_T entry_d) + SMB_ACL_T sys_acl_get_file( const char *path_p, SMB_ACL_TYPE_T type) + SMB_ACL_T sys_acl_get_fd(int fd) + int sys_acl_clear_perms(SMB_ACL_PERMSET_T permset); + int sys_acl_add_perm( SMB_ACL_PERMSET_T permset, SMB_ACL_PERM_T perm); + char *sys_acl_to_text( SMB_ACL_T theacl, ssize_t *plen) + SMB_ACL_T sys_acl_init( int count) + int sys_acl_create_entry( SMB_ACL_T *pacl, SMB_ACL_ENTRY_T *pentry) + int sys_acl_set_tag_type( SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T tagtype) + int sys_acl_set_qualifier( SMB_ACL_ENTRY_T entry, void *qual) + int sys_acl_set_permset( SMB_ACL_ENTRY_T entry, SMB_ACL_PERMSET_T permset) + int sys_acl_valid( SMB_ACL_T theacl ) + int sys_acl_set_file( const char *name, SMB_ACL_TYPE_T acltype, SMB_ACL_T theacl) + int sys_acl_set_fd( int fd, SMB_ACL_T theacl) + int sys_acl_delete_def_file(const char *path) + + This next one is not POSIX complient - but we *have* to have it ! + More POSIX braindamage. + + int sys_acl_get_perm( SMB_ACL_PERMSET_T permset, SMB_ACL_PERM_T perm) + + The generic POSIX free is the following call. We split this into + several different free functions as we may need to add tag info + to structures when emulating the POSIX interface. + + int sys_acl_free( void *obj_p) + + The calls we actually use are : + + int sys_acl_free_text(char *text) - free acl_to_text + int sys_acl_free_acl(SMB_ACL_T posix_acl) + int sys_acl_free_qualifier(void *qualifier, SMB_ACL_TAG_T tagtype) + +*/ + +#if defined(HAVE_POSIX_ACLS) + +/* Identity mapping - easy. */ + +int sys_acl_get_entry( SMB_ACL_T the_acl, int entry_id, SMB_ACL_ENTRY_T *entry_p) +{ + return acl_get_entry( the_acl, entry_id, entry_p); +} + +int sys_acl_get_tag_type( SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T *tag_type_p) +{ + return acl_get_tag_type( entry_d, tag_type_p); +} + +int sys_acl_get_permset( SMB_ACL_ENTRY_T entry_d, SMB_ACL_PERMSET_T *permset_p) +{ + return acl_get_permset( entry_d, permset_p); +} + +void *sys_acl_get_qualifier( SMB_ACL_ENTRY_T entry_d) +{ + return acl_get_qualifier( entry_d); +} + +SMB_ACL_T sys_acl_get_file( const char *path_p, SMB_ACL_TYPE_T type) +{ + return acl_get_file( path_p, type); +} + +SMB_ACL_T sys_acl_get_fd(int fd) +{ + return acl_get_fd(fd); +} + +int sys_acl_clear_perms(SMB_ACL_PERMSET_T permset) +{ + return acl_clear_perms(permset); +} + +int sys_acl_add_perm( SMB_ACL_PERMSET_T permset, SMB_ACL_PERM_T perm) +{ + return acl_add_perm(permset, perm); +} + +int sys_acl_get_perm( SMB_ACL_PERMSET_T permset, SMB_ACL_PERM_T perm) +{ +#if defined(HAVE_ACL_GET_PERM_NP) + /* + * Required for TrustedBSD-based ACL implementations where + * non-POSIX.1e functions are denoted by a _np (non-portable) + * suffix. + */ + return acl_get_perm_np(permset, perm); +#else + return acl_get_perm(permset, perm); +#endif +} + +char *sys_acl_to_text( SMB_ACL_T the_acl, ssize_t *plen) +{ + return acl_to_text( the_acl, plen); +} + +SMB_ACL_T sys_acl_init( int count) +{ + return acl_init(count); +} + +int sys_acl_create_entry( SMB_ACL_T *pacl, SMB_ACL_ENTRY_T *pentry) +{ + return acl_create_entry(pacl, pentry); +} + +int sys_acl_set_tag_type( SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T tagtype) +{ + return acl_set_tag_type(entry, tagtype); +} + +int sys_acl_set_qualifier( SMB_ACL_ENTRY_T entry, void *qual) +{ + return acl_set_qualifier(entry, qual); +} + +int sys_acl_set_permset( SMB_ACL_ENTRY_T entry, SMB_ACL_PERMSET_T permset) +{ + return acl_set_permset(entry, permset); +} + +int sys_acl_valid( SMB_ACL_T theacl ) +{ + return acl_valid(theacl); +} + +int sys_acl_set_file(const char *name, SMB_ACL_TYPE_T acltype, SMB_ACL_T theacl) +{ + return acl_set_file(name, acltype, theacl); +} + +int sys_acl_set_fd( int fd, SMB_ACL_T theacl) +{ + return acl_set_fd(fd, theacl); +} + +int sys_acl_delete_def_file(const char *name) +{ + return acl_delete_def_file(name); +} + +int sys_acl_free_text(char *text) +{ + return acl_free(text); +} + +int sys_acl_free_acl(SMB_ACL_T the_acl) +{ + return acl_free(the_acl); +} + +int sys_acl_free_qualifier(void *qual, SMB_ACL_TAG_T tagtype) +{ + return acl_free(qual); +} + +#elif defined(HAVE_TRU64_ACLS) +/* + * The interface to DEC/Compaq Tru64 UNIX ACLs + * is based on Draft 13 of the POSIX spec which is + * slightly different from the Draft 16 interface. + * + * Also, some of the permset manipulation functions + * such as acl_clear_perm() and acl_add_perm() appear + * to be broken on Tru64 so we have to manipulate + * the permission bits in the permset directly. + */ +int sys_acl_get_entry( SMB_ACL_T the_acl, int entry_id, SMB_ACL_ENTRY_T *entry_p) +{ + SMB_ACL_ENTRY_T entry; + + if (entry_id == SMB_ACL_FIRST_ENTRY && acl_first_entry(the_acl) != 0) { + return -1; + } + + errno = 0; + if ((entry = acl_get_entry(the_acl)) != NULL) { + *entry_p = entry; + return 1; + } + + return errno ? -1 : 0; +} + +int sys_acl_get_tag_type( SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T *tag_type_p) +{ + return acl_get_tag_type( entry_d, tag_type_p); +} + +int sys_acl_get_permset( SMB_ACL_ENTRY_T entry_d, SMB_ACL_PERMSET_T *permset_p) +{ + return acl_get_permset( entry_d, permset_p); +} + +void *sys_acl_get_qualifier( SMB_ACL_ENTRY_T entry_d) +{ + return acl_get_qualifier( entry_d); +} + +SMB_ACL_T sys_acl_get_file( const char *path_p, SMB_ACL_TYPE_T type) +{ + return acl_get_file((char *)path_p, type); +} + +SMB_ACL_T sys_acl_get_fd(int fd) +{ + return acl_get_fd(fd, ACL_TYPE_ACCESS); +} + +int sys_acl_clear_perms(SMB_ACL_PERMSET_T permset) +{ + *permset = 0; /* acl_clear_perm() is broken on Tru64 */ + + return 0; +} + +int sys_acl_add_perm( SMB_ACL_PERMSET_T permset, SMB_ACL_PERM_T perm) +{ + if (perm & ~(SMB_ACL_READ | SMB_ACL_WRITE | SMB_ACL_EXECUTE)) { + errno = EINVAL; + return -1; + } + + *permset |= perm; /* acl_add_perm() is broken on Tru64 */ + + return 0; +} + +int sys_acl_get_perm( SMB_ACL_PERMSET_T permset, SMB_ACL_PERM_T perm) +{ + return *permset & perm; /* Tru64 doesn't have acl_get_perm() */ +} + +char *sys_acl_to_text( SMB_ACL_T the_acl, ssize_t *plen) +{ + return acl_to_text( the_acl, plen); +} + +SMB_ACL_T sys_acl_init( int count) +{ + return acl_init(count); +} + +int sys_acl_create_entry( SMB_ACL_T *pacl, SMB_ACL_ENTRY_T *pentry) +{ + SMB_ACL_ENTRY_T entry; + + if ((entry = acl_create_entry(pacl)) == NULL) { + return -1; + } + + *pentry = entry; + return 0; +} + +int sys_acl_set_tag_type( SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T tagtype) +{ + return acl_set_tag_type(entry, tagtype); +} + +int sys_acl_set_qualifier( SMB_ACL_ENTRY_T entry, void *qual) +{ + return acl_set_qualifier(entry, qual); +} + +int sys_acl_set_permset( SMB_ACL_ENTRY_T entry, SMB_ACL_PERMSET_T permset) +{ + return acl_set_permset(entry, permset); +} + +int sys_acl_valid( SMB_ACL_T theacl ) +{ + acl_entry_t entry; + + return acl_valid(theacl, &entry); +} + +int sys_acl_set_file( const char *name, SMB_ACL_TYPE_T acltype, SMB_ACL_T theacl) +{ + return acl_set_file((char *)name, acltype, theacl); +} + +int sys_acl_set_fd( int fd, SMB_ACL_T theacl) +{ + return acl_set_fd(fd, ACL_TYPE_ACCESS, theacl); +} + +int sys_acl_delete_def_file(const char *name) +{ + return acl_delete_def_file((char *)name); +} + +int sys_acl_free_text(char *text) +{ + /* + * (void) cast and explicit return 0 are for DEC UNIX + * which just #defines acl_free_text() to be free() + */ + (void) acl_free_text(text); + return 0; +} + +int sys_acl_free_acl(SMB_ACL_T the_acl) +{ + return acl_free(the_acl); +} + +int sys_acl_free_qualifier(void *qual, SMB_ACL_TAG_T tagtype) +{ + return acl_free_qualifier(qual, tagtype); +} + +#elif defined(HAVE_UNIXWARE_ACLS) || defined(HAVE_SOLARIS_ACLS) + +/* + * Donated by Michael Davidson <md@sco.COM> for UnixWare / OpenUNIX. + * Modified by Toomas Soome <tsoome@ut.ee> for Solaris. + */ + +/* + * Note that while this code implements sufficient functionality + * to support the sys_acl_* interfaces it does not provide all + * of the semantics of the POSIX ACL interfaces. + * + * In particular, an ACL entry descriptor (SMB_ACL_ENTRY_T) returned + * from a call to sys_acl_get_entry() should not be assumed to be + * valid after calling any of the following functions, which may + * reorder the entries in the ACL. + * + * sys_acl_valid() + * sys_acl_set_file() + * sys_acl_set_fd() + */ + +/* + * The only difference between Solaris and UnixWare / OpenUNIX is + * that the #defines for the ACL operations have different names + */ +#if defined(HAVE_UNIXWARE_ACLS) + +#define SETACL ACL_SET +#define GETACL ACL_GET +#define GETACLCNT ACL_CNT + +#endif + + +int sys_acl_get_entry(SMB_ACL_T acl_d, int entry_id, SMB_ACL_ENTRY_T *entry_p) +{ + if (entry_id != SMB_ACL_FIRST_ENTRY && entry_id != SMB_ACL_NEXT_ENTRY) { + errno = EINVAL; + return -1; + } + + if (entry_p == NULL) { + errno = EINVAL; + return -1; + } + + if (entry_id == SMB_ACL_FIRST_ENTRY) { + acl_d->next = 0; + } + + if (acl_d->next < 0) { + errno = EINVAL; + return -1; + } + + if (acl_d->next >= acl_d->count) { + return 0; + } + + *entry_p = &acl_d->acl[acl_d->next++]; + + return 1; +} + +int sys_acl_get_tag_type(SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T *type_p) +{ + *type_p = entry_d->a_type; + + return 0; +} + +int sys_acl_get_permset(SMB_ACL_ENTRY_T entry_d, SMB_ACL_PERMSET_T *permset_p) +{ + *permset_p = &entry_d->a_perm; + + return 0; +} + +void *sys_acl_get_qualifier(SMB_ACL_ENTRY_T entry_d) +{ + if (entry_d->a_type != SMB_ACL_USER + && entry_d->a_type != SMB_ACL_GROUP) { + errno = EINVAL; + return NULL; + } + + return &entry_d->a_id; +} + +/* + * There is no way of knowing what size the ACL returned by + * GETACL will be unless you first call GETACLCNT which means + * making an additional system call. + * + * In the hope of avoiding the cost of the additional system + * call in most cases, we initially allocate enough space for + * an ACL with INITIAL_ACL_SIZE entries. If this turns out to + * be too small then we use GETACLCNT to find out the actual + * size, reallocate the ACL buffer, and then call GETACL again. + */ + +#define INITIAL_ACL_SIZE 16 + +SMB_ACL_T sys_acl_get_file(const char *path_p, SMB_ACL_TYPE_T type) +{ + SMB_ACL_T acl_d; + int count; /* # of ACL entries allocated */ + int naccess; /* # of access ACL entries */ + int ndefault; /* # of default ACL entries */ + + if (type != SMB_ACL_TYPE_ACCESS && type != SMB_ACL_TYPE_DEFAULT) { + errno = EINVAL; + return NULL; + } + + count = INITIAL_ACL_SIZE; + if ((acl_d = sys_acl_init(count)) == NULL) { + return NULL; + } + + /* + * If there isn't enough space for the ACL entries we use + * GETACLCNT to determine the actual number of ACL entries + * reallocate and try again. This is in a loop because it + * is possible that someone else could modify the ACL and + * increase the number of entries between the call to + * GETACLCNT and the call to GETACL. + */ + while ((count = acl(path_p, GETACL, count, &acl_d->acl[0])) < 0 + && errno == ENOSPC) { + + sys_acl_free_acl(acl_d); + + if ((count = acl(path_p, GETACLCNT, 0, NULL)) < 0) { + return NULL; + } + + if ((acl_d = sys_acl_init(count)) == NULL) { + return NULL; + } + } + + if (count < 0) { + sys_acl_free_acl(acl_d); + return NULL; + } + + /* + * calculate the number of access and default ACL entries + * + * Note: we assume that the acl() system call returned a + * well formed ACL which is sorted so that all of the + * access ACL entries preceed any default ACL entries + */ + for (naccess = 0; naccess < count; naccess++) { + if (acl_d->acl[naccess].a_type & ACL_DEFAULT) + break; + } + ndefault = count - naccess; + + /* + * if the caller wants the default ACL we have to copy + * the entries down to the start of the acl[] buffer + * and mask out the ACL_DEFAULT flag from the type field + */ + if (type == SMB_ACL_TYPE_DEFAULT) { + int i, j; + + for (i = 0, j = naccess; i < ndefault; i++, j++) { + acl_d->acl[i] = acl_d->acl[j]; + acl_d->acl[i].a_type &= ~ACL_DEFAULT; + } + + acl_d->count = ndefault; + } else { + acl_d->count = naccess; + } + + return acl_d; +} + +SMB_ACL_T sys_acl_get_fd(int fd) +{ + SMB_ACL_T acl_d; + int count; /* # of ACL entries allocated */ + int naccess; /* # of access ACL entries */ + + count = INITIAL_ACL_SIZE; + if ((acl_d = sys_acl_init(count)) == NULL) { + return NULL; + } + + while ((count = facl(fd, GETACL, count, &acl_d->acl[0])) < 0 + && errno == ENOSPC) { + + sys_acl_free_acl(acl_d); + + if ((count = facl(fd, GETACLCNT, 0, NULL)) < 0) { + return NULL; + } + + if ((acl_d = sys_acl_init(count)) == NULL) { + return NULL; + } + } + + if (count < 0) { + sys_acl_free_acl(acl_d); + return NULL; + } + + /* + * calculate the number of access ACL entries + */ + for (naccess = 0; naccess < count; naccess++) { + if (acl_d->acl[naccess].a_type & ACL_DEFAULT) + break; + } + + acl_d->count = naccess; + + return acl_d; +} + +int sys_acl_clear_perms(SMB_ACL_PERMSET_T permset_d) +{ + *permset_d = 0; + + return 0; +} + +int sys_acl_add_perm(SMB_ACL_PERMSET_T permset_d, SMB_ACL_PERM_T perm) +{ + if (perm != SMB_ACL_READ && perm != SMB_ACL_WRITE + && perm != SMB_ACL_EXECUTE) { + errno = EINVAL; + return -1; + } + + if (permset_d == NULL) { + errno = EINVAL; + return -1; + } + + *permset_d |= perm; + + return 0; +} + +int sys_acl_get_perm(SMB_ACL_PERMSET_T permset_d, SMB_ACL_PERM_T perm) +{ + return *permset_d & perm; +} + +char *sys_acl_to_text(SMB_ACL_T acl_d, ssize_t *len_p) +{ + int i; + int len, maxlen; + char *text; + + /* + * use an initial estimate of 20 bytes per ACL entry + * when allocating memory for the text representation + * of the ACL + */ + len = 0; + maxlen = 20 * acl_d->count; + if ((text = malloc(maxlen)) == NULL) { + errno = ENOMEM; + return NULL; + } + + for (i = 0; i < acl_d->count; i++) { + struct acl *ap = &acl_d->acl[i]; + struct passwd *pw; + struct group *gr; + char tagbuf[12]; + char idbuf[12]; + char *tag; + char *id = ""; + char perms[4]; + int nbytes; + + switch (ap->a_type) { + /* + * for debugging purposes it's probably more + * useful to dump unknown tag types rather + * than just returning an error + */ + default: + slprintf(tagbuf, sizeof(tagbuf)-1, "0x%x", + ap->a_type); + tag = tagbuf; + slprintf(idbuf, sizeof(idbuf)-1, "%ld", + (long)ap->a_id); + id = idbuf; + break; + + case SMB_ACL_USER: + id = uidtoname(ap->a_id); + case SMB_ACL_USER_OBJ: + tag = "user"; + break; + + case SMB_ACL_GROUP: + if ((gr = getgrgid(ap->a_id)) == NULL) { + slprintf(idbuf, sizeof(idbuf)-1, "%ld", + (long)ap->a_id); + id = idbuf; + } else { + id = gr->gr_name; + } + case SMB_ACL_GROUP_OBJ: + tag = "group"; + break; + + case SMB_ACL_OTHER: + tag = "other"; + break; + + case SMB_ACL_MASK: + tag = "mask"; + break; + + } + + perms[0] = (ap->a_perm & SMB_ACL_READ) ? 'r' : '-'; + perms[1] = (ap->a_perm & SMB_ACL_WRITE) ? 'w' : '-'; + perms[2] = (ap->a_perm & SMB_ACL_EXECUTE) ? 'x' : '-'; + perms[3] = '\0'; + + /* <tag> : <qualifier> : rwx \n \0 */ + nbytes = strlen(tag) + 1 + strlen(id) + 1 + 3 + 1 + 1; + + /* + * If this entry would overflow the buffer + * allocate enough additional memory for this + * entry and an estimate of another 20 bytes + * for each entry still to be processed + */ + if ((len + nbytes) > maxlen) { + char *oldtext = text; + + maxlen += nbytes + 20 * (acl_d->count - i); + + if ((text = Realloc(oldtext, maxlen)) == NULL) { + SAFE_FREE(oldtext); + errno = ENOMEM; + return NULL; + } + } + + slprintf(&text[len], nbytes-1, "%s:%s:%s\n", tag, id, perms); + len += nbytes - 1; + } + + if (len_p) + *len_p = len; + + return text; +} + +SMB_ACL_T sys_acl_init(int count) +{ + SMB_ACL_T a; + + if (count < 0) { + errno = EINVAL; + return NULL; + } + + /* + * note that since the definition of the structure pointed + * to by the SMB_ACL_T includes the first element of the + * acl[] array, this actually allocates an ACL with room + * for (count+1) entries + */ + if ((a = malloc(sizeof(*a) + count * sizeof(struct acl))) == NULL) { + errno = ENOMEM; + return NULL; + } + + a->size = count + 1; + a->count = 0; + a->next = -1; + + return a; +} + + +int sys_acl_create_entry(SMB_ACL_T *acl_p, SMB_ACL_ENTRY_T *entry_p) +{ + SMB_ACL_T acl_d; + SMB_ACL_ENTRY_T entry_d; + + if (acl_p == NULL || entry_p == NULL || (acl_d = *acl_p) == NULL) { + errno = EINVAL; + return -1; + } + + if (acl_d->count >= acl_d->size) { + errno = ENOSPC; + return -1; + } + + entry_d = &acl_d->acl[acl_d->count++]; + entry_d->a_type = 0; + entry_d->a_id = -1; + entry_d->a_perm = 0; + *entry_p = entry_d; + + return 0; +} + +int sys_acl_set_tag_type(SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T tag_type) +{ + switch (tag_type) { + case SMB_ACL_USER: + case SMB_ACL_USER_OBJ: + case SMB_ACL_GROUP: + case SMB_ACL_GROUP_OBJ: + case SMB_ACL_OTHER: + case SMB_ACL_MASK: + entry_d->a_type = tag_type; + break; + default: + errno = EINVAL; + return -1; + } + + return 0; +} + +int sys_acl_set_qualifier(SMB_ACL_ENTRY_T entry_d, void *qual_p) +{ + if (entry_d->a_type != SMB_ACL_GROUP + && entry_d->a_type != SMB_ACL_USER) { + errno = EINVAL; + return -1; + } + + entry_d->a_id = *((id_t *)qual_p); + + return 0; +} + +int sys_acl_set_permset(SMB_ACL_ENTRY_T entry_d, SMB_ACL_PERMSET_T permset_d) +{ + if (*permset_d & ~(SMB_ACL_READ|SMB_ACL_WRITE|SMB_ACL_EXECUTE)) { + return EINVAL; + } + + entry_d->a_perm = *permset_d; + + return 0; +} + +/* + * sort the ACL and check it for validity + * + * if it's a minimal ACL with only 4 entries then we + * need to recalculate the mask permissions to make + * sure that they are the same as the GROUP_OBJ + * permissions as required by the UnixWare acl() system call. + * + * (note: since POSIX allows minimal ACLs which only contain + * 3 entries - ie there is no mask entry - we should, in theory, + * check for this and add a mask entry if necessary - however + * we "know" that the caller of this interface always specifies + * a mask so, in practice "this never happens" (tm) - if it *does* + * happen aclsort() will fail and return an error and someone will + * have to fix it ...) + */ + +static int acl_sort(SMB_ACL_T acl_d) +{ + int fixmask = (acl_d->count <= 4); + + if (aclsort(acl_d->count, fixmask, acl_d->acl) != 0) { + errno = EINVAL; + return -1; + } + return 0; +} + +int sys_acl_valid(SMB_ACL_T acl_d) +{ + return acl_sort(acl_d); +} + +int sys_acl_set_file(const char *name, SMB_ACL_TYPE_T type, SMB_ACL_T acl_d) +{ + struct stat s; + struct acl *acl_p; + int acl_count; + struct acl *acl_buf = NULL; + int ret; + + if (type != SMB_ACL_TYPE_ACCESS && type != SMB_ACL_TYPE_DEFAULT) { + errno = EINVAL; + return -1; + } + + if (acl_sort(acl_d) != 0) { + return -1; + } + + acl_p = &acl_d->acl[0]; + acl_count = acl_d->count; + + /* + * if it's a directory there is extra work to do + * since the acl() system call will replace both + * the access ACLs and the default ACLs (if any) + */ + if (stat(name, &s) != 0) { + return -1; + } + if (S_ISDIR(s.st_mode)) { + SMB_ACL_T acc_acl; + SMB_ACL_T def_acl; + SMB_ACL_T tmp_acl; + int i; + + if (type == SMB_ACL_TYPE_ACCESS) { + acc_acl = acl_d; + def_acl = tmp_acl = sys_acl_get_file(name, SMB_ACL_TYPE_DEFAULT); + + } else { + def_acl = acl_d; + acc_acl = tmp_acl = sys_acl_get_file(name, SMB_ACL_TYPE_ACCESS); + } + + if (tmp_acl == NULL) { + return -1; + } + + /* + * allocate a temporary buffer for the complete ACL + */ + acl_count = acc_acl->count + def_acl->count; + acl_p = acl_buf = malloc(acl_count * sizeof(acl_buf[0])); + + if (acl_buf == NULL) { + sys_acl_free_acl(tmp_acl); + errno = ENOMEM; + return -1; + } + + /* + * copy the access control and default entries into the buffer + */ + memcpy(&acl_buf[0], &acc_acl->acl[0], + acc_acl->count * sizeof(acl_buf[0])); + + memcpy(&acl_buf[acc_acl->count], &def_acl->acl[0], + def_acl->count * sizeof(acl_buf[0])); + + /* + * set the ACL_DEFAULT flag on the default entries + */ + for (i = acc_acl->count; i < acl_count; i++) { + acl_buf[i].a_type |= ACL_DEFAULT; + } + + sys_acl_free_acl(tmp_acl); + + } else if (type != SMB_ACL_TYPE_ACCESS) { + errno = EINVAL; + return -1; + } + + ret = acl(name, SETACL, acl_count, acl_p); + + SAFE_FREE(acl_buf); + + return ret; +} + +int sys_acl_set_fd(int fd, SMB_ACL_T acl_d) +{ + if (acl_sort(acl_d) != 0) { + return -1; + } + + return facl(fd, SETACL, acl_d->count, &acl_d->acl[0]); +} + +int sys_acl_delete_def_file(const char *path) +{ + SMB_ACL_T acl_d; + int ret; + + /* + * fetching the access ACL and rewriting it has + * the effect of deleting the default ACL + */ + if ((acl_d = sys_acl_get_file(path, SMB_ACL_TYPE_ACCESS)) == NULL) { + return -1; + } + + ret = acl(path, SETACL, acl_d->count, acl_d->acl); + + sys_acl_free_acl(acl_d); + + return ret; +} + +int sys_acl_free_text(char *text) +{ + SAFE_FREE(text); + return 0; +} + +int sys_acl_free_acl(SMB_ACL_T acl_d) +{ + SAFE_FREE(acl_d); + return 0; +} + +int sys_acl_free_qualifier(void *qual, SMB_ACL_TAG_T tagtype) +{ + return 0; +} + +#elif defined(HAVE_HPUX_ACLS) +#include <dl.h> + +/* + * Based on the Solaris/SCO code - with modifications. + */ + +/* + * Note that while this code implements sufficient functionality + * to support the sys_acl_* interfaces it does not provide all + * of the semantics of the POSIX ACL interfaces. + * + * In particular, an ACL entry descriptor (SMB_ACL_ENTRY_T) returned + * from a call to sys_acl_get_entry() should not be assumed to be + * valid after calling any of the following functions, which may + * reorder the entries in the ACL. + * + * sys_acl_valid() + * sys_acl_set_file() + * sys_acl_set_fd() + */ + +/* This checks if the POSIX ACL system call is defined */ +/* which basically corresponds to whether JFS 3.3 or */ +/* higher is installed. If acl() was called when it */ +/* isn't defined, it causes the process to core dump */ +/* so it is important to check this and avoid acl() */ +/* calls if it isn't there. */ + +static BOOL hpux_acl_call_presence(void) +{ + + shl_t handle = NULL; + void *value; + int ret_val=0; + //static BOOL already_checked=0; + // REWRITE: add this back in?? + //if(already_checked) + // return True; + + + ret_val = shl_findsym(&handle, "acl", TYPE_PROCEDURE, &value); + + if(ret_val != 0) { + DEBUG(5, ("hpux_acl_call_presence: shl_findsym() returned %d, errno = %d, error %s\n", + ret_val, errno, strerror(errno))); + DEBUG(5,("hpux_acl_call_presence: acl() system call is not present. Check if you have JFS 3.3 and above?\n")); + return False; + } + + DEBUG(10,("hpux_acl_call_presence: acl() system call is present. We have JFS 3.3 or above \n")); + + already_checked = True; + return True; +} + +int sys_acl_get_entry(SMB_ACL_T acl_d, int entry_id, SMB_ACL_ENTRY_T *entry_p) +{ + if (entry_id != SMB_ACL_FIRST_ENTRY && entry_id != SMB_ACL_NEXT_ENTRY) { + errno = EINVAL; + return -1; + } + + if (entry_p == NULL) { + errno = EINVAL; + return -1; + } + + if (entry_id == SMB_ACL_FIRST_ENTRY) { + acl_d->next = 0; + } + + if (acl_d->next < 0) { + errno = EINVAL; + return -1; + } + + if (acl_d->next >= acl_d->count) { + return 0; + } + + *entry_p = &acl_d->acl[acl_d->next++]; + + return 1; +} + +int sys_acl_get_tag_type(SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T *type_p) +{ + *type_p = entry_d->a_type; + + return 0; +} + +int sys_acl_get_permset(SMB_ACL_ENTRY_T entry_d, SMB_ACL_PERMSET_T *permset_p) +{ + *permset_p = &entry_d->a_perm; + + return 0; +} + +void *sys_acl_get_qualifier(SMB_ACL_ENTRY_T entry_d) +{ + if (entry_d->a_type != SMB_ACL_USER + && entry_d->a_type != SMB_ACL_GROUP) { + errno = EINVAL; + return NULL; + } + + return &entry_d->a_id; +} + +/* + * There is no way of knowing what size the ACL returned by + * ACL_GET will be unless you first call ACL_CNT which means + * making an additional system call. + * + * In the hope of avoiding the cost of the additional system + * call in most cases, we initially allocate enough space for + * an ACL with INITIAL_ACL_SIZE entries. If this turns out to + * be too small then we use ACL_CNT to find out the actual + * size, reallocate the ACL buffer, and then call ACL_GET again. + */ + +#define INITIAL_ACL_SIZE 16 + +SMB_ACL_T sys_acl_get_file(const char *path_p, SMB_ACL_TYPE_T type) +{ + SMB_ACL_T acl_d; + int count; /* # of ACL entries allocated */ + int naccess; /* # of access ACL entries */ + int ndefault; /* # of default ACL entries */ + + if(hpux_acl_call_presence() == False) { + /* Looks like we don't have the acl() system call on HPUX. + * May be the system doesn't have the latest version of JFS. + */ + return NULL; + } + + if (type != SMB_ACL_TYPE_ACCESS && type != SMB_ACL_TYPE_DEFAULT) { + errno = EINVAL; + return NULL; + } + + count = INITIAL_ACL_SIZE; + if ((acl_d = sys_acl_init(count)) == NULL) { + return NULL; + } + + /* + * If there isn't enough space for the ACL entries we use + * ACL_CNT to determine the actual number of ACL entries + * reallocate and try again. This is in a loop because it + * is possible that someone else could modify the ACL and + * increase the number of entries between the call to + * ACL_CNT and the call to ACL_GET. + */ + while ((count = acl(path_p, ACL_GET, count, &acl_d->acl[0])) < 0 && errno == ENOSPC) { + + sys_acl_free_acl(acl_d); + + if ((count = acl(path_p, ACL_CNT, 0, NULL)) < 0) { + return NULL; + } + + if ((acl_d = sys_acl_init(count)) == NULL) { + return NULL; + } + } + + if (count < 0) { + sys_acl_free_acl(acl_d); + return NULL; + } + + /* + * calculate the number of access and default ACL entries + * + * Note: we assume that the acl() system call returned a + * well formed ACL which is sorted so that all of the + * access ACL entries preceed any default ACL entries + */ + for (naccess = 0; naccess < count; naccess++) { + if (acl_d->acl[naccess].a_type & ACL_DEFAULT) + break; + } + ndefault = count - naccess; + + /* + * if the caller wants the default ACL we have to copy + * the entries down to the start of the acl[] buffer + * and mask out the ACL_DEFAULT flag from the type field + */ + if (type == SMB_ACL_TYPE_DEFAULT) { + int i, j; + + for (i = 0, j = naccess; i < ndefault; i++, j++) { + acl_d->acl[i] = acl_d->acl[j]; + acl_d->acl[i].a_type &= ~ACL_DEFAULT; + } + + acl_d->count = ndefault; + } else { + acl_d->count = naccess; + } + + return acl_d; +} + +SMB_ACL_T sys_acl_get_fd(int fd) +{ + /* + * HPUX doesn't have the facl call. Fake it using the path.... JRA. + */ + + files_struct *fsp = file_find_fd(fd); + + if (fsp == NULL) { + errno = EBADF; + return NULL; + } + + /* + * We know we're in the same conn context. So we + * can use the relative path. + */ + + return sys_acl_get_file(fsp->fsp_name, SMB_ACL_TYPE_ACCESS); +} + +int sys_acl_clear_perms(SMB_ACL_PERMSET_T permset_d) +{ + *permset_d = 0; + + return 0; +} + +int sys_acl_add_perm(SMB_ACL_PERMSET_T permset_d, SMB_ACL_PERM_T perm) +{ + if (perm != SMB_ACL_READ && perm != SMB_ACL_WRITE + && perm != SMB_ACL_EXECUTE) { + errno = EINVAL; + return -1; + } + + if (permset_d == NULL) { + errno = EINVAL; + return -1; + } + + *permset_d |= perm; + + return 0; +} + +int sys_acl_get_perm(SMB_ACL_PERMSET_T permset_d, SMB_ACL_PERM_T perm) +{ + return *permset_d & perm; +} + +char *sys_acl_to_text(SMB_ACL_T acl_d, ssize_t *len_p) +{ + int i; + int len, maxlen; + char *text; + + /* + * use an initial estimate of 20 bytes per ACL entry + * when allocating memory for the text representation + * of the ACL + */ + len = 0; + maxlen = 20 * acl_d->count; + if ((text = malloc(maxlen)) == NULL) { + errno = ENOMEM; + return NULL; + } + + for (i = 0; i < acl_d->count; i++) { + struct acl *ap = &acl_d->acl[i]; + struct passwd *pw; + struct group *gr; + char tagbuf[12]; + char idbuf[12]; + char *tag; + char *id = ""; + char perms[4]; + int nbytes; + + switch (ap->a_type) { + /* + * for debugging purposes it's probably more + * useful to dump unknown tag types rather + * than just returning an error + */ + default: + slprintf(tagbuf, sizeof(tagbuf)-1, "0x%x", + ap->a_type); + tag = tagbuf; + slprintf(idbuf, sizeof(idbuf)-1, "%ld", + (long)ap->a_id); + id = idbuf; + break; + + case SMB_ACL_USER: + id = uidtoname(ap->a_id); + case SMB_ACL_USER_OBJ: + tag = "user"; + break; + + case SMB_ACL_GROUP: + if ((gr = getgrgid(ap->a_id)) == NULL) { + slprintf(idbuf, sizeof(idbuf)-1, "%ld", + (long)ap->a_id); + id = idbuf; + } else { + id = gr->gr_name; + } + case SMB_ACL_GROUP_OBJ: + tag = "group"; + break; + + case SMB_ACL_OTHER: + tag = "other"; + break; + + case SMB_ACL_MASK: + tag = "mask"; + break; + + } + + perms[0] = (ap->a_perm & SMB_ACL_READ) ? 'r' : '-'; + perms[1] = (ap->a_perm & SMB_ACL_WRITE) ? 'w' : '-'; + perms[2] = (ap->a_perm & SMB_ACL_EXECUTE) ? 'x' : '-'; + perms[3] = '\0'; + + /* <tag> : <qualifier> : rwx \n \0 */ + nbytes = strlen(tag) + 1 + strlen(id) + 1 + 3 + 1 + 1; + + /* + * If this entry would overflow the buffer + * allocate enough additional memory for this + * entry and an estimate of another 20 bytes + * for each entry still to be processed + */ + if ((len + nbytes) > maxlen) { + char *oldtext = text; + + maxlen += nbytes + 20 * (acl_d->count - i); + + if ((text = Realloc(oldtext, maxlen)) == NULL) { + free(oldtext); + errno = ENOMEM; + return NULL; + } + } + + slprintf(&text[len], nbytes-1, "%s:%s:%s\n", tag, id, perms); + len += nbytes - 1; + } + + if (len_p) + *len_p = len; + + return text; +} + +SMB_ACL_T sys_acl_init(int count) +{ + SMB_ACL_T a; + + if (count < 0) { + errno = EINVAL; + return NULL; + } + + /* + * note that since the definition of the structure pointed + * to by the SMB_ACL_T includes the first element of the + * acl[] array, this actually allocates an ACL with room + * for (count+1) entries + */ + if ((a = malloc(sizeof(*a) + count * sizeof(struct acl))) == NULL) { + errno = ENOMEM; + return NULL; + } + + a->size = count + 1; + a->count = 0; + a->next = -1; + + return a; +} + + +int sys_acl_create_entry(SMB_ACL_T *acl_p, SMB_ACL_ENTRY_T *entry_p) +{ + SMB_ACL_T acl_d; + SMB_ACL_ENTRY_T entry_d; + + if (acl_p == NULL || entry_p == NULL || (acl_d = *acl_p) == NULL) { + errno = EINVAL; + return -1; + } + + if (acl_d->count >= acl_d->size) { + errno = ENOSPC; + return -1; + } + + entry_d = &acl_d->acl[acl_d->count++]; + entry_d->a_type = 0; + entry_d->a_id = -1; + entry_d->a_perm = 0; + *entry_p = entry_d; + + return 0; +} + +int sys_acl_set_tag_type(SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T tag_type) +{ + switch (tag_type) { + case SMB_ACL_USER: + case SMB_ACL_USER_OBJ: + case SMB_ACL_GROUP: + case SMB_ACL_GROUP_OBJ: + case SMB_ACL_OTHER: + case SMB_ACL_MASK: + entry_d->a_type = tag_type; + break; + default: + errno = EINVAL; + return -1; + } + + return 0; +} + +int sys_acl_set_qualifier(SMB_ACL_ENTRY_T entry_d, void *qual_p) +{ + if (entry_d->a_type != SMB_ACL_GROUP + && entry_d->a_type != SMB_ACL_USER) { + errno = EINVAL; + return -1; + } + + entry_d->a_id = *((id_t *)qual_p); + + return 0; +} + +int sys_acl_set_permset(SMB_ACL_ENTRY_T entry_d, SMB_ACL_PERMSET_T permset_d) +{ + if (*permset_d & ~(SMB_ACL_READ|SMB_ACL_WRITE|SMB_ACL_EXECUTE)) { + return EINVAL; + } + + entry_d->a_perm = *permset_d; + + return 0; +} + +/* Structure to capture the count for each type of ACE. */ + +struct hpux_acl_types { + int n_user; + int n_def_user; + int n_user_obj; + int n_def_user_obj; + + int n_group; + int n_def_group; + int n_group_obj; + int n_def_group_obj; + + int n_other; + int n_other_obj; + int n_def_other_obj; + + int n_class_obj; + int n_def_class_obj; + + int n_illegal_obj; +}; + +/* count_obj: + * Counts the different number of objects in a given array of ACL + * structures. + * Inputs: + * + * acl_count - Count of ACLs in the array of ACL strucutres. + * aclp - Array of ACL structures. + * acl_type_count - Pointer to acl_types structure. Should already be + * allocated. + * Output: + * + * acl_type_count - This structure is filled up with counts of various + * acl types. + */ + +static int hpux_count_obj(int acl_count, struct acl *aclp, struct hpux_acl_types *acl_type_count) +{ + int i; + + memset(acl_type_count, 0, sizeof(struct hpux_acl_types)); + + for(i=0;i<acl_count;i++) { + switch(aclp[i].a_type) { + case USER: + acl_type_count->n_user++; + break; + case USER_OBJ: + acl_type_count->n_user_obj++; + break; + case DEF_USER_OBJ: + acl_type_count->n_def_user_obj++; + break; + case GROUP: + acl_type_count->n_group++; + break; + case GROUP_OBJ: + acl_type_count->n_group_obj++; + break; + case DEF_GROUP_OBJ: + acl_type_count->n_def_group_obj++; + break; + case OTHER_OBJ: + acl_type_count->n_other_obj++; + break; + case DEF_OTHER_OBJ: + acl_type_count->n_def_other_obj++; + break; + case CLASS_OBJ: + acl_type_count->n_class_obj++; + break; + case DEF_CLASS_OBJ: + acl_type_count->n_def_class_obj++; + break; + case DEF_USER: + acl_type_count->n_def_user++; + break; + case DEF_GROUP: + acl_type_count->n_def_group++; + break; + default: + acl_type_count->n_illegal_obj++; + break; + } + } +} + +/* swap_acl_entries: Swaps two ACL entries. + * + * Inputs: aclp0, aclp1 - ACL entries to be swapped. + */ + +static void hpux_swap_acl_entries(struct acl *aclp0, struct acl *aclp1) +{ + struct acl temp_acl; + + temp_acl.a_type = aclp0->a_type; + temp_acl.a_id = aclp0->a_id; + temp_acl.a_perm = aclp0->a_perm; + + aclp0->a_type = aclp1->a_type; + aclp0->a_id = aclp1->a_id; + aclp0->a_perm = aclp1->a_perm; + + aclp1->a_type = temp_acl.a_type; + aclp1->a_id = temp_acl.a_id; + aclp1->a_perm = temp_acl.a_perm; +} + +/* prohibited_duplicate_type + * Identifies if given ACL type can have duplicate entries or + * not. + * + * Inputs: acl_type - ACL Type. + * + * Outputs: + * + * Return.. + * + * True - If the ACL type matches any of the prohibited types. + * False - If the ACL type doesn't match any of the prohibited types. + */ + +static BOOL hpux_prohibited_duplicate_type(int acl_type) +{ + switch(acl_type) { + case USER: + case GROUP: + case DEF_USER: + case DEF_GROUP: + return True; + default: + return False; + } +} + +/* get_needed_class_perm + * Returns the permissions of a ACL structure only if the ACL + * type matches one of the pre-determined types for computing + * CLASS_OBJ permissions. + * + * Inputs: aclp - Pointer to ACL structure. + */ + +static int hpux_get_needed_class_perm(struct acl *aclp) +{ + switch(aclp->a_type) { + case USER: + case GROUP_OBJ: + case GROUP: + case DEF_USER_OBJ: + case DEF_USER: + case DEF_GROUP_OBJ: + case DEF_GROUP: + case DEF_CLASS_OBJ: + case DEF_OTHER_OBJ: + return aclp->a_perm; + default: + return 0; + } +} + +/* acl_sort for HPUX. + * Sorts the array of ACL structures as per the description in + * aclsort man page. Refer to aclsort man page for more details + * + * Inputs: + * + * acl_count - Count of ACLs in the array of ACL structures. + * calclass - If this is not zero, then we compute the CLASS_OBJ + * permissions. + * aclp - Array of ACL structures. + * + * Outputs: + * + * aclp - Sorted array of ACL structures. + * + * Outputs: + * + * Returns 0 for success -1 for failure. Prints a message to the Samba + * debug log in case of failure. + */ + +static int hpux_acl_sort(int acl_count, int calclass, struct acl *aclp) +{ +#if !defined(HAVE_HPUX_ACLSORT) + /* + * The aclsort() system call is availabe on the latest HPUX General + * Patch Bundles. So for HPUX, we developed our version of acl_sort + * function. Because, we don't want to update to a new + * HPUX GR bundle just for aclsort() call. + */ + + struct hpux_acl_types acl_obj_count; + int n_class_obj_perm = 0; + int i, j; + + if(!acl_count) { + DEBUG(10,("Zero acl count passed. Returning Success\n")); + return 0; + } + + if(aclp == NULL) { + DEBUG(0,("Null ACL pointer in hpux_acl_sort. Returning Failure. \n")); + return -1; + } + + /* Count different types of ACLs in the ACLs array */ + + hpux_count_obj(acl_count, aclp, &acl_obj_count); + + /* There should be only one entry each of type USER_OBJ, GROUP_OBJ, + * CLASS_OBJ and OTHER_OBJ + */ + + if( (acl_obj_count.n_user_obj != 1) || + (acl_obj_count.n_group_obj != 1) || + (acl_obj_count.n_class_obj != 1) || + (acl_obj_count.n_other_obj != 1) + ) { + DEBUG(0,("hpux_acl_sort: More than one entry or no entries for \ +USER OBJ or GROUP_OBJ or OTHER_OBJ or CLASS_OBJ\n")); + return -1; + } + + /* If any of the default objects are present, there should be only + * one of them each. + */ + + if( (acl_obj_count.n_def_user_obj > 1) || (acl_obj_count.n_def_group_obj > 1) || + (acl_obj_count.n_def_other_obj > 1) || (acl_obj_count.n_def_class_obj > 1) ) { + DEBUG(0,("hpux_acl_sort: More than one entry for DEF_CLASS_OBJ \ +or DEF_USER_OBJ or DEF_GROUP_OBJ or DEF_OTHER_OBJ\n")); + return -1; + } + + /* We now have proper number of OBJ and DEF_OBJ entries. Now sort the acl + * structures. + * + * Sorting crieteria - First sort by ACL type. If there are multiple entries of + * same ACL type, sort by ACL id. + * + * I am using the trival kind of sorting method here because, performance isn't + * really effected by the ACLs feature. More over there aren't going to be more + * than 17 entries on HPUX. + */ + + for(i=0; i<acl_count;i++) { + for (j=i+1; j<acl_count; j++) { + if( aclp[i].a_type > aclp[j].a_type ) { + /* ACL entries out of order, swap them */ + + hpux_swap_acl_entries((aclp+i), (aclp+j)); + + } else if ( aclp[i].a_type == aclp[j].a_type ) { + + /* ACL entries of same type, sort by id */ + + if(aclp[i].a_id > aclp[j].a_id) { + hpux_swap_acl_entries((aclp+i), (aclp+j)); + } else if (aclp[i].a_id == aclp[j].a_id) { + /* We have a duplicate entry. */ + if(hpux_prohibited_duplicate_type(aclp[i].a_type)) { + DEBUG(0, ("hpux_acl_sort: Duplicate entry: Type(hex): %x Id: %d\n", + aclp[i].a_type, aclp[i].a_id)); + return -1; + } + } + + } + } + } + + /* set the class obj permissions to the computed one. */ + if(calclass) { + int n_class_obj_index = -1; + + for(i=0;i<acl_count;i++) { + n_class_obj_perm |= hpux_get_needed_class_perm((aclp+i)); + + if(aclp[i].a_type == CLASS_OBJ) + n_class_obj_index = i; + } + aclp[n_class_obj_index].a_perm = n_class_obj_perm; + } + + return 0; +#else + return aclsort(acl_count, calclass, aclp); +#endif +} + +/* + * sort the ACL and check it for validity + * + * if it's a minimal ACL with only 4 entries then we + * need to recalculate the mask permissions to make + * sure that they are the same as the GROUP_OBJ + * permissions as required by the UnixWare acl() system call. + * + * (note: since POSIX allows minimal ACLs which only contain + * 3 entries - ie there is no mask entry - we should, in theory, + * check for this and add a mask entry if necessary - however + * we "know" that the caller of this interface always specifies + * a mask so, in practice "this never happens" (tm) - if it *does* + * happen aclsort() will fail and return an error and someone will + * have to fix it ...) + */ + +static int acl_sort(SMB_ACL_T acl_d) +{ + int fixmask = (acl_d->count <= 4); + + if (hpux_acl_sort(acl_d->count, fixmask, acl_d->acl) != 0) { + errno = EINVAL; + return -1; + } + return 0; +} + +int sys_acl_valid(SMB_ACL_T acl_d) +{ + return acl_sort(acl_d); +} + +int sys_acl_set_file(const char *name, SMB_ACL_TYPE_T type, SMB_ACL_T acl_d) +{ + struct stat s; + struct acl *acl_p; + int acl_count; + struct acl *acl_buf = NULL; + int ret; + + if(hpux_acl_call_presence() == False) { + /* Looks like we don't have the acl() system call on HPUX. + * May be the system doesn't have the latest version of JFS. + */ + errno=ENOSYS; + return -1; + } + + if (type != SMB_ACL_TYPE_ACCESS && type != SMB_ACL_TYPE_DEFAULT) { + errno = EINVAL; + return -1; + } + + if (acl_sort(acl_d) != 0) { + return -1; + } + + acl_p = &acl_d->acl[0]; + acl_count = acl_d->count; + + /* + * if it's a directory there is extra work to do + * since the acl() system call will replace both + * the access ACLs and the default ACLs (if any) + */ + if (stat(name, &s) != 0) { + return -1; + } + if (S_ISDIR(s.st_mode)) { + SMB_ACL_T acc_acl; + SMB_ACL_T def_acl; + SMB_ACL_T tmp_acl; + int i; + + if (type == SMB_ACL_TYPE_ACCESS) { + acc_acl = acl_d; + def_acl = tmp_acl = sys_acl_get_file(name, SMB_ACL_TYPE_DEFAULT); + + } else { + def_acl = acl_d; + acc_acl = tmp_acl = sys_acl_get_file(name, SMB_ACL_TYPE_ACCESS); + } + + if (tmp_acl == NULL) { + return -1; + } + + /* + * allocate a temporary buffer for the complete ACL + */ + acl_count = acc_acl->count + def_acl->count; + acl_p = acl_buf = malloc(acl_count * sizeof(acl_buf[0])); + + if (acl_buf == NULL) { + sys_acl_free_acl(tmp_acl); + errno = ENOMEM; + return -1; + } + + /* + * copy the access control and default entries into the buffer + */ + memcpy(&acl_buf[0], &acc_acl->acl[0], + acc_acl->count * sizeof(acl_buf[0])); + + memcpy(&acl_buf[acc_acl->count], &def_acl->acl[0], + def_acl->count * sizeof(acl_buf[0])); + + /* + * set the ACL_DEFAULT flag on the default entries + */ + for (i = acc_acl->count; i < acl_count; i++) { + acl_buf[i].a_type |= ACL_DEFAULT; + } + + sys_acl_free_acl(tmp_acl); + + } else if (type != SMB_ACL_TYPE_ACCESS) { + errno = EINVAL; + return -1; + } + + ret = acl(name, ACL_SET, acl_count, acl_p); + + if (acl_buf) { + free(acl_buf); + } + + return ret; +} + +int sys_acl_set_fd(int fd, SMB_ACL_T acl_d) +{ + /* + * HPUX doesn't have the facl call. Fake it using the path.... JRA. + */ + + files_struct *fsp = file_find_fd(fd); + + if (fsp == NULL) { + errno = EBADF; + return NULL; + } + + if (acl_sort(acl_d) != 0) { + return -1; + } + + /* + * We know we're in the same conn context. So we + * can use the relative path. + */ + + return sys_acl_set_file(fsp->fsp_name, SMB_ACL_TYPE_ACCESS, acl_d); +} + +int sys_acl_delete_def_file(const char *path) +{ + SMB_ACL_T acl_d; + int ret; + + /* + * fetching the access ACL and rewriting it has + * the effect of deleting the default ACL + */ + if ((acl_d = sys_acl_get_file(path, SMB_ACL_TYPE_ACCESS)) == NULL) { + return -1; + } + + ret = acl(path, ACL_SET, acl_d->count, acl_d->acl); + + sys_acl_free_acl(acl_d); + + return ret; +} + +int sys_acl_free_text(char *text) +{ + free(text); + return 0; +} + +int sys_acl_free_acl(SMB_ACL_T acl_d) +{ + free(acl_d); + return 0; +} + +int sys_acl_free_qualifier(void *qual, SMB_ACL_TAG_T tagtype) +{ + return 0; +} + +#elif defined(HAVE_IRIX_ACLS) + +int sys_acl_get_entry(SMB_ACL_T acl_d, int entry_id, SMB_ACL_ENTRY_T *entry_p) +{ + if (entry_id != SMB_ACL_FIRST_ENTRY && entry_id != SMB_ACL_NEXT_ENTRY) { + errno = EINVAL; + return -1; + } + + if (entry_p == NULL) { + errno = EINVAL; + return -1; + } + + if (entry_id == SMB_ACL_FIRST_ENTRY) { + acl_d->next = 0; + } + + if (acl_d->next < 0) { + errno = EINVAL; + return -1; + } + + if (acl_d->next >= acl_d->aclp->acl_cnt) { + return 0; + } + + *entry_p = &acl_d->aclp->acl_entry[acl_d->next++]; + + return 1; +} + +int sys_acl_get_tag_type(SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T *type_p) +{ + *type_p = entry_d->ae_tag; + + return 0; +} + +int sys_acl_get_permset(SMB_ACL_ENTRY_T entry_d, SMB_ACL_PERMSET_T *permset_p) +{ + *permset_p = entry_d; + + return 0; +} + +void *sys_acl_get_qualifier(SMB_ACL_ENTRY_T entry_d) +{ + if (entry_d->ae_tag != SMB_ACL_USER + && entry_d->ae_tag != SMB_ACL_GROUP) { + errno = EINVAL; + return NULL; + } + + return &entry_d->ae_id; +} + +SMB_ACL_T sys_acl_get_file(const char *path_p, SMB_ACL_TYPE_T type) +{ + SMB_ACL_T a; + + if ((a = malloc(sizeof(*a))) == NULL) { + errno = ENOMEM; + return NULL; + } + if ((a->aclp = acl_get_file(path_p, type)) == NULL) { + SAFE_FREE(a); + return NULL; + } + a->next = -1; + a->freeaclp = True; + return a; +} + +SMB_ACL_T sys_acl_get_fd(int fd) +{ + SMB_ACL_T a; + + if ((a = malloc(sizeof(*a))) == NULL) { + errno = ENOMEM; + return NULL; + } + if ((a->aclp = acl_get_fd(fd)) == NULL) { + SAFE_FREE(a); + return NULL; + } + a->next = -1; + a->freeaclp = True; + return a; +} + +int sys_acl_clear_perms(SMB_ACL_PERMSET_T permset_d) +{ + permset_d->ae_perm = 0; + + return 0; +} + +int sys_acl_add_perm(SMB_ACL_PERMSET_T permset_d, SMB_ACL_PERM_T perm) +{ + if (perm != SMB_ACL_READ && perm != SMB_ACL_WRITE + && perm != SMB_ACL_EXECUTE) { + errno = EINVAL; + return -1; + } + + if (permset_d == NULL) { + errno = EINVAL; + return -1; + } + + permset_d->ae_perm |= perm; + + return 0; +} + +int sys_acl_get_perm(SMB_ACL_PERMSET_T permset_d, SMB_ACL_PERM_T perm) +{ + return permset_d->ae_perm & perm; +} + +char *sys_acl_to_text(SMB_ACL_T acl_d, ssize_t *len_p) +{ + return acl_to_text(acl_d->aclp, len_p); +} + +SMB_ACL_T sys_acl_init(int count) +{ + SMB_ACL_T a; + + if (count < 0) { + errno = EINVAL; + return NULL; + } + + if ((a = malloc(sizeof(*a) + sizeof(struct acl))) == NULL) { + errno = ENOMEM; + return NULL; + } + + a->next = -1; + a->freeaclp = False; + a->aclp = (struct acl *)(&a->aclp + sizeof(struct acl *)); + a->aclp->acl_cnt = 0; + + return a; +} + + +int sys_acl_create_entry(SMB_ACL_T *acl_p, SMB_ACL_ENTRY_T *entry_p) +{ + SMB_ACL_T acl_d; + SMB_ACL_ENTRY_T entry_d; + + if (acl_p == NULL || entry_p == NULL || (acl_d = *acl_p) == NULL) { + errno = EINVAL; + return -1; + } + + if (acl_d->aclp->acl_cnt >= ACL_MAX_ENTRIES) { + errno = ENOSPC; + return -1; + } + + entry_d = &acl_d->aclp->acl_entry[acl_d->aclp->acl_cnt++]; + entry_d->ae_tag = 0; + entry_d->ae_id = 0; + entry_d->ae_perm = 0; + *entry_p = entry_d; + + return 0; +} + +int sys_acl_set_tag_type(SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T tag_type) +{ + switch (tag_type) { + case SMB_ACL_USER: + case SMB_ACL_USER_OBJ: + case SMB_ACL_GROUP: + case SMB_ACL_GROUP_OBJ: + case SMB_ACL_OTHER: + case SMB_ACL_MASK: + entry_d->ae_tag = tag_type; + break; + default: + errno = EINVAL; + return -1; + } + + return 0; +} + +int sys_acl_set_qualifier(SMB_ACL_ENTRY_T entry_d, void *qual_p) +{ + if (entry_d->ae_tag != SMB_ACL_GROUP + && entry_d->ae_tag != SMB_ACL_USER) { + errno = EINVAL; + return -1; + } + + entry_d->ae_id = *((id_t *)qual_p); + + return 0; +} + +int sys_acl_set_permset(SMB_ACL_ENTRY_T entry_d, SMB_ACL_PERMSET_T permset_d) +{ + if (permset_d->ae_perm & ~(SMB_ACL_READ|SMB_ACL_WRITE|SMB_ACL_EXECUTE)) { + return EINVAL; + } + + entry_d->ae_perm = permset_d->ae_perm; + + return 0; +} + +int sys_acl_valid(SMB_ACL_T acl_d) +{ + return acl_valid(acl_d->aclp); +} + +int sys_acl_set_file(const char *name, SMB_ACL_TYPE_T type, SMB_ACL_T acl_d) +{ + return acl_set_file(name, type, acl_d->aclp); +} + +int sys_acl_set_fd(int fd, SMB_ACL_T acl_d) +{ + return acl_set_fd(fd, acl_d->aclp); +} + +int sys_acl_delete_def_file(const char *name) +{ + return acl_delete_def_file(name); +} + +int sys_acl_free_text(char *text) +{ + return acl_free(text); +} + +int sys_acl_free_acl(SMB_ACL_T acl_d) +{ + if (acl_d->freeaclp) { + acl_free(acl_d->aclp); + } + acl_free(acl_d); + return 0; +} + +int sys_acl_free_qualifier(void *qual, SMB_ACL_TAG_T tagtype) +{ + return 0; +} + +#elif defined(HAVE_AIX_ACLS) + +/* Donated by Medha Date, mdate@austin.ibm.com, for IBM */ + +int sys_acl_get_entry( SMB_ACL_T theacl, int entry_id, SMB_ACL_ENTRY_T *entry_p) +{ + struct acl_entry_link *link; + struct new_acl_entry *entry; + int keep_going; + + DEBUG(10,("This is the count: %d\n",theacl->count)); + + /* Check if count was previously set to -1. * + * If it was, that means we reached the end * + * of the acl last time. */ + if(theacl->count == -1) + return(0); + + link = theacl; + /* To get to the next acl, traverse linked list until index * + * of acl matches the count we are keeping. This count is * + * incremented each time we return an acl entry. */ + + for(keep_going = 0; keep_going < theacl->count; keep_going++) + link = link->nextp; + + entry = *entry_p = link->entryp; + + DEBUG(10,("*entry_p is %d\n",entry_p)); + DEBUG(10,("*entry_p->ace_access is %d\n",entry->ace_access)); + + /* Increment count */ + theacl->count++; + if(link->nextp == NULL) + theacl->count = -1; + + return(1); +} + +int sys_acl_get_tag_type( SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T *tag_type_p) +{ + /* Initialize tag type */ + + *tag_type_p = -1; + DEBUG(10,("the tagtype is %d\n",entry_d->ace_id->id_type)); + + /* Depending on what type of entry we have, * + * return tag type. */ + switch(entry_d->ace_id->id_type) { + case ACEID_USER: + *tag_type_p = SMB_ACL_USER; + break; + case ACEID_GROUP: + *tag_type_p = SMB_ACL_GROUP; + break; + + case SMB_ACL_USER_OBJ: + case SMB_ACL_GROUP_OBJ: + case SMB_ACL_OTHER: + *tag_type_p = entry_d->ace_id->id_type; + break; + + default: + return(-1); + } + + return(0); +} + +int sys_acl_get_permset( SMB_ACL_ENTRY_T entry_d, SMB_ACL_PERMSET_T *permset_p) +{ + DEBUG(10,("Starting AIX sys_acl_get_permset\n")); + *permset_p = &entry_d->ace_access; + DEBUG(10,("**permset_p is %d\n",**permset_p)); + if(!(**permset_p & S_IXUSR) && + !(**permset_p & S_IWUSR) && + !(**permset_p & S_IRUSR) && + (**permset_p != 0)) + return(-1); + + DEBUG(10,("Ending AIX sys_acl_get_permset\n")); + return(0); +} + +void *sys_acl_get_qualifier( SMB_ACL_ENTRY_T entry_d) +{ + return(entry_d->ace_id->id_data); +} + +SMB_ACL_T sys_acl_get_file( const char *path_p, SMB_ACL_TYPE_T type) +{ + struct acl *file_acl = (struct acl *)NULL; + struct acl_entry *acl_entry; + struct new_acl_entry *new_acl_entry; + struct ace_id *idp; + struct acl_entry_link *acl_entry_link; + struct acl_entry_link *acl_entry_link_head; + int i; + int rc = 0; + uid_t user_id; + + /* Get the acl using statacl */ + + DEBUG(10,("Entering sys_acl_get_file\n")); + DEBUG(10,("path_p is %s\n",path_p)); + + file_acl = (struct acl *)malloc(BUFSIZ); + + if(file_acl == NULL) { + errno=ENOMEM; + DEBUG(0,("Error in AIX sys_acl_get_file: %d\n",errno)); + return(NULL); + } + + memset(file_acl,0,BUFSIZ); + + rc = statacl((char *)path_p,0,file_acl,BUFSIZ); + if(rc == -1) { + DEBUG(0,("statacl returned %d with errno %d\n",rc,errno)); + SAFE_FREE(file_acl); + return(NULL); + } + + DEBUG(10,("Got facl and returned it\n")); + + /* Point to the first acl entry in the acl */ + acl_entry = file_acl->acl_ext; + + /* Begin setting up the head of the linked list * + * that will be used for the storing the acl * + * in a way that is useful for the posix_acls.c * + * code. */ + + acl_entry_link_head = acl_entry_link = sys_acl_init(0); + if(acl_entry_link_head == NULL) + return(NULL); + + acl_entry_link->entryp = (struct new_acl_entry *)malloc(sizeof(struct new_acl_entry)); + if(acl_entry_link->entryp == NULL) { + SAFE_FREE(file_acl); + errno = ENOMEM; + DEBUG(0,("Error in AIX sys_acl_get_file is %d\n",errno)); + return(NULL); + } + + DEBUG(10,("acl_entry is %d\n",acl_entry)); + DEBUG(10,("acl_last(file_acl) id %d\n",acl_last(file_acl))); + + /* Check if the extended acl bit is on. * + * If it isn't, do not show the * + * contents of the acl since AIX intends * + * the extended info to remain unused */ + + if(file_acl->acl_mode & S_IXACL){ + /* while we are not pointing to the very end */ + while(acl_entry < acl_last(file_acl)) { + /* before we malloc anything, make sure this is */ + /* a valid acl entry and one that we want to map */ + idp = id_nxt(acl_entry->ace_id); + if((acl_entry->ace_type == ACC_SPECIFY || + (acl_entry->ace_type == ACC_PERMIT)) && (idp != id_last(acl_entry))) { + acl_entry = acl_nxt(acl_entry); + continue; + } + + idp = acl_entry->ace_id; + + /* Check if this is the first entry in the linked list. * + * The first entry needs to keep prevp pointing to NULL * + * and already has entryp allocated. */ + + if(acl_entry_link_head->count != 0) { + acl_entry_link->nextp = (struct acl_entry_link *) + malloc(sizeof(struct acl_entry_link)); + + if(acl_entry_link->nextp == NULL) { + SAFE_FREE(file_acl); + errno = ENOMEM; + DEBUG(0,("Error in AIX sys_acl_get_file is %d\n",errno)); + return(NULL); + } + + acl_entry_link->nextp->prevp = acl_entry_link; + acl_entry_link = acl_entry_link->nextp; + acl_entry_link->entryp = (struct new_acl_entry *)malloc(sizeof(struct new_acl_entry)); + if(acl_entry_link->entryp == NULL) { + SAFE_FREE(file_acl); + errno = ENOMEM; + DEBUG(0,("Error in AIX sys_acl_get_file is %d\n",errno)); + return(NULL); + } + acl_entry_link->nextp = NULL; + } + + acl_entry_link->entryp->ace_len = acl_entry->ace_len; + + /* Don't really need this since all types are going * + * to be specified but, it's better than leaving it 0 */ + + acl_entry_link->entryp->ace_type = acl_entry->ace_type; + + acl_entry_link->entryp->ace_access = acl_entry->ace_access; + + memcpy(acl_entry_link->entryp->ace_id,idp,sizeof(struct ace_id)); + + /* The access in the acl entries must be left shifted by * + * three bites, because they will ultimately be compared * + * to S_IRUSR, S_IWUSR, and S_IXUSR. */ + + switch(acl_entry->ace_type){ + case ACC_PERMIT: + case ACC_SPECIFY: + acl_entry_link->entryp->ace_access = acl_entry->ace_access; + acl_entry_link->entryp->ace_access <<= 6; + acl_entry_link_head->count++; + break; + case ACC_DENY: + /* Since there is no way to return a DENY acl entry * + * change to PERMIT and then shift. */ + DEBUG(10,("acl_entry->ace_access is %d\n",acl_entry->ace_access)); + acl_entry_link->entryp->ace_access = ~acl_entry->ace_access & 7; + DEBUG(10,("acl_entry_link->entryp->ace_access is %d\n",acl_entry_link->entryp->ace_access)); + acl_entry_link->entryp->ace_access <<= 6; + acl_entry_link_head->count++; + break; + default: + return(0); + } + + DEBUG(10,("acl_entry = %d\n",acl_entry)); + DEBUG(10,("The ace_type is %d\n",acl_entry->ace_type)); + + acl_entry = acl_nxt(acl_entry); + } + } /* end of if enabled */ + + /* Since owner, group, other acl entries are not * + * part of the acl entries in an acl, they must * + * be dummied up to become part of the list. */ + + for( i = 1; i < 4; i++) { + DEBUG(10,("i is %d\n",i)); + if(acl_entry_link_head->count != 0) { + acl_entry_link->nextp = (struct acl_entry_link *)malloc(sizeof(struct acl_entry_link)); + if(acl_entry_link->nextp == NULL) { + SAFE_FREE(file_acl); + errno = ENOMEM; + DEBUG(0,("Error in AIX sys_acl_get_file is %d\n",errno)); + return(NULL); + } + + acl_entry_link->nextp->prevp = acl_entry_link; + acl_entry_link = acl_entry_link->nextp; + acl_entry_link->entryp = (struct new_acl_entry *)malloc(sizeof(struct new_acl_entry)); + if(acl_entry_link->entryp == NULL) { + SAFE_FREE(file_acl); + errno = ENOMEM; + DEBUG(0,("Error in AIX sys_acl_get_file is %d\n",errno)); + return(NULL); + } + } + + acl_entry_link->nextp = NULL; + + new_acl_entry = acl_entry_link->entryp; + idp = new_acl_entry->ace_id; + + new_acl_entry->ace_len = sizeof(struct acl_entry); + new_acl_entry->ace_type = ACC_PERMIT; + idp->id_len = sizeof(struct ace_id); + DEBUG(10,("idp->id_len = %d\n",idp->id_len)); + memset(idp->id_data,0,sizeof(uid_t)); + + switch(i) { + case 2: + new_acl_entry->ace_access = file_acl->g_access << 6; + idp->id_type = SMB_ACL_GROUP_OBJ; + break; + + case 3: + new_acl_entry->ace_access = file_acl->o_access << 6; + idp->id_type = SMB_ACL_OTHER; + break; + + case 1: + new_acl_entry->ace_access = file_acl->u_access << 6; + idp->id_type = SMB_ACL_USER_OBJ; + break; + + default: + return(NULL); + + } + + acl_entry_link_head->count++; + DEBUG(10,("new_acl_entry->ace_access = %d\n",new_acl_entry->ace_access)); + } + + acl_entry_link_head->count = 0; + SAFE_FREE(file_acl); + + return(acl_entry_link_head); +} + +SMB_ACL_T sys_acl_get_fd(int fd) +{ + struct acl *file_acl = (struct acl *)NULL; + struct acl_entry *acl_entry; + struct new_acl_entry *new_acl_entry; + struct ace_id *idp; + struct acl_entry_link *acl_entry_link; + struct acl_entry_link *acl_entry_link_head; + int i; + int rc = 0; + uid_t user_id; + + /* Get the acl using fstatacl */ + + DEBUG(10,("Entering sys_acl_get_fd\n")); + DEBUG(10,("fd is %d\n",fd)); + file_acl = (struct acl *)malloc(BUFSIZ); + + if(file_acl == NULL) { + errno=ENOMEM; + DEBUG(0,("Error in sys_acl_get_fd is %d\n",errno)); + return(NULL); + } + + memset(file_acl,0,BUFSIZ); + + rc = fstatacl(fd,0,file_acl,BUFSIZ); + if(rc == -1) { + DEBUG(0,("The fstatacl call returned %d with errno %d\n",rc,errno)); + SAFE_FREE(file_acl); + return(NULL); + } + + DEBUG(10,("Got facl and returned it\n")); + + /* Point to the first acl entry in the acl */ + + acl_entry = file_acl->acl_ext; + /* Begin setting up the head of the linked list * + * that will be used for the storing the acl * + * in a way that is useful for the posix_acls.c * + * code. */ + + acl_entry_link_head = acl_entry_link = sys_acl_init(0); + if(acl_entry_link_head == NULL){ + SAFE_FREE(file_acl); + return(NULL); + } + + acl_entry_link->entryp = (struct new_acl_entry *)malloc(sizeof(struct new_acl_entry)); + + if(acl_entry_link->entryp == NULL) { + errno = ENOMEM; + DEBUG(0,("Error in sys_acl_get_fd is %d\n",errno)); + SAFE_FREE(file_acl); + return(NULL); + } + + DEBUG(10,("acl_entry is %d\n",acl_entry)); + DEBUG(10,("acl_last(file_acl) id %d\n",acl_last(file_acl))); + + /* Check if the extended acl bit is on. * + * If it isn't, do not show the * + * contents of the acl since AIX intends * + * the extended info to remain unused */ + + if(file_acl->acl_mode & S_IXACL){ + /* while we are not pointing to the very end */ + while(acl_entry < acl_last(file_acl)) { + /* before we malloc anything, make sure this is */ + /* a valid acl entry and one that we want to map */ + + idp = id_nxt(acl_entry->ace_id); + if((acl_entry->ace_type == ACC_SPECIFY || + (acl_entry->ace_type == ACC_PERMIT)) && (idp != id_last(acl_entry))) { + acl_entry = acl_nxt(acl_entry); + continue; + } + + idp = acl_entry->ace_id; + + /* Check if this is the first entry in the linked list. * + * The first entry needs to keep prevp pointing to NULL * + * and already has entryp allocated. */ + + if(acl_entry_link_head->count != 0) { + acl_entry_link->nextp = (struct acl_entry_link *)malloc(sizeof(struct acl_entry_link)); + if(acl_entry_link->nextp == NULL) { + errno = ENOMEM; + DEBUG(0,("Error in sys_acl_get_fd is %d\n",errno)); + SAFE_FREE(file_acl); + return(NULL); + } + acl_entry_link->nextp->prevp = acl_entry_link; + acl_entry_link = acl_entry_link->nextp; + acl_entry_link->entryp = (struct new_acl_entry *)malloc(sizeof(struct new_acl_entry)); + if(acl_entry_link->entryp == NULL) { + errno = ENOMEM; + DEBUG(0,("Error in sys_acl_get_fd is %d\n",errno)); + SAFE_FREE(file_acl); + return(NULL); + } + + acl_entry_link->nextp = NULL; + } + + acl_entry_link->entryp->ace_len = acl_entry->ace_len; + + /* Don't really need this since all types are going * + * to be specified but, it's better than leaving it 0 */ + + acl_entry_link->entryp->ace_type = acl_entry->ace_type; + acl_entry_link->entryp->ace_access = acl_entry->ace_access; + + memcpy(acl_entry_link->entryp->ace_id, idp, sizeof(struct ace_id)); + + /* The access in the acl entries must be left shifted by * + * three bites, because they will ultimately be compared * + * to S_IRUSR, S_IWUSR, and S_IXUSR. */ + + switch(acl_entry->ace_type){ + case ACC_PERMIT: + case ACC_SPECIFY: + acl_entry_link->entryp->ace_access = acl_entry->ace_access; + acl_entry_link->entryp->ace_access <<= 6; + acl_entry_link_head->count++; + break; + case ACC_DENY: + /* Since there is no way to return a DENY acl entry * + * change to PERMIT and then shift. */ + DEBUG(10,("acl_entry->ace_access is %d\n",acl_entry->ace_access)); + acl_entry_link->entryp->ace_access = ~acl_entry->ace_access & 7; + DEBUG(10,("acl_entry_link->entryp->ace_access is %d\n",acl_entry_link->entryp->ace_access)); + acl_entry_link->entryp->ace_access <<= 6; + acl_entry_link_head->count++; + break; + default: + return(0); + } + + DEBUG(10,("acl_entry = %d\n",acl_entry)); + DEBUG(10,("The ace_type is %d\n",acl_entry->ace_type)); + + acl_entry = acl_nxt(acl_entry); + } + } /* end of if enabled */ + + /* Since owner, group, other acl entries are not * + * part of the acl entries in an acl, they must * + * be dummied up to become part of the list. */ + + for( i = 1; i < 4; i++) { + DEBUG(10,("i is %d\n",i)); + if(acl_entry_link_head->count != 0){ + acl_entry_link->nextp = (struct acl_entry_link *)malloc(sizeof(struct acl_entry_link)); + if(acl_entry_link->nextp == NULL) { + errno = ENOMEM; + DEBUG(0,("Error in sys_acl_get_fd is %d\n",errno)); + SAFE_FREE(file_acl); + return(NULL); + } + + acl_entry_link->nextp->prevp = acl_entry_link; + acl_entry_link = acl_entry_link->nextp; + acl_entry_link->entryp = (struct new_acl_entry *)malloc(sizeof(struct new_acl_entry)); + + if(acl_entry_link->entryp == NULL) { + SAFE_FREE(file_acl); + errno = ENOMEM; + DEBUG(0,("Error in sys_acl_get_fd is %d\n",errno)); + return(NULL); + } + } + + acl_entry_link->nextp = NULL; + + new_acl_entry = acl_entry_link->entryp; + idp = new_acl_entry->ace_id; + + new_acl_entry->ace_len = sizeof(struct acl_entry); + new_acl_entry->ace_type = ACC_PERMIT; + idp->id_len = sizeof(struct ace_id); + DEBUG(10,("idp->id_len = %d\n",idp->id_len)); + memset(idp->id_data,0,sizeof(uid_t)); + + switch(i) { + case 2: + new_acl_entry->ace_access = file_acl->g_access << 6; + idp->id_type = SMB_ACL_GROUP_OBJ; + break; + + case 3: + new_acl_entry->ace_access = file_acl->o_access << 6; + idp->id_type = SMB_ACL_OTHER; + break; + + case 1: + new_acl_entry->ace_access = file_acl->u_access << 6; + idp->id_type = SMB_ACL_USER_OBJ; + break; + + default: + return(NULL); + } + + acl_entry_link_head->count++; + DEBUG(10,("new_acl_entry->ace_access = %d\n",new_acl_entry->ace_access)); + } + + acl_entry_link_head->count = 0; + SAFE_FREE(file_acl); + + return(acl_entry_link_head); +} + +int sys_acl_clear_perms(SMB_ACL_PERMSET_T permset) +{ + *permset = *permset & ~0777; + return(0); +} + +int sys_acl_add_perm( SMB_ACL_PERMSET_T permset, SMB_ACL_PERM_T perm) +{ + if((perm != 0) && + (perm & (S_IXUSR | S_IWUSR | S_IRUSR)) == 0) + return(-1); + + *permset |= perm; + DEBUG(10,("This is the permset now: %d\n",*permset)); + return(0); +} + +char *sys_acl_to_text( SMB_ACL_T theacl, ssize_t *plen) +{ + return(NULL); +} + +SMB_ACL_T sys_acl_init( int count) +{ + struct acl_entry_link *theacl = NULL; + + DEBUG(10,("Entering sys_acl_init\n")); + + theacl = (struct acl_entry_link *)malloc(sizeof(struct acl_entry_link)); + if(theacl == NULL) { + errno = ENOMEM; + DEBUG(0,("Error in sys_acl_init is %d\n",errno)); + return(NULL); + } + + theacl->count = 0; + theacl->nextp = NULL; + theacl->prevp = NULL; + theacl->entryp = NULL; + DEBUG(10,("Exiting sys_acl_init\n")); + return(theacl); +} + +int sys_acl_create_entry( SMB_ACL_T *pacl, SMB_ACL_ENTRY_T *pentry) +{ + struct acl_entry_link *theacl; + struct acl_entry_link *acl_entryp; + struct acl_entry_link *temp_entry; + int counting; + + DEBUG(10,("Entering the sys_acl_create_entry\n")); + + theacl = acl_entryp = *pacl; + + /* Get to the end of the acl before adding entry */ + + for(counting=0; counting < theacl->count; counting++){ + DEBUG(10,("The acl_entryp is %d\n",acl_entryp)); + temp_entry = acl_entryp; + acl_entryp = acl_entryp->nextp; + } + + if(theacl->count != 0){ + temp_entry->nextp = acl_entryp = (struct acl_entry_link *)malloc(sizeof(struct acl_entry_link)); + if(acl_entryp == NULL) { + errno = ENOMEM; + DEBUG(0,("Error in sys_acl_create_entry is %d\n",errno)); + return(-1); + } + + DEBUG(10,("The acl_entryp is %d\n",acl_entryp)); + acl_entryp->prevp = temp_entry; + DEBUG(10,("The acl_entryp->prevp is %d\n",acl_entryp->prevp)); + } + + *pentry = acl_entryp->entryp = (struct new_acl_entry *)malloc(sizeof(struct new_acl_entry)); + if(*pentry == NULL) { + errno = ENOMEM; + DEBUG(0,("Error in sys_acl_create_entry is %d\n",errno)); + return(-1); + } + + memset(*pentry,0,sizeof(struct new_acl_entry)); + acl_entryp->entryp->ace_len = sizeof(struct acl_entry); + acl_entryp->entryp->ace_type = ACC_PERMIT; + acl_entryp->entryp->ace_id->id_len = sizeof(struct ace_id); + acl_entryp->nextp = NULL; + theacl->count++; + DEBUG(10,("Exiting sys_acl_create_entry\n")); + return(0); +} + +int sys_acl_set_tag_type( SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T tagtype) +{ + DEBUG(10,("Starting AIX sys_acl_set_tag_type\n")); + entry->ace_id->id_type = tagtype; + DEBUG(10,("The tag type is %d\n",entry->ace_id->id_type)); + DEBUG(10,("Ending AIX sys_acl_set_tag_type\n")); +} + +int sys_acl_set_qualifier( SMB_ACL_ENTRY_T entry, void *qual) +{ + DEBUG(10,("Starting AIX sys_acl_set_qualifier\n")); + memcpy(entry->ace_id->id_data,qual,sizeof(uid_t)); + DEBUG(10,("Ending AIX sys_acl_set_qualifier\n")); + return(0); +} + +int sys_acl_set_permset( SMB_ACL_ENTRY_T entry, SMB_ACL_PERMSET_T permset) +{ + DEBUG(10,("Starting AIX sys_acl_set_permset\n")); + if(!(*permset & S_IXUSR) && + !(*permset & S_IWUSR) && + !(*permset & S_IRUSR) && + (*permset != 0)) + return(-1); + + entry->ace_access = *permset; + DEBUG(10,("entry->ace_access = %d\n",entry->ace_access)); + DEBUG(10,("Ending AIX sys_acl_set_permset\n")); + return(0); +} + +int sys_acl_valid( SMB_ACL_T theacl ) +{ + int user_obj = 0; + int group_obj = 0; + int other_obj = 0; + struct acl_entry_link *acl_entry; + + for(acl_entry=theacl; acl_entry != NULL; acl_entry = acl_entry->nextp) { + user_obj += (acl_entry->entryp->ace_id->id_type == SMB_ACL_USER_OBJ); + group_obj += (acl_entry->entryp->ace_id->id_type == SMB_ACL_GROUP_OBJ); + other_obj += (acl_entry->entryp->ace_id->id_type == SMB_ACL_OTHER); + } + + DEBUG(10,("user_obj=%d, group_obj=%d, other_obj=%d\n",user_obj,group_obj,other_obj)); + + if(user_obj != 1 || group_obj != 1 || other_obj != 1) + return(-1); + + return(0); +} + +int sys_acl_set_file( const char *name, SMB_ACL_TYPE_T acltype, SMB_ACL_T theacl) +{ + struct acl_entry_link *acl_entry_link = NULL; + struct acl *file_acl = NULL; + struct acl *file_acl_temp = NULL; + struct acl_entry *acl_entry = NULL; + struct ace_id *ace_id = NULL; + uint id_type; + uint ace_access; + uint user_id; + uint acl_length; + uint rc; + + DEBUG(10,("Entering sys_acl_set_file\n")); + DEBUG(10,("File name is %s\n",name)); + + /* AIX has no default ACL */ + if(acltype == SMB_ACL_TYPE_DEFAULT) + return(0); + + acl_length = BUFSIZ; + file_acl = (struct acl *)malloc(BUFSIZ); + + if(file_acl == NULL) { + errno = ENOMEM; + DEBUG(0,("Error in sys_acl_set_file is %d\n",errno)); + return(-1); + } + + memset(file_acl,0,BUFSIZ); + + file_acl->acl_len = ACL_SIZ; + file_acl->acl_mode = S_IXACL; + + for(acl_entry_link=theacl; acl_entry_link != NULL; acl_entry_link = acl_entry_link->nextp) { + acl_entry_link->entryp->ace_access >>= 6; + id_type = acl_entry_link->entryp->ace_id->id_type; + + switch(id_type) { + case SMB_ACL_USER_OBJ: + file_acl->u_access = acl_entry_link->entryp->ace_access; + continue; + case SMB_ACL_GROUP_OBJ: + file_acl->g_access = acl_entry_link->entryp->ace_access; + continue; + case SMB_ACL_OTHER: + file_acl->o_access = acl_entry_link->entryp->ace_access; + continue; + case SMB_ACL_MASK: + continue; + } + + if((file_acl->acl_len + sizeof(struct acl_entry)) > acl_length) { + acl_length += sizeof(struct acl_entry); + file_acl_temp = (struct acl *)malloc(acl_length); + if(file_acl_temp == NULL) { + SAFE_FREE(file_acl); + errno = ENOMEM; + DEBUG(0,("Error in sys_acl_set_file is %d\n",errno)); + return(-1); + } + + memcpy(file_acl_temp,file_acl,file_acl->acl_len); + SAFE_FREE(file_acl); + file_acl = file_acl_temp; + } + + acl_entry = (struct acl_entry *)((char *)file_acl + file_acl->acl_len); + file_acl->acl_len += sizeof(struct acl_entry); + acl_entry->ace_len = acl_entry_link->entryp->ace_len; + acl_entry->ace_access = acl_entry_link->entryp->ace_access; + + /* In order to use this, we'll need to wait until we can get denies */ + /* if(!acl_entry->ace_access && acl_entry->ace_type == ACC_PERMIT) + acl_entry->ace_type = ACC_SPECIFY; */ + + acl_entry->ace_type = ACC_SPECIFY; + + ace_id = acl_entry->ace_id; + + ace_id->id_type = acl_entry_link->entryp->ace_id->id_type; + DEBUG(10,("The id type is %d\n",ace_id->id_type)); + ace_id->id_len = acl_entry_link->entryp->ace_id->id_len; + memcpy(&user_id, acl_entry_link->entryp->ace_id->id_data, sizeof(uid_t)); + memcpy(acl_entry->ace_id->id_data, &user_id, sizeof(uid_t)); + } + + rc = chacl(name,file_acl,file_acl->acl_len); + DEBUG(10,("errno is %d\n",errno)); + DEBUG(10,("return code is %d\n",rc)); + SAFE_FREE(file_acl); + DEBUG(10,("Exiting the sys_acl_set_file\n")); + return(rc); +} + +int sys_acl_set_fd( int fd, SMB_ACL_T theacl) +{ + struct acl_entry_link *acl_entry_link = NULL; + struct acl *file_acl = NULL; + struct acl *file_acl_temp = NULL; + struct acl_entry *acl_entry = NULL; + struct ace_id *ace_id = NULL; + uint id_type; + uint user_id; + uint acl_length; + uint rc; + + DEBUG(10,("Entering sys_acl_set_fd\n")); + acl_length = BUFSIZ; + file_acl = (struct acl *)malloc(BUFSIZ); + + if(file_acl == NULL) { + errno = ENOMEM; + DEBUG(0,("Error in sys_acl_set_fd is %d\n",errno)); + return(-1); + } + + memset(file_acl,0,BUFSIZ); + + file_acl->acl_len = ACL_SIZ; + file_acl->acl_mode = S_IXACL; + + for(acl_entry_link=theacl; acl_entry_link != NULL; acl_entry_link = acl_entry_link->nextp) { + acl_entry_link->entryp->ace_access >>= 6; + id_type = acl_entry_link->entryp->ace_id->id_type; + DEBUG(10,("The id_type is %d\n",id_type)); + + switch(id_type) { + case SMB_ACL_USER_OBJ: + file_acl->u_access = acl_entry_link->entryp->ace_access; + continue; + case SMB_ACL_GROUP_OBJ: + file_acl->g_access = acl_entry_link->entryp->ace_access; + continue; + case SMB_ACL_OTHER: + file_acl->o_access = acl_entry_link->entryp->ace_access; + continue; + case SMB_ACL_MASK: + continue; + } + + if((file_acl->acl_len + sizeof(struct acl_entry)) > acl_length) { + acl_length += sizeof(struct acl_entry); + file_acl_temp = (struct acl *)malloc(acl_length); + if(file_acl_temp == NULL) { + SAFE_FREE(file_acl); + errno = ENOMEM; + DEBUG(0,("Error in sys_acl_set_fd is %d\n",errno)); + return(-1); + } + + memcpy(file_acl_temp,file_acl,file_acl->acl_len); + SAFE_FREE(file_acl); + file_acl = file_acl_temp; + } + + acl_entry = (struct acl_entry *)((char *)file_acl + file_acl->acl_len); + file_acl->acl_len += sizeof(struct acl_entry); + acl_entry->ace_len = acl_entry_link->entryp->ace_len; + acl_entry->ace_access = acl_entry_link->entryp->ace_access; + + /* In order to use this, we'll need to wait until we can get denies */ + /* if(!acl_entry->ace_access && acl_entry->ace_type == ACC_PERMIT) + acl_entry->ace_type = ACC_SPECIFY; */ + + acl_entry->ace_type = ACC_SPECIFY; + + ace_id = acl_entry->ace_id; + + ace_id->id_type = acl_entry_link->entryp->ace_id->id_type; + DEBUG(10,("The id type is %d\n",ace_id->id_type)); + ace_id->id_len = acl_entry_link->entryp->ace_id->id_len; + memcpy(&user_id, acl_entry_link->entryp->ace_id->id_data, sizeof(uid_t)); + memcpy(ace_id->id_data, &user_id, sizeof(uid_t)); + } + + rc = fchacl(fd,file_acl,file_acl->acl_len); + DEBUG(10,("errno is %d\n",errno)); + DEBUG(10,("return code is %d\n",rc)); + SAFE_FREE(file_acl); + DEBUG(10,("Exiting sys_acl_set_fd\n")); + return(rc); +} + +int sys_acl_delete_def_file(const char *name) +{ + /* AIX has no default ACL */ + return 0; +} + +int sys_acl_get_perm( SMB_ACL_PERMSET_T permset, SMB_ACL_PERM_T perm) +{ + return(*permset & perm); +} + +int sys_acl_free_text(char *text) +{ + return(0); +} + +int sys_acl_free_acl(SMB_ACL_T posix_acl) +{ + struct acl_entry_link *acl_entry_link; + + for(acl_entry_link = posix_acl->nextp; acl_entry_link->nextp != NULL; acl_entry_link = acl_entry_link->nextp) { + SAFE_FREE(acl_entry_link->prevp->entryp); + SAFE_FREE(acl_entry_link->prevp); + } + + SAFE_FREE(acl_entry_link->prevp->entryp); + SAFE_FREE(acl_entry_link->prevp); + SAFE_FREE(acl_entry_link->entryp); + SAFE_FREE(acl_entry_link); + + return(0); +} + +int sys_acl_free_qualifier(void *qual, SMB_ACL_TAG_T tagtype) +{ + return(0); +} + +#else /* No ACLs. */ + +int sys_acl_get_entry( SMB_ACL_T the_acl, int entry_id, SMB_ACL_ENTRY_T *entry_p) +{ + errno = ENOSYS; + return -1; +} + +int sys_acl_get_tag_type( SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T *tag_type_p) +{ + errno = ENOSYS; + return -1; +} + +int sys_acl_get_permset( SMB_ACL_ENTRY_T entry_d, SMB_ACL_PERMSET_T *permset_p) +{ + errno = ENOSYS; + return -1; +} + +void *sys_acl_get_qualifier( SMB_ACL_ENTRY_T entry_d) +{ + errno = ENOSYS; + return NULL; +} + +SMB_ACL_T sys_acl_get_file( const char *path_p, SMB_ACL_TYPE_T type) +{ + errno = ENOSYS; + return (SMB_ACL_T)NULL; +} + +SMB_ACL_T sys_acl_get_fd(int fd) +{ + errno = ENOSYS; + return (SMB_ACL_T)NULL; +} + +int sys_acl_clear_perms(SMB_ACL_PERMSET_T permset) +{ + errno = ENOSYS; + return -1; +} + +int sys_acl_add_perm( SMB_ACL_PERMSET_T permset, SMB_ACL_PERM_T perm) +{ + errno = ENOSYS; + return -1; +} + +int sys_acl_get_perm( SMB_ACL_PERMSET_T permset, SMB_ACL_PERM_T perm) +{ + errno = ENOSYS; + return (permset & perm) ? 1 : 0; +} + +char *sys_acl_to_text( SMB_ACL_T the_acl, ssize_t *plen) +{ + errno = ENOSYS; + return NULL; +} + +int sys_acl_free_text(char *text) +{ + errno = ENOSYS; + return -1; +} + +SMB_ACL_T sys_acl_init( int count) +{ + errno = ENOSYS; + return NULL; +} + +int sys_acl_create_entry( SMB_ACL_T *pacl, SMB_ACL_ENTRY_T *pentry) +{ + errno = ENOSYS; + return -1; +} + +int sys_acl_set_tag_type( SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T tagtype) +{ + errno = ENOSYS; + return -1; +} + +int sys_acl_set_qualifier( SMB_ACL_ENTRY_T entry, void *qual) +{ + errno = ENOSYS; + return -1; +} + +int sys_acl_set_permset( SMB_ACL_ENTRY_T entry, SMB_ACL_PERMSET_T permset) +{ + errno = ENOSYS; + return -1; +} + +int sys_acl_valid( SMB_ACL_T theacl ) +{ + errno = ENOSYS; + return -1; +} + +int sys_acl_set_file( const char *name, SMB_ACL_TYPE_T acltype, SMB_ACL_T theacl) +{ + errno = ENOSYS; + return -1; +} + +int sys_acl_set_fd( int fd, SMB_ACL_T theacl) +{ + errno = ENOSYS; + return -1; +} + +int sys_acl_delete_def_file(const char *name) +{ + errno = ENOSYS; + return -1; +} + +int sys_acl_free_acl(SMB_ACL_T the_acl) +{ + errno = ENOSYS; + return -1; +} + +int sys_acl_free_qualifier(void *qual, SMB_ACL_TAG_T tagtype) +{ + errno = ENOSYS; + return -1; +} + +#endif /* No ACLs. */ diff --git a/source4/lib/system.c b/source4/lib/system.c new file mode 100644 index 0000000000..bafe689a30 --- /dev/null +++ b/source4/lib/system.c @@ -0,0 +1,1114 @@ +/* + Unix SMB/CIFS implementation. + Samba system utilities + Copyright (C) Andrew Tridgell 1992-1998 + Copyright (C) Jeremy Allison 1998-2002 + + 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" + +/* + The idea is that this file will eventually have wrappers around all + important system calls in samba. The aims are: + + - to enable easier porting by putting OS dependent stuff in here + + - to allow for hooks into other "pseudo-filesystems" + + - to allow easier integration of things like the japanese extensions + + - to support the philosophy of Samba to expose the features of + the OS within the SMB model. In general whatever file/printer/variable + expansions/etc make sense to the OS should be acceptable to Samba. +*/ + + + +/******************************************************************* + A wrapper for usleep in case we don't have one. +********************************************************************/ + +int sys_usleep(long usecs) +{ +#ifndef HAVE_USLEEP + struct timeval tval; +#endif + + /* + * We need this braindamage as the glibc usleep + * is not SPEC1170 complient... grumble... JRA. + */ + + if(usecs < 0 || usecs > 1000000) { + errno = EINVAL; + return -1; + } + +#if HAVE_USLEEP + usleep(usecs); + return 0; +#else /* HAVE_USLEEP */ + /* + * Fake it with select... + */ + tval.tv_sec = 0; + tval.tv_usec = usecs/1000; + select(0,NULL,NULL,NULL,&tval); + return 0; +#endif /* HAVE_USLEEP */ +} + +/******************************************************************* +A read wrapper that will deal with EINTR. +********************************************************************/ + +ssize_t sys_read(int fd, void *buf, size_t count) +{ + ssize_t ret; + + do { + ret = read(fd, buf, count); + } while (ret == -1 && errno == EINTR); + return ret; +} + +/******************************************************************* +A write wrapper that will deal with EINTR. +********************************************************************/ + +ssize_t sys_write(int fd, const void *buf, size_t count) +{ + ssize_t ret; + + do { + ret = write(fd, buf, count); + } while (ret == -1 && errno == EINTR); + return ret; +} + +/******************************************************************* +A send wrapper that will deal with EINTR. +********************************************************************/ + +ssize_t sys_send(int s, const void *msg, size_t len, int flags) +{ + ssize_t ret; + + do { + ret = send(s, msg, len, flags); + } while (ret == -1 && errno == EINTR); + return ret; +} + +/******************************************************************* +A sendto wrapper that will deal with EINTR. +********************************************************************/ + +ssize_t sys_sendto(int s, const void *msg, size_t len, int flags, const struct sockaddr *to, socklen_t tolen) +{ + ssize_t ret; + + do { + ret = sendto(s, msg, len, flags, to, tolen); + } while (ret == -1 && errno == EINTR); + return ret; +} + +/******************************************************************* +A recvfrom wrapper that will deal with EINTR. +********************************************************************/ + +ssize_t sys_recvfrom(int s, void *buf, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen) +{ + ssize_t ret; + + do { + ret = recvfrom(s, buf, len, flags, from, fromlen); + } while (ret == -1 && errno == EINTR); + return ret; +} + +/******************************************************************* +A fcntl wrapper that will deal with EINTR. +********************************************************************/ + +int sys_fcntl_ptr(int fd, int cmd, void *arg) +{ + int ret; + + do { + ret = fcntl(fd, cmd, arg); + } while (ret == -1 && errno == EINTR); + return ret; +} + +/******************************************************************* +A fcntl wrapper that will deal with EINTR. +********************************************************************/ + +int sys_fcntl_long(int fd, int cmd, long arg) +{ + int ret; + + do { + ret = fcntl(fd, cmd, arg); + } while (ret == -1 && errno == EINTR); + return ret; +} + +/******************************************************************* +A stat() wrapper that will deal with 64 bit filesizes. +********************************************************************/ + +int sys_stat(const char *fname,SMB_STRUCT_STAT *sbuf) +{ + int ret; +#if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_OFF64_T) && defined(HAVE_STAT64) + ret = stat64(fname, sbuf); +#else + ret = stat(fname, sbuf); +#endif + /* we always want directories to appear zero size */ + if (ret == 0 && S_ISDIR(sbuf->st_mode)) sbuf->st_size = 0; + return ret; +} + +/******************************************************************* + An fstat() wrapper that will deal with 64 bit filesizes. +********************************************************************/ + +int sys_fstat(int fd,SMB_STRUCT_STAT *sbuf) +{ + int ret; +#if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_OFF64_T) && defined(HAVE_FSTAT64) + ret = fstat64(fd, sbuf); +#else + ret = fstat(fd, sbuf); +#endif + /* we always want directories to appear zero size */ + if (ret == 0 && S_ISDIR(sbuf->st_mode)) sbuf->st_size = 0; + return ret; +} + +/******************************************************************* + An lstat() wrapper that will deal with 64 bit filesizes. +********************************************************************/ + +int sys_lstat(const char *fname,SMB_STRUCT_STAT *sbuf) +{ + int ret; +#if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_OFF64_T) && defined(HAVE_LSTAT64) + ret = lstat64(fname, sbuf); +#else + ret = lstat(fname, sbuf); +#endif + /* we always want directories to appear zero size */ + if (ret == 0 && S_ISDIR(sbuf->st_mode)) sbuf->st_size = 0; + return ret; +} + +/******************************************************************* + An ftruncate() wrapper that will deal with 64 bit filesizes. +********************************************************************/ + +int sys_ftruncate(int fd, SMB_OFF_T offset) +{ +#if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_OFF64_T) && defined(HAVE_FTRUNCATE64) + return ftruncate64(fd, offset); +#else + return ftruncate(fd, offset); +#endif +} + +/******************************************************************* + An lseek() wrapper that will deal with 64 bit filesizes. +********************************************************************/ + +SMB_OFF_T sys_lseek(int fd, SMB_OFF_T offset, int whence) +{ +#if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_OFF64_T) && defined(HAVE_LSEEK64) + return lseek64(fd, offset, whence); +#else + return lseek(fd, offset, whence); +#endif +} + +/******************************************************************* + An fseek() wrapper that will deal with 64 bit filesizes. +********************************************************************/ + +int sys_fseek(FILE *fp, SMB_OFF_T offset, int whence) +{ +#if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(LARGE_SMB_OFF_T) && defined(HAVE_FSEEK64) + return fseek64(fp, offset, whence); +#elif defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(LARGE_SMB_OFF_T) && defined(HAVE_FSEEKO64) + return fseeko64(fp, offset, whence); +#else + return fseek(fp, offset, whence); +#endif +} + +/******************************************************************* + An ftell() wrapper that will deal with 64 bit filesizes. +********************************************************************/ + +SMB_OFF_T sys_ftell(FILE *fp) +{ +#if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(LARGE_SMB_OFF_T) && defined(HAVE_FTELL64) + return (SMB_OFF_T)ftell64(fp); +#elif defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(LARGE_SMB_OFF_T) && defined(HAVE_FTELLO64) + return (SMB_OFF_T)ftello64(fp); +#else + return (SMB_OFF_T)ftell(fp); +#endif +} + +/******************************************************************* + A creat() wrapper that will deal with 64 bit filesizes. +********************************************************************/ + +int sys_creat(const char *path, mode_t mode) +{ +#if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_CREAT64) + return creat64(path, mode); +#else + /* + * If creat64 isn't defined then ensure we call a potential open64. + * JRA. + */ + return sys_open(path, O_WRONLY | O_CREAT | O_TRUNC, mode); +#endif +} + +/******************************************************************* + An open() wrapper that will deal with 64 bit filesizes. +********************************************************************/ + +int sys_open(const char *path, int oflag, mode_t mode) +{ +#if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_OPEN64) + return open64(path, oflag, mode); +#else + return open(path, oflag, mode); +#endif +} + +/******************************************************************* + An fopen() wrapper that will deal with 64 bit filesizes. +********************************************************************/ + +FILE *sys_fopen(const char *path, const char *type) +{ +#if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_FOPEN64) + return fopen64(path, type); +#else + return fopen(path, type); +#endif +} + +/******************************************************************* + A readdir wrapper that will deal with 64 bit filesizes. +********************************************************************/ + +SMB_STRUCT_DIRENT *sys_readdir(DIR *dirp) +{ +#if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_READDIR64) + return readdir64(dirp); +#else + return readdir(dirp); +#endif +} + +/******************************************************************* +The wait() calls vary between systems +********************************************************************/ + +int sys_waitpid(pid_t pid,int *status,int options) +{ +#ifdef HAVE_WAITPID + return waitpid(pid,status,options); +#else /* HAVE_WAITPID */ + return wait4(pid, status, options, NULL); +#endif /* HAVE_WAITPID */ +} + +/******************************************************************* + System wrapper for getwd +********************************************************************/ + +char *sys_getwd(char *s) +{ + char *wd; +#ifdef HAVE_GETCWD + wd = (char *)getcwd(s, sizeof (pstring)); +#else + wd = (char *)getwd(s); +#endif + return wd; +} + +/******************************************************************* +system wrapper for link +********************************************************************/ + +int sys_link(const char *oldpath, const char *newpath) +{ +#ifndef HAVE_LINK + errno = ENOSYS; + return -1; +#else + return link(oldpath, newpath); +#endif +} + +/******************************************************************* +os/2 also doesn't have chroot +********************************************************************/ +int sys_chroot(const char *dname) +{ +#ifndef HAVE_CHROOT + static int done; + if (!done) { + DEBUG(1,("WARNING: no chroot!\n")); + done=1; + } + errno = ENOSYS; + return -1; +#else + return(chroot(dname)); +#endif +} + +/************************************************************************** +A wrapper for gethostbyname() that tries avoids looking up hostnames +in the root domain, which can cause dial-on-demand links to come up for no +apparent reason. +****************************************************************************/ + +struct hostent *sys_gethostbyname(const char *name) +{ +#ifdef REDUCE_ROOT_DNS_LOOKUPS + char query[256], hostname[256]; + char *domain; + + /* Does this name have any dots in it? If so, make no change */ + + if (strchr_m(name, '.')) + return(gethostbyname(name)); + + /* Get my hostname, which should have domain name + attached. If not, just do the gethostname on the + original string. + */ + + gethostname(hostname, sizeof(hostname) - 1); + hostname[sizeof(hostname) - 1] = 0; + if ((domain = strchr_m(hostname, '.')) == NULL) + return(gethostbyname(name)); + + /* Attach domain name to query and do modified query. + If names too large, just do gethostname on the + original string. + */ + + if((strlen(name) + strlen(domain)) >= sizeof(query)) + return(gethostbyname(name)); + + slprintf(query, sizeof(query)-1, "%s%s", name, domain); + return(gethostbyname(query)); +#else /* REDUCE_ROOT_DNS_LOOKUPS */ + return(gethostbyname(name)); +#endif /* REDUCE_ROOT_DNS_LOOKUPS */ +} + + +#if defined(HAVE_IRIX_SPECIFIC_CAPABILITIES) +/************************************************************************** + Try and abstract process capabilities (for systems that have them). +****************************************************************************/ +static BOOL set_process_capability( uint32 cap_flag, BOOL enable ) +{ + if(cap_flag == KERNEL_OPLOCK_CAPABILITY) { + cap_t cap = cap_get_proc(); + + if (cap == NULL) { + DEBUG(0,("set_process_capability: cap_get_proc failed. Error was %s\n", + strerror(errno))); + return False; + } + + if(enable) + cap->cap_effective |= CAP_NETWORK_MGT; + else + cap->cap_effective &= ~CAP_NETWORK_MGT; + + if (cap_set_proc(cap) == -1) { + DEBUG(0,("set_process_capability: cap_set_proc failed. Error was %s\n", + strerror(errno))); + cap_free(cap); + return False; + } + + cap_free(cap); + + DEBUG(10,("set_process_capability: Set KERNEL_OPLOCK_CAPABILITY.\n")); + } + return True; +} + +/************************************************************************** + Try and abstract inherited process capabilities (for systems that have them). +****************************************************************************/ + +static BOOL set_inherited_process_capability( uint32 cap_flag, BOOL enable ) +{ + if(cap_flag == KERNEL_OPLOCK_CAPABILITY) { + cap_t cap = cap_get_proc(); + + if (cap == NULL) { + DEBUG(0,("set_inherited_process_capability: cap_get_proc failed. Error was %s\n", + strerror(errno))); + return False; + } + + if(enable) + cap->cap_inheritable |= CAP_NETWORK_MGT; + else + cap->cap_inheritable &= ~CAP_NETWORK_MGT; + + if (cap_set_proc(cap) == -1) { + DEBUG(0,("set_inherited_process_capability: cap_set_proc failed. Error was %s\n", + strerror(errno))); + cap_free(cap); + return False; + } + + cap_free(cap); + + DEBUG(10,("set_inherited_process_capability: Set KERNEL_OPLOCK_CAPABILITY.\n")); + } + return True; +} +#endif + +/**************************************************************************** + Gain the oplock capability from the kernel if possible. +****************************************************************************/ + +void oplock_set_capability(BOOL this_process, BOOL inherit) +{ +#if HAVE_KERNEL_OPLOCKS_IRIX + set_process_capability(KERNEL_OPLOCK_CAPABILITY,this_process); + set_inherited_process_capability(KERNEL_OPLOCK_CAPABILITY,inherit); +#endif +} + +/************************************************************************** + Wrapper for random(). +****************************************************************************/ + +long sys_random(void) +{ +#if defined(HAVE_RANDOM) + return (long)random(); +#elif defined(HAVE_RAND) + return (long)rand(); +#else + DEBUG(0,("Error - no random function available !\n")); + exit(1); +#endif +} + +/************************************************************************** + Wrapper for srandom(). +****************************************************************************/ + +void sys_srandom(unsigned int seed) +{ +#if defined(HAVE_SRANDOM) + srandom(seed); +#elif defined(HAVE_SRAND) + srand(seed); +#else + DEBUG(0,("Error - no srandom function available !\n")); + exit(1); +#endif +} + +/************************************************************************** + Returns equivalent to NGROUPS_MAX - using sysconf if needed. +****************************************************************************/ + +int groups_max(void) +{ +#if defined(SYSCONF_SC_NGROUPS_MAX) + int ret = sysconf(_SC_NGROUPS_MAX); + return (ret == -1) ? NGROUPS_MAX : ret; +#else + return NGROUPS_MAX; +#endif +} + +/************************************************************************** + Wrapper for getgroups. Deals with broken (int) case. +****************************************************************************/ + +int sys_getgroups(int setlen, gid_t *gidset) +{ +#if !defined(HAVE_BROKEN_GETGROUPS) + return getgroups(setlen, gidset); +#else + + GID_T gid; + GID_T *group_list; + int i, ngroups; + + if(setlen == 0) { + return getgroups(setlen, &gid); + } + + /* + * Broken case. We need to allocate a + * GID_T array of size setlen. + */ + + if(setlen < 0) { + errno = EINVAL; + return -1; + } + + if (setlen == 0) + setlen = groups_max(); + + if((group_list = (GID_T *)malloc(setlen * sizeof(GID_T))) == NULL) { + DEBUG(0,("sys_getgroups: Malloc fail.\n")); + return -1; + } + + if((ngroups = getgroups(setlen, group_list)) < 0) { + int saved_errno = errno; + SAFE_FREE(group_list); + errno = saved_errno; + return -1; + } + + for(i = 0; i < ngroups; i++) + gidset[i] = (gid_t)group_list[i]; + + SAFE_FREE(group_list); + return ngroups; +#endif /* HAVE_BROKEN_GETGROUPS */ +} + +#ifdef HAVE_SETGROUPS + +/************************************************************************** + Wrapper for setgroups. Deals with broken (int) case. Automatically used + if we have broken getgroups. +****************************************************************************/ + +int sys_setgroups(int setlen, gid_t *gidset) +{ +#if !defined(HAVE_BROKEN_GETGROUPS) + return setgroups(setlen, gidset); +#else + + GID_T *group_list; + int i ; + + if (setlen == 0) + return 0 ; + + if (setlen < 0 || setlen > groups_max()) { + errno = EINVAL; + return -1; + } + + /* + * Broken case. We need to allocate a + * GID_T array of size setlen. + */ + + if((group_list = (GID_T *)malloc(setlen * sizeof(GID_T))) == NULL) { + DEBUG(0,("sys_setgroups: Malloc fail.\n")); + return -1; + } + + for(i = 0; i < setlen; i++) + group_list[i] = (GID_T) gidset[i]; + + if(setgroups(setlen, group_list) != 0) { + int saved_errno = errno; + SAFE_FREE(group_list); + errno = saved_errno; + return -1; + } + + SAFE_FREE(group_list); + return 0 ; +#endif /* HAVE_BROKEN_GETGROUPS */ +} + +#endif /* HAVE_SETGROUPS */ + +struct passwd *sys_getpwent(void) +{ + return getpwent(); +} + +void sys_endpwent(void) +{ + endpwent(); +} + +/************************************************************************** + Wrappers for getpwnam(), getpwuid(), getgrnam(), getgrgid() +****************************************************************************/ + +struct passwd *sys_getpwnam(const char *name) +{ + return getpwnam(name); +} + +struct passwd *sys_getpwuid(uid_t uid) +{ + return getpwuid(uid); +} + +struct group *sys_getgrnam(const char *name) +{ + return getgrnam(name); +} + +struct group *sys_getgrgid(gid_t gid) +{ + return getgrgid(gid); +} + +#if 0 /* NOT CURRENTLY USED - JRA */ +/************************************************************************** + The following are the UNICODE versions of *all* system interface functions + called within Samba. Ok, ok, the exceptions are the gethostbyXX calls, + which currently are left as ascii as they are not used other than in name + resolution. +****************************************************************************/ + +/************************************************************************** + Wide stat. Just narrow and call sys_xxx. +****************************************************************************/ + +int wsys_stat(const smb_ucs2_t *wfname,SMB_STRUCT_STAT *sbuf) +{ + pstring fname; + return sys_stat(unicode_to_unix(fname,wfname,sizeof(fname)), sbuf); +} + +/************************************************************************** + Wide lstat. Just narrow and call sys_xxx. +****************************************************************************/ + +int wsys_lstat(const smb_ucs2_t *wfname,SMB_STRUCT_STAT *sbuf) +{ + pstring fname; + return sys_lstat(unicode_to_unix(fname,wfname,sizeof(fname)), sbuf); +} + +/************************************************************************** + Wide creat. Just narrow and call sys_xxx. +****************************************************************************/ + +int wsys_creat(const smb_ucs2_t *wfname, mode_t mode) +{ + pstring fname; + return sys_creat(unicode_to_unix(fname,wfname,sizeof(fname)), mode); +} + +/************************************************************************** + Wide open. Just narrow and call sys_xxx. +****************************************************************************/ + +int wsys_open(const smb_ucs2_t *wfname, int oflag, mode_t mode) +{ + pstring fname; + return sys_open(unicode_to_unix(fname,wfname,sizeof(fname)), oflag, mode); +} + +/************************************************************************** + Wide fopen. Just narrow and call sys_xxx. +****************************************************************************/ + +FILE *wsys_fopen(const smb_ucs2_t *wfname, const char *type) +{ + pstring fname; + return sys_fopen(unicode_to_unix(fname,wfname,sizeof(fname)), type); +} + +/************************************************************************** + Wide opendir. Just narrow and call sys_xxx. +****************************************************************************/ + +DIR *wsys_opendir(const smb_ucs2_t *wfname) +{ + pstring fname; + return opendir(unicode_to_unix(fname,wfname,sizeof(fname))); +} + +/************************************************************************** + Wide readdir. Return a structure pointer containing a wide filename. +****************************************************************************/ + +SMB_STRUCT_WDIRENT *wsys_readdir(DIR *dirp) +{ + static SMB_STRUCT_WDIRENT retval; + SMB_STRUCT_DIRENT *dirval = sys_readdir(dirp); + + if(!dirval) + return NULL; + + /* + * The only POSIX defined member of this struct is d_name. + */ + + unix_to_unicode(retval.d_name,dirval->d_name,sizeof(retval.d_name)); + + return &retval; +} + +/************************************************************************** + Wide getwd. Call sys_xxx and widen. Assumes s points to a wpstring. +****************************************************************************/ + +smb_ucs2_t *wsys_getwd(smb_ucs2_t *s) +{ + pstring fname; + char *p = sys_getwd(fname); + + if(!p) + return NULL; + + return unix_to_unicode(s, p, sizeof(wpstring)); +} + +/************************************************************************** + Wide chown. Just narrow and call sys_xxx. +****************************************************************************/ + +int wsys_chown(const smb_ucs2_t *wfname, uid_t uid, gid_t gid) +{ + pstring fname; + return chown(unicode_to_unix(fname,wfname,sizeof(fname)), uid, gid); +} + +/************************************************************************** + Wide chroot. Just narrow and call sys_xxx. +****************************************************************************/ + +int wsys_chroot(const smb_ucs2_t *wfname) +{ + pstring fname; + return chroot(unicode_to_unix(fname,wfname,sizeof(fname))); +} + +/************************************************************************** + Wide getpwnam. Return a structure pointer containing wide names. +****************************************************************************/ + +SMB_STRUCT_WPASSWD *wsys_getpwnam(const smb_ucs2_t *wname) +{ + static SMB_STRUCT_WPASSWD retval; + fstring name; + struct passwd *pwret = sys_getpwnam(unicode_to_unix(name,wname,sizeof(name))); + + if(!pwret) + return NULL; + + unix_to_unicode(retval.pw_name, pwret->pw_name, sizeof(retval.pw_name)); + retval.pw_passwd = pwret->pw_passwd; + retval.pw_uid = pwret->pw_uid; + retval.pw_gid = pwret->pw_gid; + unix_to_unicode(retval.pw_gecos, pwret->pw_gecos, sizeof(retval.pw_gecos)); + unix_to_unicode(retval.pw_dir, pwret->pw_dir, sizeof(retval.pw_dir)); + unix_to_unicode(retval.pw_shell, pwret->pw_shell, sizeof(retval.pw_shell)); + + return &retval; +} + +/************************************************************************** + Wide getpwuid. Return a structure pointer containing wide names. +****************************************************************************/ + +SMB_STRUCT_WPASSWD *wsys_getpwuid(uid_t uid) +{ + static SMB_STRUCT_WPASSWD retval; + struct passwd *pwret = sys_getpwuid(uid); + + if(!pwret) + return NULL; + + unix_to_unicode(retval.pw_name, pwret->pw_name, sizeof(retval.pw_name)); + retval.pw_passwd = pwret->pw_passwd; + retval.pw_uid = pwret->pw_uid; + retval.pw_gid = pwret->pw_gid; + unix_to_unicode(retval.pw_gecos, pwret->pw_gecos, sizeof(retval.pw_gecos)); + unix_to_unicode(retval.pw_dir, pwret->pw_dir, sizeof(retval.pw_dir)); + unix_to_unicode(retval.pw_shell, pwret->pw_shell, sizeof(retval.pw_shell)); + + return &retval; +} +#endif /* NOT CURRENTLY USED - JRA */ + +/************************************************************************** + Extract a command into an arg list. Uses a static pstring for storage. + Caller frees returned arg list (which contains pointers into the static pstring). +****************************************************************************/ + +static char **extract_args(const char *command) +{ + static pstring trunc_cmd; + char *ptr; + int argcl; + char **argl = NULL; + int i; + + pstrcpy(trunc_cmd, command); + + if(!(ptr = strtok(trunc_cmd, " \t"))) { + errno = EINVAL; + return NULL; + } + + /* + * Count the args. + */ + + for( argcl = 1; ptr; ptr = strtok(NULL, " \t")) + argcl++; + + if((argl = (char **)malloc((argcl + 1) * sizeof(char *))) == NULL) + return NULL; + + /* + * Now do the extraction. + */ + + pstrcpy(trunc_cmd, command); + + ptr = strtok(trunc_cmd, " \t"); + i = 0; + argl[i++] = ptr; + + while((ptr = strtok(NULL, " \t")) != NULL) + argl[i++] = ptr; + + argl[i++] = NULL; + return argl; +} + +/************************************************************************** + Wrapper for popen. Safer as it doesn't search a path. + Modified from the glibc sources. + modified by tridge to return a file descriptor. We must kick our FILE* habit +****************************************************************************/ + +typedef struct _popen_list +{ + int fd; + pid_t child_pid; + struct _popen_list *next; +} popen_list; + +static popen_list *popen_chain; + +int sys_popen(const char *command) +{ + int parent_end, child_end; + int pipe_fds[2]; + popen_list *entry = NULL; + char **argl = NULL; + + if (pipe(pipe_fds) < 0) + return -1; + + parent_end = pipe_fds[0]; + child_end = pipe_fds[1]; + + if (!*command) { + errno = EINVAL; + goto err_exit; + } + + if((entry = (popen_list *)malloc(sizeof(popen_list))) == NULL) + goto err_exit; + + ZERO_STRUCTP(entry); + + /* + * Extract the command and args into a NULL terminated array. + */ + + if(!(argl = extract_args(command))) + goto err_exit; + + entry->child_pid = fork(); + + if (entry->child_pid == -1) { + goto err_exit; + } + + if (entry->child_pid == 0) { + + /* + * Child ! + */ + + int child_std_end = STDOUT_FILENO; + popen_list *p; + + close(parent_end); + if (child_end != child_std_end) { + dup2 (child_end, child_std_end); + close (child_end); + } + + /* + * POSIX.2: "popen() shall ensure that any streams from previous + * popen() calls that remain open in the parent process are closed + * in the new child process." + */ + + for (p = popen_chain; p; p = p->next) + close(p->fd); + + execv(argl[0], argl); + _exit (127); + } + + /* + * Parent. + */ + + close (child_end); + SAFE_FREE(argl); + + /* Link into popen_chain. */ + entry->next = popen_chain; + popen_chain = entry; + entry->fd = parent_end; + + return entry->fd; + +err_exit: + + SAFE_FREE(entry); + SAFE_FREE(argl); + close(pipe_fds[0]); + close(pipe_fds[1]); + return -1; +} + +/************************************************************************** + Wrapper for pclose. Modified from the glibc sources. +****************************************************************************/ + +int sys_pclose(int fd) +{ + int wstatus; + popen_list **ptr = &popen_chain; + popen_list *entry = NULL; + pid_t wait_pid; + int status = -1; + + /* Unlink from popen_chain. */ + for ( ; *ptr != NULL; ptr = &(*ptr)->next) { + if ((*ptr)->fd == fd) { + entry = *ptr; + *ptr = (*ptr)->next; + status = 0; + break; + } + } + + if (status < 0 || close(entry->fd) < 0) + return -1; + + /* + * As Samba is catching and eating child process + * exits we don't really care about the child exit + * code, a -1 with errno = ECHILD will do fine for us. + */ + + do { + wait_pid = sys_waitpid (entry->child_pid, &wstatus, 0); + } while (wait_pid == -1 && errno == EINTR); + + SAFE_FREE(entry); + + if (wait_pid == -1) + return -1; + return wstatus; +} + +/************************************************************************** + Wrappers for dlopen, dlsym, dlclose. +****************************************************************************/ + +void *sys_dlopen(const char *name, int flags) +{ +#if defined(HAVE_DLOPEN) + return dlopen(name, flags); +#else + return NULL; +#endif +} + +void *sys_dlsym(void *handle, const char *symbol) +{ +#if defined(HAVE_DLSYM) + return dlsym(handle, symbol); +#else + return NULL; +#endif +} + +int sys_dlclose (void *handle) +{ +#if defined(HAVE_DLCLOSE) + return dlclose(handle); +#else + return 0; +#endif +} + +const char *sys_dlerror(void) +{ +#if defined(HAVE_DLERROR) + return dlerror(); +#else + return NULL; +#endif +} + +int sys_dup2(int oldfd, int newfd) +{ +#if defined(HAVE_DUP2) + return dup2(oldfd, newfd); +#else + errno = ENOSYS; + return -1; +#endif +} + diff --git a/source4/lib/system_smbd.c b/source4/lib/system_smbd.c new file mode 100644 index 0000000000..3ae0a6395e --- /dev/null +++ b/source4/lib/system_smbd.c @@ -0,0 +1,119 @@ +/* + Unix SMB/CIFS implementation. + system call wrapper interface. + Copyright (C) Andrew Tridgell 2002 + Copyright (C) Andrew Barteltt 2002 + + 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. +*/ + +/* + This file may assume linkage with smbd - for things like become_root() + etc. +*/ + +#include "includes.h" + +#ifndef HAVE_GETGROUPLIST +/* + This is a *much* faster way of getting the list of groups for a user + without changing the current supplemenrary group list. The old + method used getgrent() which could take 20 minutes on a really big + network with hundeds of thousands of groups and users. The new method + takes a couple of seconds. + + NOTE!! this function only works if it is called as root! + */ +static int getgrouplist_internals(const char *user, gid_t gid, gid_t *groups, int *grpcnt) +{ + gid_t *gids_saved; + int ret, ngrp_saved, num_gids; + + if (non_root_mode()) { + *grpcnt = 0; + return 0; + } + + /* work out how many groups we need to save */ + ngrp_saved = getgroups(0, NULL); + if (ngrp_saved == -1) { + /* this shouldn't happen */ + return -1; + } + + gids_saved = (gid_t *)malloc(sizeof(gid_t) * (ngrp_saved+1)); + if (!gids_saved) { + errno = ENOMEM; + return -1; + } + + ngrp_saved = getgroups(ngrp_saved, gids_saved); + if (ngrp_saved == -1) { + SAFE_FREE(gids_saved); + /* very strange! */ + return -1; + } + + if (initgroups(user, gid) != 0) { + DEBUG(0, ("getgrouplist_internals: initgroups() failed!\n")); + SAFE_FREE(gids_saved); + return -1; + } + + /* this must be done to cope with systems that put the current egid in the + return from getgroups() */ + save_re_gid(); + set_effective_gid(gid); + setgid(gid); + + num_gids = getgroups(0, NULL); + if (num_gids + 1 > *grpcnt) { + *grpcnt = num_gids + 1; + ret = -1; + } else { + ret = getgroups(*grpcnt - 1, &groups[1]); + if (ret >= 0) { + groups[0] = gid; + *grpcnt = ret + 1; + } + } + + restore_re_gid(); + + if (setgroups(ngrp_saved, gids_saved) != 0) { + /* yikes! */ + DEBUG(0,("ERROR: getgrouplist: failed to reset group list!\n")); + smb_panic("getgrouplist: failed to reset group list!\n"); + free(gids_saved); + return -1; + } + + free(gids_saved); + return ret; +} +#endif + +int sys_getgrouplist(const char *user, gid_t gid, gid_t *groups, int *grpcnt) +{ +#ifdef HAVE_GETGROUPLIST + return getgrouplist(user, gid, groups, grpcnt); +#else + int retval; + become_root(); + retval = getgrouplist_internals(user, gid, groups, grpcnt); + unbecome_root(); + return retval; +#endif +} diff --git a/source4/lib/talloc.c b/source4/lib/talloc.c new file mode 100644 index 0000000000..83a99d8071 --- /dev/null +++ b/source4/lib/talloc.c @@ -0,0 +1,515 @@ +/* + Samba Unix SMB/CIFS implementation. + Samba temporary memory allocation functions + Copyright (C) Andrew Tridgell 2000 + Copyright (C) 2001, 2002 by Martin Pool <mbp@samba.org> + + 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. +*/ + +/** + @defgroup talloc Simple memory allocator + @{ + + This is a very simple temporary memory allocator. To use it do the following: + + 1) when you first want to allocate a pool of meomry use + talloc_init() and save the resulting context pointer somewhere + + 2) to allocate memory use talloc() + + 3) when _all_ of the memory allocated using this context is no longer needed + use talloc_destroy() + + talloc does not zero the memory. It guarantees memory of a + TALLOC_ALIGN alignment + + @sa talloc.h +*/ + +/** + * @todo We could allocate both the talloc_chunk structure, and the + * memory it contains all in one allocation, which might be a bit + * faster and perhaps use less memory overhead. + * + * That smells like a premature optimization, though. -- mbp + **/ + +/** + * If you want testing for memory corruption, link with dmalloc or use + * Insure++. It doesn't seem useful to duplicate them here. + **/ + +#include "includes.h" + +struct talloc_chunk { + struct talloc_chunk *next; + size_t size; + void *ptr; +}; + + +struct talloc_ctx { + struct talloc_chunk *list; + off_t total_alloc_size; + + /** The name recorded for this pool, if any. Should describe + * the purpose for which it was allocated. The string is + * allocated within the pool. **/ + char *name; + + /** Pointer to the next allocate talloc pool, so that we can + * summarize all talloc memory usage. **/ + struct talloc_ctx *next, *prev; +}; + + +/** + * Start of linked list of all talloc pools. + * + * @todo We should turn the global list off when using Insure++, + * otherwise all the memory will be seen as still reachable. + **/ +static TALLOC_CTX *list_head; + +/** + * Add to the global list + **/ +static void talloc_enroll(TALLOC_CTX *t) +{ +#if 0 + /* disabled enrole/disenrole until we have __thread support */ + MUTEX_LOCK_BY_ID(MUTEX_TALLOC); + DLIST_ADD(list_head, t); + MUTEX_UNLOCK_BY_ID(MUTEX_TALLOC); +#endif +} + + +static void talloc_disenroll(TALLOC_CTX *t) +{ +#if 0 + /* disabled enrole/disenrole until we have __thread support */ + MUTEX_LOCK_BY_ID(MUTEX_TALLOC); + DLIST_REMOVE(list_head, t); + MUTEX_UNLOCK_BY_ID(MUTEX_TALLOC); +#endif +} + + +/** Create a new talloc context. **/ +static TALLOC_CTX *talloc_init_internal(void) +{ + TALLOC_CTX *t; + + t = (TALLOC_CTX *)malloc(sizeof(TALLOC_CTX)); + if (t) { + t->list = NULL; + t->total_alloc_size = 0; + t->name = NULL; + talloc_enroll(t); + } + + return t; +} + + + +/** + * Create a new talloc context, with a name specifying its purpose. + **/ + + TALLOC_CTX *talloc_init(char const *fmt, ...) +{ + TALLOC_CTX *t; + va_list ap; + + t = talloc_init_internal(); + if (t && fmt) { + /* + * t->name must not be talloced. + * as destroying the pool would destroy it. JRA. + */ + t->name = NULL; + va_start(ap, fmt); + vasprintf(&t->name, fmt, ap); + va_end(ap); + if (!t->name) { + talloc_destroy(t); + t = NULL; + } + } + + return t; +} + + +/** Allocate a bit of memory from the specified pool **/ +void *talloc(TALLOC_CTX *t, size_t size) +{ + void *p; + struct talloc_chunk *tc; + + if (!t || size == 0) return NULL; + + p = malloc(size); + if (p) { + tc = malloc(sizeof(*tc)); + if (tc) { + tc->ptr = p; + tc->size = size; + tc->next = t->list; + t->list = tc; + t->total_alloc_size += size; + } + else { + SAFE_FREE(p); + } + } + return p; +} + +/** A talloc version of realloc */ +void *talloc_realloc(TALLOC_CTX *t, void *ptr, size_t size) +{ + struct talloc_chunk *tc; + void *new_ptr; + + /* size zero is equivalent to free() */ + if (!t || size == 0) + return NULL; + + /* realloc(NULL) is equavalent to malloc() */ + if (ptr == NULL) + return talloc(t, size); + + for (tc=t->list; tc; tc=tc->next) { + if (tc->ptr == ptr) { + new_ptr = Realloc(ptr, size); + if (new_ptr) { + t->total_alloc_size += (size - tc->size); + tc->size = size; + tc->ptr = new_ptr; + } + return new_ptr; + } + } + return NULL; +} + +/** Destroy all the memory allocated inside @p t, but not @p t + * itself. */ +void talloc_destroy_pool(TALLOC_CTX *t) +{ + struct talloc_chunk *c; + + if (!t) + return; + + while (t->list) { + c = t->list->next; + SAFE_FREE(t->list->ptr); + SAFE_FREE(t->list); + t->list = c; + } + + t->total_alloc_size = 0; +} + +/** Destroy a whole pool including the context */ +void talloc_destroy(TALLOC_CTX *t) +{ + if (!t) + return; + + talloc_destroy_pool(t); + talloc_disenroll(t); + SAFE_FREE(t->name); + SAFE_FREE(t); +} + +/** Return the current total size of the pool. */ +size_t talloc_pool_size(TALLOC_CTX *t) +{ + return t->total_alloc_size; +} + +const char *talloc_pool_name(TALLOC_CTX const *t) +{ + if (t) return t->name; + + return NULL; +} + + +/** talloc and zero memory. */ +void *talloc_zero(TALLOC_CTX *t, size_t size) +{ + void *p = talloc(t, size); + + if (p) { + memset(p, '\0', size); + } + + return p; +} + + +/** memdup with a talloc. */ +void *talloc_memdup(TALLOC_CTX *t, const void *p, size_t size) +{ + void *newp = talloc(t,size); + + if (newp) { + memcpy(newp, p, size); + } + + return newp; +} + +/** strdup with a talloc */ +char *talloc_strdup(TALLOC_CTX *t, const char *p) +{ + return talloc_memdup(t, p, strlen(p) + 1); +} + +/** strndup with a talloc */ +char *talloc_strndup(TALLOC_CTX *t, const char *p, size_t n) +{ + size_t len = strnlen(p, n); + char *ret; + + ret = talloc(t, len + 1); + if (!ret) { return NULL; } + memcpy(ret, p, len); + ret[len] = 0; + return ret; +} + +/** strdup_w with a talloc */ +smb_ucs2_t *talloc_strdup_w(TALLOC_CTX *t, const smb_ucs2_t *p) +{ + if (p) + return talloc_memdup(t, p, (strlen_w(p) + 1) * sizeof(smb_ucs2_t)); + return NULL; +} + +/** + * Perform string formatting, and return a pointer to newly allocated + * memory holding the result, inside a memory pool. + **/ + char *talloc_asprintf(TALLOC_CTX *t, const char *fmt, ...) +{ + va_list ap; + char *ret; + + va_start(ap, fmt); + ret = talloc_vasprintf(t, fmt, ap); + va_end(ap); + return ret; +} + + + char *talloc_vasprintf(TALLOC_CTX *t, const char *fmt, va_list ap) +{ + int len; + char *ret; + va_list ap2; + + VA_COPY(ap2, ap); + + len = vsnprintf(NULL, 0, fmt, ap2); + + ret = talloc(t, len+1); + if (ret) { + VA_COPY(ap2, ap); + vsnprintf(ret, len+1, fmt, ap2); + } + + return ret; +} + + +/** + * Realloc @p s to append the formatted result of @p fmt and return @p + * s, which may have moved. Good for gradually accumulating output + * into a string buffer. + **/ + char *talloc_asprintf_append(TALLOC_CTX *t, char *s, + const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + s = talloc_vasprintf_append(t, s, fmt, ap); + va_end(ap); + return s; +} + + + +/** + * Realloc @p s to append the formatted result of @p fmt and @p ap, + * and return @p s, which may have moved. Good for gradually + * accumulating output into a string buffer. + **/ + char *talloc_vasprintf_append(TALLOC_CTX *t, char *s, + const char *fmt, va_list ap) +{ + int len, s_len; + va_list ap2; + + VA_COPY(ap2, ap); + + s_len = strlen(s); + len = vsnprintf(NULL, 0, fmt, ap2); + + s = talloc_realloc(t, s, s_len + len+1); + if (!s) return NULL; + + VA_COPY(ap2, ap); + + vsnprintf(s+s_len, len+1, fmt, ap2); + + return s; +} + + +/** + * Return a human-readable description of all talloc memory usage. + * The result is allocated from @p t. + **/ +char *talloc_describe_all(TALLOC_CTX *rt) +{ + int n_pools = 0, total_chunks = 0; + size_t total_bytes = 0; + TALLOC_CTX *it; + char *s; + + if (!rt) return NULL; + + s = talloc_asprintf(rt, "global talloc allocations in pid: %u\n", + (unsigned) getpid()); + s = talloc_asprintf_append(rt, s, "%-40s %8s %8s\n", + "name", "chunks", "bytes"); + s = talloc_asprintf_append(rt, s, "%-40s %8s %8s\n", + "----------------------------------------", + "--------", + "--------"); + MUTEX_LOCK_BY_ID(MUTEX_TALLOC); + + for (it = list_head; it; it = it->next) { + size_t bytes; + int n_chunks; + fstring what; + + n_pools++; + + talloc_get_allocation(it, &bytes, &n_chunks); + + if (it->name) + fstrcpy(what, it->name); + else + slprintf(what, sizeof(what), "@%p", it); + + s = talloc_asprintf_append(rt, s, "%-40s %8u %8u\n", + what, + (unsigned) n_chunks, + (unsigned) bytes); + total_bytes += bytes; + total_chunks += n_chunks; + } + + MUTEX_UNLOCK_BY_ID(MUTEX_TALLOC); + + s = talloc_asprintf_append(rt, s, "%-40s %8s %8s\n", + "----------------------------------------", + "--------", + "--------"); + + s = talloc_asprintf_append(rt, s, "%-40s %8u %8u\n", + "TOTAL", + (unsigned) total_chunks, (unsigned) total_bytes); + + return s; +} + + + +/** + * Return an estimated memory usage for the specified pool. This does + * not include memory used by the underlying malloc implementation. + **/ +void talloc_get_allocation(TALLOC_CTX *t, + size_t *total_bytes, + int *n_chunks) +{ + struct talloc_chunk *chunk; + + if (t) { + *total_bytes = 0; + *n_chunks = 0; + + for (chunk = t->list; chunk; chunk = chunk->next) { + n_chunks[0]++; + *total_bytes += chunk->size; + } + } +} + + +/* + move a lump of memory from one talloc context to another + return the ptr on success, or NULL if it could not be found + in the old context or could not be transferred +*/ +const void *talloc_steal(TALLOC_CTX *old_ctx, TALLOC_CTX *new_ctx, const void *ptr) +{ + struct talloc_chunk *tc, *tc2; + + if (!ptr || !old_ctx->list) return NULL; + + /* as a special case, see if its the first element in the + list */ + if (old_ctx->list->ptr == ptr) { + tc = old_ctx->list; + old_ctx->list = old_ctx->list->next; + tc->next = new_ctx->list; + new_ctx->list = tc; + old_ctx->total_alloc_size -= tc->size; + new_ctx->total_alloc_size += tc->size; + return ptr; + } + + /* find it in the old context */ + for (tc=old_ctx->list; tc->next; tc=tc->next) { + if (tc->next->ptr == ptr) break; + } + + if (!tc->next) return NULL; + + /* move it to the new context */ + tc2 = tc->next; + tc->next = tc->next->next; + tc2->next = new_ctx->list; + new_ctx->list = tc2; + old_ctx->total_alloc_size -= tc2->size; + new_ctx->total_alloc_size += tc2->size; + + return ptr; +} + + +/** @} */ diff --git a/source4/lib/tallocmsg.c b/source4/lib/tallocmsg.c new file mode 100644 index 0000000000..bbe1ee60a4 --- /dev/null +++ b/source4/lib/tallocmsg.c @@ -0,0 +1,58 @@ +/* + samba -- Unix SMB/CIFS implementation. + Copyright (C) 2001, 2002 by Martin Pool + + 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" + +/** + * @file tallocmsg.c + * + * Glue code between talloc profiling and the Samba messaging system. + **/ + + +/** + * Respond to a POOL_USAGE message by sending back string form of memory + * usage stats. + **/ +void msg_pool_usage(int msg_type, pid_t src_pid, + void *UNUSED(buf), size_t UNUSED(len)) +{ + char *reply; + TALLOC_CTX *reply_pool = talloc_init("msg_pool_usage"); + + SMB_ASSERT(msg_type == MSG_REQ_POOL_USAGE); + + DEBUG(2,("Got POOL_USAGE\n")); + + reply = talloc_describe_all(reply_pool); + + message_send_pid(src_pid, MSG_POOL_USAGE, + reply, strlen(reply)+1, True); + + talloc_destroy(reply_pool); +} + +/** + * Register handler for MSG_REQ_POOL_USAGE + **/ +void register_msg_pool_usage(void) +{ + message_register(MSG_REQ_POOL_USAGE, msg_pool_usage); + DEBUG(2, ("Registered MSG_REQ_POOL_USAGE\n")); +} diff --git a/source4/lib/talloctort.c b/source4/lib/talloctort.c new file mode 100644 index 0000000000..ad5de38581 --- /dev/null +++ b/source4/lib/talloctort.c @@ -0,0 +1,65 @@ +/* + Unix SMB/CIFS implementation. + Samba temporary memory allocation functions -- torturer + Copyright (C) 2001 by Martin Pool <mbp@samba.org> + + 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" + +#define NCTX 10 +#define NOBJ 20 + +int main(void) +{ + int i; + TALLOC_CTX *ctx[NCTX]; + + for (i = 0; i < NCTX; i++) { + ctx[i] = talloc_init("torture(%d)", i); + } + + for (i = 0; i < NCTX; i++) { + int j; + for (j = 0; j < NOBJ; j++) { + char *p; + size_t size = 1<<(i/3+j); + + p = talloc(ctx[i], size); + if (!p) { + fprintf(stderr, + "failed to talloc %.0f bytes\n", + (double) size); + exit(1); + } + + memset(p, 'A' + j, size); + } + } + + for (i = 0; i < NCTX; i++) { + printf("talloc@%p %-40s %dkB\n", ctx[i], + talloc_pool_name(ctx[i]), + talloc_pool_size(ctx[i]) >> 10); + } + + printf("%s", talloc_describe_all(ctx[0])); + + for (i = NCTX - 1; i >= 0; i--) + talloc_destroy(ctx[i]); + + return 0; +} diff --git a/source4/lib/tdb/README b/source4/lib/tdb/README new file mode 100644 index 0000000000..fac3eacb4d --- /dev/null +++ b/source4/lib/tdb/README @@ -0,0 +1,167 @@ +tdb - a trivial database system +tridge@linuxcare.com December 1999 +================================== + +This is a simple database API. It was inspired by the realisation that +in Samba we have several ad-hoc bits of code that essentially +implement small databases for sharing structures between parts of +Samba. As I was about to add another I realised that a generic +database module was called for to replace all the ad-hoc bits. + +I based the interface on gdbm. I couldn't use gdbm as we need to be +able to have multiple writers to the databases at one time. + +Compilation +----------- + +add HAVE_MMAP=1 to use mmap instead of read/write +add TDB_DEBUG=1 for verbose debug info +add NOLOCK=1 to disable locking code + +Testing +------- + +Compile tdbtest.c and link with gdbm for testing. tdbtest will perform +identical operations via tdb and gdbm then make sure the result is the +same + +Also included is tdbtool, which allows simple database manipulation +on the commandline. + +tdbtest and tdbtool are not built as part of Samba, but are included +for completeness. + +Interface +--------- + +The interface is very similar to gdbm except for the following: + +- different open interface. The tdb_open call is more similar to a + traditional open() +- no tdbm_reorganise() function +- no tdbm_sync() function. No operations are cached in the library anyway +- added a tdb_traverse() function for traversing the whole database + +A general rule for using tdb is that the caller frees any returned +TDB_DATA structures. Just call free(p.dptr) to free a TDB_DATA +return value called p. This is the same as gdbm. + +here is a full list of tdb functions with brief descriptions. + + +---------------------------------------------------------------------- +TDB_CONTEXT *tdb_open(char *name, int hash_size, int tdb_flags, + int open_flags, mode_t mode) + + open the database, creating it if necessary + + The open_flags and mode are passed straight to the open call on the database + file. A flags value of O_WRONLY is invalid + + The hash size is advisory, use zero for a default value. + + return is NULL on error + + possible tdb_flags are: + TDB_CLEAR_IF_FIRST - clear database if we are the only one with it open + TDB_INTERNAL - don't use a file, instaed store the data in + memory. The filename is ignored in this case. + TDB_NOLOCK - don't do any locking + TDB_NOMMAP - don't use mmap + +---------------------------------------------------------------------- +char *tdb_error(TDB_CONTEXT *tdb); + + return a error string for the last tdb error + +---------------------------------------------------------------------- +int tdb_close(TDB_CONTEXT *tdb); + + close a database + +---------------------------------------------------------------------- +int tdb_update(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf); + + update an entry in place - this only works if the new data size + is <= the old data size and the key exists. + on failure return -1 + +---------------------------------------------------------------------- +TDB_DATA tdb_fetch(TDB_CONTEXT *tdb, TDB_DATA key); + + fetch an entry in the database given a key + if the return value has a null dptr then a error occurred + + caller must free the resulting data + +---------------------------------------------------------------------- +int tdb_exists(TDB_CONTEXT *tdb, TDB_DATA key); + + check if an entry in the database exists + + note that 1 is returned if the key is found and 0 is returned if not found + this doesn't match the conventions in the rest of this module, but is + compatible with gdbm + +---------------------------------------------------------------------- +int tdb_traverse(TDB_CONTEXT *tdb, int (*fn)(TDB_CONTEXT *tdb, + TDB_DATA key, TDB_DATA dbuf, void *state), void *state); + + traverse the entire database - calling fn(tdb, key, data, state) on each + element. + + return -1 on error or the record count traversed + + if fn is NULL then it is not called + + a non-zero return value from fn() indicates that the traversal should stop + +---------------------------------------------------------------------- +TDB_DATA tdb_firstkey(TDB_CONTEXT *tdb); + + find the first entry in the database and return its key + + the caller must free the returned data + +---------------------------------------------------------------------- +TDB_DATA tdb_nextkey(TDB_CONTEXT *tdb, TDB_DATA key); + + find the next entry in the database, returning its key + + the caller must free the returned data + +---------------------------------------------------------------------- +int tdb_delete(TDB_CONTEXT *tdb, TDB_DATA key); + + delete an entry in the database given a key + +---------------------------------------------------------------------- +int tdb_store(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, int flag); + + store an element in the database, replacing any existing element + with the same key + + If flag==TDB_INSERT then don't overwrite an existing entry + If flag==TDB_MODIFY then don't create a new entry + + return 0 on success, -1 on failure + +---------------------------------------------------------------------- +int tdb_writelock(TDB_CONTEXT *tdb); + + lock the database. If we already have it locked then don't do anything + +---------------------------------------------------------------------- +int tdb_writeunlock(TDB_CONTEXT *tdb); + unlock the database + +---------------------------------------------------------------------- +int tdb_lockchain(TDB_CONTEXT *tdb, TDB_DATA key); + + lock one hash chain. This is meant to be used to reduce locking + contention - it cannot guarantee how many records will be locked + +---------------------------------------------------------------------- +int tdb_unlockchain(TDB_CONTEXT *tdb, TDB_DATA key); + + unlock one hash chain diff --git a/source4/lib/tdb/spinlock.c b/source4/lib/tdb/spinlock.c new file mode 100644 index 0000000000..2370ce3bdd --- /dev/null +++ b/source4/lib/tdb/spinlock.c @@ -0,0 +1,430 @@ +/* + Unix SMB/CIFS implementation. + Samba database functions + Copyright (C) Anton Blanchard 2001 + + 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. +*/ +#if HAVE_CONFIG_H +#include <config.h> +#endif + +#if STANDALONE +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <fcntl.h> +#include <errno.h> +#include <sys/stat.h> +#include <time.h> +#include <signal.h> +#include "tdb.h" +#include "spinlock.h" + +#define DEBUG +#else +#include "includes.h" +#endif + +#ifdef USE_SPINLOCKS + +/* + * ARCH SPECIFIC + */ + +#if defined(SPARC_SPINLOCKS) + +static inline int __spin_trylock(spinlock_t *lock) +{ + unsigned int result; + + asm volatile("ldstub [%1], %0" + : "=r" (result) + : "r" (lock) + : "memory"); + + return (result == 0) ? 0 : EBUSY; +} + +static inline void __spin_unlock(spinlock_t *lock) +{ + asm volatile("":::"memory"); + *lock = 0; +} + +static inline void __spin_lock_init(spinlock_t *lock) +{ + *lock = 0; +} + +static inline int __spin_is_locked(spinlock_t *lock) +{ + return (*lock != 0); +} + +#elif defined(POWERPC_SPINLOCKS) + +static inline int __spin_trylock(spinlock_t *lock) +{ + unsigned int result; + + __asm__ __volatile__( +"1: lwarx %0,0,%1\n\ + cmpwi 0,%0,0\n\ + li %0,0\n\ + bne- 2f\n\ + li %0,1\n\ + stwcx. %0,0,%1\n\ + bne- 1b\n\ + isync\n\ +2:" : "=&r"(result) + : "r"(lock) + : "cr0", "memory"); + + return (result == 1) ? 0 : EBUSY; +} + +static inline void __spin_unlock(spinlock_t *lock) +{ + asm volatile("eieio":::"memory"); + *lock = 0; +} + +static inline void __spin_lock_init(spinlock_t *lock) +{ + *lock = 0; +} + +static inline int __spin_is_locked(spinlock_t *lock) +{ + return (*lock != 0); +} + +#elif defined(INTEL_SPINLOCKS) + +static inline int __spin_trylock(spinlock_t *lock) +{ + int oldval; + + asm volatile("xchgl %0,%1" + : "=r" (oldval), "=m" (*lock) + : "0" (0) + : "memory"); + + return oldval > 0 ? 0 : EBUSY; +} + +static inline void __spin_unlock(spinlock_t *lock) +{ + asm volatile("":::"memory"); + *lock = 1; +} + +static inline void __spin_lock_init(spinlock_t *lock) +{ + *lock = 1; +} + +static inline int __spin_is_locked(spinlock_t *lock) +{ + return (*lock != 1); +} + +#elif defined(MIPS_SPINLOCKS) + +static inline unsigned int load_linked(unsigned long addr) +{ + unsigned int res; + + __asm__ __volatile__("ll\t%0,(%1)" + : "=r" (res) + : "r" (addr)); + + return res; +} + +static inline unsigned int store_conditional(unsigned long addr, unsigned int value) +{ + unsigned int res; + + __asm__ __volatile__("sc\t%0,(%2)" + : "=r" (res) + : "0" (value), "r" (addr)); + return res; +} + +static inline int __spin_trylock(spinlock_t *lock) +{ + unsigned int mw; + + do { + mw = load_linked(lock); + if (mw) + return EBUSY; + } while (!store_conditional(lock, 1)); + + asm volatile("":::"memory"); + + return 0; +} + +static inline void __spin_unlock(spinlock_t *lock) +{ + asm volatile("":::"memory"); + *lock = 0; +} + +static inline void __spin_lock_init(spinlock_t *lock) +{ + *lock = 0; +} + +static inline int __spin_is_locked(spinlock_t *lock) +{ + return (*lock != 0); +} + +#else +#error Need to implement spinlock code in spinlock.c +#endif + +/* + * OS SPECIFIC + */ + +static void yield_cpu(void) +{ + struct timespec tm; + +#ifdef USE_SCHED_YIELD + sched_yield(); +#else + /* Linux will busy loop for delays < 2ms on real time tasks */ + tm.tv_sec = 0; + tm.tv_nsec = 2000000L + 1; + nanosleep(&tm, NULL); +#endif +} + +static int this_is_smp(void) +{ + return 0; +} + +/* + * GENERIC + */ + +static int smp_machine = 0; + +static inline void __spin_lock(spinlock_t *lock) +{ + int ntries = 0; + + while(__spin_trylock(lock)) { + while(__spin_is_locked(lock)) { + if (smp_machine && ntries++ < MAX_BUSY_LOOPS) + continue; + yield_cpu(); + } + } +} + +static void __read_lock(tdb_rwlock_t *rwlock) +{ + int ntries = 0; + + while(1) { + __spin_lock(&rwlock->lock); + + if (!(rwlock->count & RWLOCK_BIAS)) { + rwlock->count++; + __spin_unlock(&rwlock->lock); + return; + } + + __spin_unlock(&rwlock->lock); + + while(rwlock->count & RWLOCK_BIAS) { + if (smp_machine && ntries++ < MAX_BUSY_LOOPS) + continue; + yield_cpu(); + } + } +} + +static void __write_lock(tdb_rwlock_t *rwlock) +{ + int ntries = 0; + + while(1) { + __spin_lock(&rwlock->lock); + + if (rwlock->count == 0) { + rwlock->count |= RWLOCK_BIAS; + __spin_unlock(&rwlock->lock); + return; + } + + __spin_unlock(&rwlock->lock); + + while(rwlock->count != 0) { + if (smp_machine && ntries++ < MAX_BUSY_LOOPS) + continue; + yield_cpu(); + } + } +} + +static void __write_unlock(tdb_rwlock_t *rwlock) +{ + __spin_lock(&rwlock->lock); + +#ifdef DEBUG + if (!(rwlock->count & RWLOCK_BIAS)) + fprintf(stderr, "bug: write_unlock\n"); +#endif + + rwlock->count &= ~RWLOCK_BIAS; + __spin_unlock(&rwlock->lock); +} + +static void __read_unlock(tdb_rwlock_t *rwlock) +{ + __spin_lock(&rwlock->lock); + +#ifdef DEBUG + if (!rwlock->count) + fprintf(stderr, "bug: read_unlock\n"); + + if (rwlock->count & RWLOCK_BIAS) + fprintf(stderr, "bug: read_unlock\n"); +#endif + + rwlock->count--; + __spin_unlock(&rwlock->lock); +} + +/* TDB SPECIFIC */ + +/* lock a list in the database. list -1 is the alloc list */ +int tdb_spinlock(TDB_CONTEXT *tdb, int list, int rw_type) +{ + tdb_rwlock_t *rwlocks; + + if (!tdb->map_ptr) return -1; + rwlocks = (tdb_rwlock_t *)((char *)tdb->map_ptr + tdb->header.rwlocks); + + switch(rw_type) { + case F_RDLCK: + __read_lock(&rwlocks[list+1]); + break; + + case F_WRLCK: + __write_lock(&rwlocks[list+1]); + break; + + default: + return TDB_ERRCODE(TDB_ERR_LOCK, -1); + } + return 0; +} + +/* unlock the database. */ +int tdb_spinunlock(TDB_CONTEXT *tdb, int list, int rw_type) +{ + tdb_rwlock_t *rwlocks; + + if (!tdb->map_ptr) return -1; + rwlocks = (tdb_rwlock_t *)((char *)tdb->map_ptr + tdb->header.rwlocks); + + switch(rw_type) { + case F_RDLCK: + __read_unlock(&rwlocks[list+1]); + break; + + case F_WRLCK: + __write_unlock(&rwlocks[list+1]); + break; + + default: + return TDB_ERRCODE(TDB_ERR_LOCK, -1); + } + + return 0; +} + +int tdb_create_rwlocks(int fd, unsigned int hash_size) +{ + unsigned size, i; + tdb_rwlock_t *rwlocks; + + size = (hash_size + 1) * sizeof(tdb_rwlock_t); + rwlocks = malloc(size); + if (!rwlocks) + return -1; + + for(i = 0; i < hash_size+1; i++) { + __spin_lock_init(&rwlocks[i].lock); + rwlocks[i].count = 0; + } + + /* Write it out (appending to end) */ + if (write(fd, rwlocks, size) != size) { + free(rwlocks); + return -1; + } + smp_machine = this_is_smp(); + free(rwlocks); + return 0; +} + +int tdb_clear_spinlocks(TDB_CONTEXT *tdb) +{ + tdb_rwlock_t *rwlocks; + unsigned i; + + if (tdb->header.rwlocks == 0) return 0; + if (!tdb->map_ptr) return -1; + + /* We're mmapped here */ + rwlocks = (tdb_rwlock_t *)((char *)tdb->map_ptr + tdb->header.rwlocks); + for(i = 0; i < tdb->header.hash_size+1; i++) { + __spin_lock_init(&rwlocks[i].lock); + rwlocks[i].count = 0; + } + return 0; +} +#else +int tdb_create_rwlocks(int fd, unsigned int hash_size) { return 0; } +int tdb_spinlock(TDB_CONTEXT *tdb, int list, int rw_type) { return -1; } +int tdb_spinunlock(TDB_CONTEXT *tdb, int list, int rw_type) { return -1; } + +/* Non-spinlock version: remove spinlock pointer */ +int tdb_clear_spinlocks(TDB_CONTEXT *tdb) +{ + tdb_off off = (tdb_off)((char *)&tdb->header.rwlocks + - (char *)&tdb->header); + + tdb->header.rwlocks = 0; + if (lseek(tdb->fd, off, SEEK_SET) != off + || write(tdb->fd, (void *)&tdb->header.rwlocks, + sizeof(tdb->header.rwlocks)) + != sizeof(tdb->header.rwlocks)) + return -1; + return 0; +} +#endif diff --git a/source4/lib/tdb/spinlock.h b/source4/lib/tdb/spinlock.h new file mode 100644 index 0000000000..d6a2ac6eb8 --- /dev/null +++ b/source4/lib/tdb/spinlock.h @@ -0,0 +1,55 @@ +#ifndef __SPINLOCK_H__ +#define __SPINLOCK_H__ + +#if HAVE_CONFIG_H +#include <config.h> +#endif + +#include "tdb.h" + +#ifdef USE_SPINLOCKS + +#define RWLOCK_BIAS 0x1000UL + +/* OS SPECIFIC */ +#define MAX_BUSY_LOOPS 1000 +#undef USE_SCHED_YIELD + +/* ARCH SPECIFIC */ +/* We should make sure these are padded to a cache line */ +#if defined(SPARC_SPINLOCKS) +typedef volatile char spinlock_t; +#elif defined(POWERPC_SPINLOCKS) +typedef volatile unsigned long spinlock_t; +#elif defined(INTEL_SPINLOCKS) +typedef volatile int spinlock_t; +#elif defined(MIPS_SPINLOCKS) +typedef volatile unsigned long spinlock_t; +#else +#error Need to implement spinlock code in spinlock.h +#endif + +typedef struct { + spinlock_t lock; + volatile int count; +} tdb_rwlock_t; + +int tdb_spinlock(TDB_CONTEXT *tdb, int list, int rw_type); +int tdb_spinunlock(TDB_CONTEXT *tdb, int list, int rw_type); +int tdb_create_rwlocks(int fd, unsigned int hash_size); +int tdb_clear_spinlocks(TDB_CONTEXT *tdb); + +#else /* !USE_SPINLOCKS */ +#if 0 +#define tdb_create_rwlocks(fd, hash_size) 0 +#define tdb_spinlock(tdb, list, rw_type) (-1) +#define tdb_spinunlock(tdb, list, rw_type) (-1) +#else +int tdb_spinlock(TDB_CONTEXT *tdb, int list, int rw_type); +int tdb_spinunlock(TDB_CONTEXT *tdb, int list, int rw_type); +int tdb_create_rwlocks(int fd, unsigned int hash_size); +#endif +int tdb_clear_spinlocks(TDB_CONTEXT *tdb); +#endif + +#endif diff --git a/source4/lib/tdb/tdb.c b/source4/lib/tdb/tdb.c new file mode 100644 index 0000000000..097209ff7a --- /dev/null +++ b/source4/lib/tdb/tdb.c @@ -0,0 +1,2020 @@ + /* + Unix SMB/CIFS implementation. + Samba database functions + Copyright (C) Andrew Tridgell 1999-2000 + Copyright (C) Luke Kenneth Casson Leighton 2000 + Copyright (C) Paul `Rusty' Russell 2000 + Copyright (C) Jeremy Allison 2000-2003 + + 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. +*/ +#ifdef STANDALONE +#if HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdlib.h> +#include <stdio.h> +#include <fcntl.h> +#include <unistd.h> +#include <string.h> +#include <fcntl.h> +#include <errno.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <signal.h> +#include "tdb.h" +#include "spinlock.h" +#else +#include "includes.h" +#endif + +#define TDB_MAGIC_FOOD "TDB file\n" +#define TDB_VERSION (0x26011967 + 6) +#define TDB_MAGIC (0x26011999U) +#define TDB_FREE_MAGIC (~TDB_MAGIC) +#define TDB_DEAD_MAGIC (0xFEE1DEAD) +#define TDB_ALIGNMENT 4 +#define MIN_REC_SIZE (2*sizeof(struct list_struct) + TDB_ALIGNMENT) +#define DEFAULT_HASH_SIZE 131 +#define TDB_PAGE_SIZE 0x2000 +#define FREELIST_TOP (sizeof(struct tdb_header)) +#define TDB_ALIGN(x,a) (((x) + (a)-1) & ~((a)-1)) +#define TDB_BYTEREV(x) (((((x)&0xff)<<24)|((x)&0xFF00)<<8)|(((x)>>8)&0xFF00)|((x)>>24)) +#define TDB_DEAD(r) ((r)->magic == TDB_DEAD_MAGIC) +#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)) + +/* NB assumes there is a local variable called "tdb" that is the + * current context, also takes doubly-parenthesized print-style + * argument. */ +#define TDB_LOG(x) (tdb->log_fn?((tdb->log_fn x),0) : 0) + +/* lock offsets */ +#define GLOBAL_LOCK 0 +#define ACTIVE_LOCK 4 + +#ifndef MAP_FILE +#define MAP_FILE 0 +#endif + +#ifndef MAP_FAILED +#define MAP_FAILED ((void *)-1) +#endif + +/* free memory if the pointer is valid and zero the pointer */ +#ifndef SAFE_FREE +#define SAFE_FREE(x) do { if ((x) != NULL) {free((x)); (x)=NULL;} } while(0) +#endif + +#define BUCKET(hash) ((hash) % tdb->header.hash_size) +TDB_DATA tdb_null; + +/* all contexts, to ensure no double-opens (fcntl locks don't nest!) */ +static TDB_CONTEXT *tdbs = NULL; + +static int tdb_munmap(TDB_CONTEXT *tdb) +{ + if (tdb->flags & TDB_INTERNAL) + return 0; + +#ifdef HAVE_MMAP + if (tdb->map_ptr) { + int ret = munmap(tdb->map_ptr, tdb->map_size); + if (ret != 0) + return ret; + } +#endif + tdb->map_ptr = NULL; + return 0; +} + +static void tdb_mmap(TDB_CONTEXT *tdb) +{ + if (tdb->flags & TDB_INTERNAL) + return; + +#ifdef HAVE_MMAP + if (!(tdb->flags & TDB_NOMMAP)) { + tdb->map_ptr = mmap(NULL, tdb->map_size, + PROT_READ|(tdb->read_only? 0:PROT_WRITE), + MAP_SHARED|MAP_FILE, tdb->fd, 0); + + /* + * NB. When mmap fails it returns MAP_FAILED *NOT* NULL !!!! + */ + + if (tdb->map_ptr == MAP_FAILED) { + tdb->map_ptr = NULL; + TDB_LOG((tdb, 2, "tdb_mmap failed for size %d (%s)\n", + tdb->map_size, strerror(errno))); + } + } else { + tdb->map_ptr = NULL; + } +#else + tdb->map_ptr = NULL; +#endif +} + +/* Endian conversion: we only ever deal with 4 byte quantities */ +static void *convert(void *buf, u32 size) +{ + u32 i, *p = buf; + for (i = 0; i < size / 4; i++) + p[i] = TDB_BYTEREV(p[i]); + return buf; +} +#define DOCONV() (tdb->flags & TDB_CONVERT) +#define CONVERT(x) (DOCONV() ? convert(&x, sizeof(x)) : &x) + +/* the body of the database is made of one list_struct for the free space + plus a separate data list for each hash value */ +struct list_struct { + tdb_off next; /* offset of the next record in the list */ + tdb_len rec_len; /* total byte length of record */ + tdb_len key_len; /* byte length of key */ + tdb_len data_len; /* byte length of data */ + u32 full_hash; /* the full 32 bit hash of the key */ + u32 magic; /* try to catch errors */ + /* the following union is implied: + union { + char record[rec_len]; + struct { + char key[key_len]; + char data[data_len]; + } + u32 totalsize; (tailer) + } + */ +}; + +/*************************************************************** + Allow a caller to set a "alarm" flag that tdb can check to abort + a blocking lock on SIGALRM. +***************************************************************/ + +static sig_atomic_t *palarm_fired; + +void tdb_set_lock_alarm(sig_atomic_t *palarm) +{ + palarm_fired = palarm; +} + +/* a byte range locking function - return 0 on success + this functions locks/unlocks 1 byte at the specified offset. + + On error, errno is also set so that errors are passed back properly + through tdb_open(). */ +static int tdb_brlock(TDB_CONTEXT *tdb, tdb_off offset, + int rw_type, int lck_type, int probe) +{ + struct flock fl; + int ret; + + if (tdb->flags & TDB_NOLOCK) + return 0; + if ((rw_type == F_WRLCK) && (tdb->read_only)) { + errno = EACCES; + return -1; + } + + fl.l_type = rw_type; + fl.l_whence = SEEK_SET; + fl.l_start = offset; + fl.l_len = 1; + fl.l_pid = 0; + + do { + ret = fcntl(tdb->fd,lck_type,&fl); + if (ret == -1 && errno == EINTR && palarm_fired && *palarm_fired) + break; + } while (ret == -1 && errno == EINTR); + + if (ret == -1) { + if (!probe && lck_type != F_SETLK) { + /* Ensure error code is set for log fun to examine. */ + if (errno == EINTR && palarm_fired && *palarm_fired) + tdb->ecode = TDB_ERR_LOCK_TIMEOUT; + else + tdb->ecode = TDB_ERR_LOCK; + TDB_LOG((tdb, 5,"tdb_brlock failed (fd=%d) at offset %d rw_type=%d lck_type=%d\n", + tdb->fd, offset, rw_type, lck_type)); + } + /* Was it an alarm timeout ? */ + if (errno == EINTR && palarm_fired && *palarm_fired) + return TDB_ERRCODE(TDB_ERR_LOCK_TIMEOUT, -1); + /* Otherwise - generic lock error. */ + /* errno set by fcntl */ + return TDB_ERRCODE(TDB_ERR_LOCK, -1); + } + return 0; +} + +/* lock a list in the database. list -1 is the alloc list */ +static int tdb_lock(TDB_CONTEXT *tdb, int list, int ltype) +{ + if (list < -1 || list >= (int)tdb->header.hash_size) { + TDB_LOG((tdb, 0,"tdb_lock: invalid list %d for ltype=%d\n", + list, ltype)); + return -1; + } + if (tdb->flags & TDB_NOLOCK) + return 0; + + /* Since fcntl locks don't nest, we do a lock for the first one, + and simply bump the count for future ones */ + if (tdb->locked[list+1].count == 0) { + if (!tdb->read_only && tdb->header.rwlocks) { + if (tdb_spinlock(tdb, list, ltype)) { + TDB_LOG((tdb, 0, "tdb_lock spinlock failed on list ltype=%d\n", + list, ltype)); + return -1; + } + } else if (tdb_brlock(tdb,FREELIST_TOP+4*list,ltype,F_SETLKW, 0)) { + TDB_LOG((tdb, 0,"tdb_lock failed on list %d ltype=%d (%s)\n", + list, ltype, strerror(errno))); + return -1; + } + tdb->locked[list+1].ltype = ltype; + } + tdb->locked[list+1].count++; + return 0; +} + +/* unlock the database: returns void because it's too late for errors. */ + /* changed to return int it may be interesting to know there + has been an error --simo */ +static int tdb_unlock(TDB_CONTEXT *tdb, int list, int ltype) +{ + int ret = -1; + + if (tdb->flags & TDB_NOLOCK) + return 0; + + /* Sanity checks */ + if (list < -1 || list >= (int)tdb->header.hash_size) { + TDB_LOG((tdb, 0, "tdb_unlock: list %d invalid (%d)\n", list, tdb->header.hash_size)); + return ret; + } + + if (tdb->locked[list+1].count==0) { + TDB_LOG((tdb, 0, "tdb_unlock: count is 0\n")); + return ret; + } + + if (tdb->locked[list+1].count == 1) { + /* Down to last nested lock: unlock underneath */ + if (!tdb->read_only && tdb->header.rwlocks) { + ret = tdb_spinunlock(tdb, list, ltype); + } else { + ret = tdb_brlock(tdb, FREELIST_TOP+4*list, F_UNLCK, F_SETLKW, 0); + } + } else { + ret = 0; + } + tdb->locked[list+1].count--; + + if (ret) + TDB_LOG((tdb, 0,"tdb_unlock: An error occurred unlocking!\n")); + return ret; +} + +/* This is based on the hash algorithm from gdbm */ +static u32 tdb_hash(TDB_DATA *key) +{ + u32 value; /* Used to compute the hash value. */ + u32 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++) + value = (value + (key->dptr[i] << (i*5 % 24))); + + return (1103515243 * value + 12345); +} + +/* check for an out of bounds access - if it is out of bounds then + see if the database has been expanded by someone else and expand + if necessary + note that "len" is the minimum length needed for the db +*/ +static int tdb_oob(TDB_CONTEXT *tdb, tdb_off len, int probe) +{ + struct stat st; + if (len <= tdb->map_size) + return 0; + if (tdb->flags & TDB_INTERNAL) { + if (!probe) { + /* Ensure ecode is set for log fn. */ + tdb->ecode = TDB_ERR_IO; + TDB_LOG((tdb, 0,"tdb_oob len %d beyond internal malloc size %d\n", + (int)len, (int)tdb->map_size)); + } + return TDB_ERRCODE(TDB_ERR_IO, -1); + } + + if (fstat(tdb->fd, &st) == -1) + return TDB_ERRCODE(TDB_ERR_IO, -1); + + if (st.st_size < (size_t)len) { + if (!probe) { + /* Ensure ecode is set for log fn. */ + tdb->ecode = TDB_ERR_IO; + TDB_LOG((tdb, 0,"tdb_oob len %d beyond eof at %d\n", + (int)len, (int)st.st_size)); + } + return TDB_ERRCODE(TDB_ERR_IO, -1); + } + + /* Unmap, update size, remap */ + if (tdb_munmap(tdb) == -1) + return TDB_ERRCODE(TDB_ERR_IO, -1); + tdb->map_size = st.st_size; + tdb_mmap(tdb); + return 0; +} + +/* write a lump of data at a specified offset */ +static int tdb_write(TDB_CONTEXT *tdb, tdb_off off, void *buf, tdb_len len) +{ + if (tdb_oob(tdb, off + len, 0) != 0) + return -1; + + if (tdb->map_ptr) + memcpy(off + (char *)tdb->map_ptr, buf, len); +#ifdef HAVE_PWRITE + else if (pwrite(tdb->fd, buf, len, off) != (ssize_t)len) { +#else + else if (lseek(tdb->fd, off, SEEK_SET) != off + || write(tdb->fd, buf, len) != (ssize_t)len) { +#endif + /* Ensure ecode is set for log fn. */ + tdb->ecode = TDB_ERR_IO; + TDB_LOG((tdb, 0,"tdb_write failed at %d len=%d (%s)\n", + off, len, strerror(errno))); + return TDB_ERRCODE(TDB_ERR_IO, -1); + } + return 0; +} + +/* read a lump of data at a specified offset, maybe convert */ +static int tdb_read(TDB_CONTEXT *tdb,tdb_off off,void *buf,tdb_len len,int cv) +{ + if (tdb_oob(tdb, off + len, 0) != 0) + return -1; + + if (tdb->map_ptr) + memcpy(buf, off + (char *)tdb->map_ptr, len); +#ifdef HAVE_PREAD + else if (pread(tdb->fd, buf, len, off) != (ssize_t)len) { +#else + else if (lseek(tdb->fd, off, SEEK_SET) != off + || read(tdb->fd, buf, len) != (ssize_t)len) { +#endif + /* Ensure ecode is set for log fn. */ + tdb->ecode = TDB_ERR_IO; + TDB_LOG((tdb, 0,"tdb_read failed at %d len=%d (%s)\n", + off, len, strerror(errno))); + return TDB_ERRCODE(TDB_ERR_IO, -1); + } + if (cv) + convert(buf, len); + return 0; +} + +/* read a lump of data, allocating the space for it */ +static char *tdb_alloc_read(TDB_CONTEXT *tdb, tdb_off offset, tdb_len len) +{ + char *buf; + + if (!(buf = malloc(len))) { + /* Ensure ecode is set for log fn. */ + tdb->ecode = TDB_ERR_OOM; + TDB_LOG((tdb, 0,"tdb_alloc_read malloc failed len=%d (%s)\n", + len, strerror(errno))); + return TDB_ERRCODE(TDB_ERR_OOM, buf); + } + if (tdb_read(tdb, offset, buf, len, 0) == -1) { + SAFE_FREE(buf); + return NULL; + } + return buf; +} + +/* read/write a tdb_off */ +static int ofs_read(TDB_CONTEXT *tdb, tdb_off offset, tdb_off *d) +{ + return tdb_read(tdb, offset, (char*)d, sizeof(*d), DOCONV()); +} +static int ofs_write(TDB_CONTEXT *tdb, tdb_off offset, tdb_off *d) +{ + tdb_off off = *d; + return tdb_write(tdb, offset, CONVERT(off), sizeof(*d)); +} + +/* read/write a record */ +static int rec_read(TDB_CONTEXT *tdb, tdb_off offset, struct list_struct *rec) +{ + if (tdb_read(tdb, offset, rec, sizeof(*rec),DOCONV()) == -1) + return -1; + if (TDB_BAD_MAGIC(rec)) { + /* Ensure ecode is set for log fn. */ + tdb->ecode = TDB_ERR_CORRUPT; + TDB_LOG((tdb, 0,"rec_read bad magic 0x%x at offset=%d\n", rec->magic, offset)); + return TDB_ERRCODE(TDB_ERR_CORRUPT, -1); + } + return tdb_oob(tdb, rec->next+sizeof(*rec), 0); +} +static int rec_write(TDB_CONTEXT *tdb, tdb_off offset, struct list_struct *rec) +{ + struct list_struct r = *rec; + return tdb_write(tdb, offset, CONVERT(r), sizeof(r)); +} + +/* read a freelist record and check for simple errors */ +static int rec_free_read(TDB_CONTEXT *tdb, tdb_off off, struct list_struct *rec) +{ + if (tdb_read(tdb, off, rec, sizeof(*rec),DOCONV()) == -1) + return -1; + + if (rec->magic == TDB_MAGIC) { + /* this happens when a app is showdown while deleting a record - we should + not completely fail when this happens */ + TDB_LOG((tdb, 0,"rec_free_read non-free magic at offset=%d - fixing\n", + rec->magic, off)); + rec->magic = TDB_FREE_MAGIC; + if (tdb_write(tdb, off, rec, sizeof(*rec)) == -1) + return -1; + } + + if (rec->magic != TDB_FREE_MAGIC) { + /* Ensure ecode is set for log fn. */ + tdb->ecode = TDB_ERR_CORRUPT; + TDB_LOG((tdb, 0,"rec_free_read bad magic 0x%x at offset=%d\n", + rec->magic, off)); + return TDB_ERRCODE(TDB_ERR_CORRUPT, -1); + } + if (tdb_oob(tdb, rec->next+sizeof(*rec), 0) != 0) + return -1; + return 0; +} + +/* update a record tailer (must hold allocation lock) */ +static int update_tailer(TDB_CONTEXT *tdb, tdb_off offset, + const struct list_struct *rec) +{ + tdb_off totalsize; + + /* Offset of tailer from record header */ + totalsize = sizeof(*rec) + rec->rec_len; + return ofs_write(tdb, offset + totalsize - sizeof(tdb_off), + &totalsize); +} + +static tdb_off tdb_dump_record(TDB_CONTEXT *tdb, tdb_off offset) +{ + struct list_struct rec; + tdb_off tailer_ofs, tailer; + + if (tdb_read(tdb, offset, (char *)&rec, sizeof(rec), DOCONV()) == -1) { + printf("ERROR: failed to read record at %u\n", offset); + return 0; + } + + printf(" rec: offset=%u next=%d rec_len=%d key_len=%d data_len=%d full_hash=0x%x magic=0x%x\n", + offset, rec.next, rec.rec_len, rec.key_len, rec.data_len, rec.full_hash, rec.magic); + + tailer_ofs = offset + sizeof(rec) + rec.rec_len - sizeof(tdb_off); + if (ofs_read(tdb, tailer_ofs, &tailer) == -1) { + printf("ERROR: failed to read tailer at %u\n", tailer_ofs); + return rec.next; + } + + if (tailer != rec.rec_len + sizeof(rec)) { + printf("ERROR: tailer does not match record! tailer=%u totalsize=%u\n", + (unsigned)tailer, (unsigned)(rec.rec_len + sizeof(rec))); + } + return rec.next; +} + +static int tdb_dump_chain(TDB_CONTEXT *tdb, int i) +{ + tdb_off rec_ptr, top; + + top = TDB_HASH_TOP(i); + + if (tdb_lock(tdb, i, F_WRLCK) != 0) + return -1; + + if (ofs_read(tdb, top, &rec_ptr) == -1) + return tdb_unlock(tdb, i, F_WRLCK); + + if (rec_ptr) + printf("hash=%d\n", i); + + while (rec_ptr) { + rec_ptr = tdb_dump_record(tdb, rec_ptr); + } + + return tdb_unlock(tdb, i, F_WRLCK); +} + +void tdb_dump_all(TDB_CONTEXT *tdb) +{ + int i; + for (i=0;i<tdb->header.hash_size;i++) { + tdb_dump_chain(tdb, i); + } + printf("freelist:\n"); + tdb_dump_chain(tdb, -1); +} + +int tdb_printfreelist(TDB_CONTEXT *tdb) +{ + int ret; + long total_free = 0; + tdb_off offset, rec_ptr; + struct list_struct rec; + + if ((ret = tdb_lock(tdb, -1, F_WRLCK)) != 0) + return ret; + + offset = FREELIST_TOP; + + /* read in the freelist top */ + if (ofs_read(tdb, offset, &rec_ptr) == -1) { + tdb_unlock(tdb, -1, F_WRLCK); + return 0; + } + + printf("freelist top=[0x%08x]\n", rec_ptr ); + while (rec_ptr) { + if (tdb_read(tdb, rec_ptr, (char *)&rec, sizeof(rec), DOCONV()) == -1) { + tdb_unlock(tdb, -1, F_WRLCK); + return -1; + } + + if (rec.magic != TDB_FREE_MAGIC) { + printf("bad magic 0x%08x in free list\n", rec.magic); + tdb_unlock(tdb, -1, F_WRLCK); + return -1; + } + + printf("entry offset=[0x%08x], rec.rec_len = [0x%08x (%d)]\n", rec.next, rec.rec_len, rec.rec_len ); + total_free += rec.rec_len; + + /* move to the next record */ + rec_ptr = rec.next; + } + printf("total rec_len = [0x%08x (%d)]\n", (int)total_free, + (int)total_free); + + return tdb_unlock(tdb, -1, F_WRLCK); +} + +/* Remove an element from the freelist. Must have alloc lock. */ +static int remove_from_freelist(TDB_CONTEXT *tdb, tdb_off off, tdb_off next) +{ + tdb_off last_ptr, i; + + /* read in the freelist top */ + last_ptr = FREELIST_TOP; + while (ofs_read(tdb, last_ptr, &i) != -1 && i != 0) { + if (i == off) { + /* We've found it! */ + return ofs_write(tdb, last_ptr, &next); + } + /* Follow chain (next offset is at start of record) */ + last_ptr = i; + } + TDB_LOG((tdb, 0,"remove_from_freelist: not on list at off=%d\n", off)); + return TDB_ERRCODE(TDB_ERR_CORRUPT, -1); +} + +/* Add an element into the freelist. Merge adjacent records if + neccessary. */ +static int tdb_free(TDB_CONTEXT *tdb, tdb_off offset, struct list_struct *rec) +{ + tdb_off right, left; + + /* Allocation and tailer lock */ + if (tdb_lock(tdb, -1, F_WRLCK) != 0) + return -1; + + /* set an initial tailer, so if we fail we don't leave a bogus record */ + if (update_tailer(tdb, offset, rec) != 0) { + TDB_LOG((tdb, 0, "tdb_free: upfate_tailer failed!\n")); + goto fail; + } + + /* Look right first (I'm an Australian, dammit) */ + right = offset + sizeof(*rec) + rec->rec_len; + if (right + sizeof(*rec) <= tdb->map_size) { + struct list_struct r; + + if (tdb_read(tdb, right, &r, sizeof(r), DOCONV()) == -1) { + TDB_LOG((tdb, 0, "tdb_free: right read failed at %u\n", right)); + goto left; + } + + /* If it's free, expand to include it. */ + if (r.magic == TDB_FREE_MAGIC) { + if (remove_from_freelist(tdb, right, r.next) == -1) { + TDB_LOG((tdb, 0, "tdb_free: right free failed at %u\n", right)); + goto left; + } + rec->rec_len += sizeof(r) + r.rec_len; + } + } + +left: + /* Look left */ + left = offset - sizeof(tdb_off); + if (left > TDB_HASH_TOP(tdb->header.hash_size-1)) { + struct list_struct l; + tdb_off leftsize; + + /* Read in tailer and jump back to header */ + if (ofs_read(tdb, left, &leftsize) == -1) { + TDB_LOG((tdb, 0, "tdb_free: left offset read failed at %u\n", left)); + goto update; + } + left = offset - leftsize; + + /* Now read in record */ + if (tdb_read(tdb, left, &l, sizeof(l), DOCONV()) == -1) { + TDB_LOG((tdb, 0, "tdb_free: left read failed at %u (%u)\n", left, leftsize)); + goto update; + } + + /* 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, 0, "tdb_free: left free failed at %u\n", left)); + goto update; + } else { + offset = left; + rec->rec_len += leftsize; + } + } + } + +update: + if (update_tailer(tdb, offset, rec) == -1) { + TDB_LOG((tdb, 0, "tdb_free: update_tailer failed at %u\n", offset)); + goto fail; + } + + /* Now, prepend to free list */ + rec->magic = TDB_FREE_MAGIC; + + if (ofs_read(tdb, FREELIST_TOP, &rec->next) == -1 || + rec_write(tdb, offset, rec) == -1 || + ofs_write(tdb, FREELIST_TOP, &offset) == -1) { + TDB_LOG((tdb, 0, "tdb_free record write failed at offset=%d\n", offset)); + goto fail; + } + + /* And we're done. */ + tdb_unlock(tdb, -1, F_WRLCK); + return 0; + + fail: + tdb_unlock(tdb, -1, F_WRLCK); + return -1; +} + + +/* expand a file. we prefer to use ftruncate, as that is what posix + says to use for mmap expansion */ +static int expand_file(TDB_CONTEXT *tdb, tdb_off size, tdb_off addition) +{ + char buf[1024]; +#if HAVE_FTRUNCATE_EXTEND + if (ftruncate(tdb->fd, size+addition) != 0) { + TDB_LOG((tdb, 0, "expand_file ftruncate to %d failed (%s)\n", + size+addition, strerror(errno))); + return -1; + } +#else + char b = 0; + +#ifdef HAVE_PWRITE + if (pwrite(tdb->fd, &b, 1, (size+addition) - 1) != 1) { +#else + if (lseek(tdb->fd, (size+addition) - 1, SEEK_SET) != (size+addition) - 1 || + write(tdb->fd, &b, 1) != 1) { +#endif + TDB_LOG((tdb, 0, "expand_file to %d failed (%s)\n", + size+addition, strerror(errno))); + return -1; + } +#endif + + /* now fill the file with something. This ensures that the file isn't sparse, which would be + very bad if we ran out of disk. This must be done with write, not via mmap */ + memset(buf, 0x42, sizeof(buf)); + while (addition) { + int n = addition>sizeof(buf)?sizeof(buf):addition; +#ifdef HAVE_PWRITE + int ret = pwrite(tdb->fd, buf, n, size); +#else + int ret; + if (lseek(tdb->fd, size, SEEK_SET) != size) + return -1; + ret = write(tdb->fd, buf, n); +#endif + if (ret != n) { + TDB_LOG((tdb, 0, "expand_file write of %d failed (%s)\n", + n, strerror(errno))); + return -1; + } + addition -= n; + size += n; + } + return 0; +} + + +/* expand the database at least size bytes by expanding the underlying + file and doing the mmap again if necessary */ +static int tdb_expand(TDB_CONTEXT *tdb, tdb_off size) +{ + struct list_struct rec; + tdb_off offset; + + if (tdb_lock(tdb, -1, F_WRLCK) == -1) { + TDB_LOG((tdb, 0, "lock failed in tdb_expand\n")); + return -1; + } + + /* must know about any previous expansions by another process */ + 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 TDB_PAGE_SIZE */ + size = TDB_ALIGN(tdb->map_size + size*10, TDB_PAGE_SIZE) - tdb->map_size; + + if (!(tdb->flags & TDB_INTERNAL)) + tdb_munmap(tdb); + + /* + * We must ensure the file is unmapped before doing this + * to ensure consistency with systems like OpenBSD where + * writes and mmaps are not consistent. + */ + + /* expand the file itself */ + if (!(tdb->flags & TDB_INTERNAL)) { + if (expand_file(tdb, tdb->map_size, size) != 0) + goto fail; + } + + tdb->map_size += size; + + if (tdb->flags & TDB_INTERNAL) + tdb->map_ptr = realloc(tdb->map_ptr, tdb->map_size); + else { + /* + * We must ensure the file is remapped before adding the space + * to ensure consistency with systems like OpenBSD where + * writes and mmaps are not consistent. + */ + + /* We're ok if the mmap fails as we'll fallback to read/write */ + tdb_mmap(tdb); + } + + /* form a new freelist record */ + memset(&rec,'\0',sizeof(rec)); + rec.rec_len = size - sizeof(rec); + + /* link it into the free list */ + offset = tdb->map_size - size; + if (tdb_free(tdb, offset, &rec) == -1) + goto fail; + + tdb_unlock(tdb, -1, F_WRLCK); + return 0; + fail: + tdb_unlock(tdb, -1, F_WRLCK); + return -1; +} + +/* allocate some space from the free list. The offset returned points + to a unconnected list_struct within the database with room for at + least length bytes of total data + + 0 is returned if the space could not be allocated + */ +static tdb_off tdb_allocate(TDB_CONTEXT *tdb, tdb_len length, + struct list_struct *rec) +{ + tdb_off rec_ptr, last_ptr, newrec_ptr; + struct list_struct newrec; + + if (tdb_lock(tdb, -1, F_WRLCK) == -1) + return 0; + + /* Extra bytes required for tailer */ + length += sizeof(tdb_off); + + again: + last_ptr = FREELIST_TOP; + + /* read in the freelist top */ + if (ofs_read(tdb, FREELIST_TOP, &rec_ptr) == -1) + goto fail; + + /* keep looking until we find a freelist record big enough */ + while (rec_ptr) { + if (rec_free_read(tdb, rec_ptr, rec) == -1) + goto fail; + + if (rec->rec_len >= length) { + /* 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; + + /* Remove allocated record from the free list */ + if (ofs_write(tdb, last_ptr, &rec->next) == -1) + goto fail; + + /* 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). */ + rec->magic = TDB_MAGIC; + if (rec_write(tdb, rec_ptr, rec) == -1) + goto fail; + + /* Did we create new block? */ + if (newrec_ptr) { + /* Update allocated record tailer (we + shortened it). */ + if (update_tailer(tdb, rec_ptr, rec) == -1) + goto fail; + + /* Free new record */ + if (tdb_free(tdb, newrec_ptr, &newrec) == -1) + goto fail; + } + + /* all done - return the new record offset */ + tdb_unlock(tdb, -1, F_WRLCK); + return rec_ptr; + } + /* move to the next record */ + last_ptr = rec_ptr; + rec_ptr = rec->next; + } + /* we didn't find enough space. See if we can expand the + database and if we can then try again */ + if (tdb_expand(tdb, length + sizeof(*rec)) == 0) + goto again; + fail: + tdb_unlock(tdb, -1, F_WRLCK); + return 0; +} + +/* initialise a new database with a specified hash size */ +static int tdb_new_database(TDB_CONTEXT *tdb, int hash_size) +{ + struct tdb_header *newdb; + int size, ret = -1; + + /* We make it up in memory, then write it out if not internal */ + size = sizeof(struct tdb_header) + (hash_size+1)*sizeof(tdb_off); + if (!(newdb = calloc(size, 1))) + return TDB_ERRCODE(TDB_ERR_OOM, -1); + + /* Fill in the header */ + newdb->version = TDB_VERSION; + newdb->hash_size = hash_size; +#ifdef USE_SPINLOCKS + newdb->rwlocks = size; +#endif + if (tdb->flags & TDB_INTERNAL) { + tdb->map_size = size; + tdb->map_ptr = (char *)newdb; + memcpy(&tdb->header, newdb, sizeof(tdb->header)); + /* Convert the `ondisk' version if asked. */ + CONVERT(*newdb); + return 0; + } + if (lseek(tdb->fd, 0, SEEK_SET) == -1) + goto fail; + + if (ftruncate(tdb->fd, 0) == -1) + goto fail; + + /* This creates an endian-converted header, as if read from disk */ + CONVERT(*newdb); + memcpy(&tdb->header, newdb, sizeof(tdb->header)); + /* Don't endian-convert the magic food! */ + memcpy(newdb->magic_food, TDB_MAGIC_FOOD, strlen(TDB_MAGIC_FOOD)+1); + if (write(tdb->fd, newdb, size) != size) + ret = -1; + else + ret = tdb_create_rwlocks(tdb->fd, hash_size); + + fail: + SAFE_FREE(newdb); + return ret; +} + +/* Returns 0 on fail. On success, return offset of record, and fills + in rec */ +static tdb_off tdb_find(TDB_CONTEXT *tdb, TDB_DATA key, u32 hash, + struct list_struct *r) +{ + tdb_off rec_ptr; + + /* read in the hash top */ + if (ofs_read(tdb, TDB_HASH_TOP(hash), &rec_ptr) == -1) + return 0; + + /* keep looking until we find the right record */ + while (rec_ptr) { + if (rec_read(tdb, rec_ptr, r) == -1) + return 0; + + if (!TDB_DEAD(r) && hash==r->full_hash && key.dsize==r->key_len) { + char *k; + /* a very likely hit - read the key */ + k = tdb_alloc_read(tdb, rec_ptr + sizeof(*r), + r->key_len); + if (!k) + return 0; + + if (memcmp(key.dptr, k, key.dsize) == 0) { + SAFE_FREE(k); + return rec_ptr; + } + SAFE_FREE(k); + } + rec_ptr = r->next; + } + return TDB_ERRCODE(TDB_ERR_NOEXIST, 0); +} + +/* If they do lockkeys, check that this hash is one they locked */ +static int tdb_keylocked(TDB_CONTEXT *tdb, u32 hash) +{ + u32 i; + if (!tdb->lockedkeys) + return 1; + for (i = 0; i < tdb->lockedkeys[0]; i++) + if (tdb->lockedkeys[i+1] == hash) + return 1; + return TDB_ERRCODE(TDB_ERR_NOLOCK, 0); +} + +/* As tdb_find, but if you succeed, keep the lock */ +static tdb_off tdb_find_lock(TDB_CONTEXT *tdb, TDB_DATA key, int locktype, + struct list_struct *rec) +{ + u32 hash, rec_ptr; + + hash = tdb_hash(&key); + if (!tdb_keylocked(tdb, hash)) + return 0; + if (tdb_lock(tdb, BUCKET(hash), locktype) == -1) + return 0; + if (!(rec_ptr = tdb_find(tdb, key, hash, rec))) + tdb_unlock(tdb, BUCKET(hash), locktype); + return rec_ptr; +} + +enum TDB_ERROR tdb_error(TDB_CONTEXT *tdb) +{ + return tdb->ecode; +} + +static struct tdb_errname { + enum TDB_ERROR ecode; const char *estring; +} emap[] = { {TDB_SUCCESS, "Success"}, + {TDB_ERR_CORRUPT, "Corrupt database"}, + {TDB_ERR_IO, "IO Error"}, + {TDB_ERR_LOCK, "Locking error"}, + {TDB_ERR_OOM, "Out of memory"}, + {TDB_ERR_EXISTS, "Record exists"}, + {TDB_ERR_NOLOCK, "Lock exists on other keys"}, + {TDB_ERR_NOEXIST, "Record does not exist"} }; + +/* Error string for the last tdb error */ +const char *tdb_errorstr(TDB_CONTEXT *tdb) +{ + u32 i; + for (i = 0; i < sizeof(emap) / sizeof(struct tdb_errname); i++) + if (tdb->ecode == emap[i].ecode) + return emap[i].estring; + return "Invalid error code"; +} + +/* update an entry in place - this only works if the new data size + is <= the old data size and the key exists. + on failure return -1. +*/ + +static int tdb_update(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf) +{ + struct list_struct rec; + tdb_off rec_ptr; + + /* find entry */ + if (!(rec_ptr = tdb_find(tdb, key, tdb_hash(&key), &rec))) + return -1; + + /* must be long enough key, data and tailer */ + if (rec.rec_len < key.dsize + dbuf.dsize + sizeof(tdb_off)) { + tdb->ecode = TDB_SUCCESS; /* Not really an error */ + return -1; + } + + if (tdb_write(tdb, rec_ptr + sizeof(rec) + rec.key_len, + dbuf.dptr, dbuf.dsize) == -1) + return -1; + + if (dbuf.dsize != rec.data_len) { + /* update size */ + rec.data_len = dbuf.dsize; + return rec_write(tdb, rec_ptr, &rec); + } + + return 0; +} + +/* find an entry in the database given a key */ +/* If an entry doesn't exist tdb_err will be set to + * TDB_ERR_NOEXIST. If a key has no data attached + * tdb_err will not be set. Both will return a + * zero pptr and zero dsize. + */ + +TDB_DATA tdb_fetch(TDB_CONTEXT *tdb, TDB_DATA key) +{ + tdb_off rec_ptr; + struct list_struct rec; + TDB_DATA ret; + + /* find which hash bucket it is in */ + if (!(rec_ptr = tdb_find_lock(tdb,key,F_RDLCK,&rec))) + return tdb_null; + + if (rec.data_len) + ret.dptr = tdb_alloc_read(tdb, rec_ptr + sizeof(rec) + rec.key_len, + rec.data_len); + else + ret.dptr = NULL; + ret.dsize = rec.data_len; + tdb_unlock(tdb, BUCKET(rec.full_hash), F_RDLCK); + return ret; +} + +/* check if an entry in the database exists + + note that 1 is returned if the key is found and 0 is returned if not found + this doesn't match the conventions in the rest of this module, but is + compatible with gdbm +*/ +int tdb_exists(TDB_CONTEXT *tdb, TDB_DATA key) +{ + struct list_struct rec; + + if (tdb_find_lock(tdb, key, F_RDLCK, &rec) == 0) + return 0; + tdb_unlock(tdb, BUCKET(rec.full_hash), F_RDLCK); + return 1; +} + +/* record lock stops delete underneath */ +static int lock_record(TDB_CONTEXT *tdb, tdb_off off) +{ + return off ? tdb_brlock(tdb, off, F_RDLCK, F_SETLKW, 0) : 0; +} +/* + Write locks override our own fcntl readlocks, so check it here. + Note this is meant to be F_SETLK, *not* F_SETLKW, as it's not + an error to fail to get the lock here. +*/ + +static int write_lock_record(TDB_CONTEXT *tdb, tdb_off off) +{ + struct tdb_traverse_lock *i; + for (i = &tdb->travlocks; i; i = i->next) + if (i->off == off) + return -1; + return tdb_brlock(tdb, off, F_WRLCK, F_SETLK, 1); +} + +/* + Note this is meant to be F_SETLK, *not* F_SETLKW, as it's not + an error to fail to get the lock here. +*/ + +static int write_unlock_record(TDB_CONTEXT *tdb, tdb_off off) +{ + return tdb_brlock(tdb, off, F_UNLCK, F_SETLK, 0); +} +/* fcntl locks don't stack: avoid unlocking someone else's */ +static int unlock_record(TDB_CONTEXT *tdb, tdb_off off) +{ + struct tdb_traverse_lock *i; + u32 count = 0; + + if (off == 0) + return 0; + for (i = &tdb->travlocks; i; i = i->next) + if (i->off == off) + count++; + return (count == 1 ? tdb_brlock(tdb, off, F_UNLCK, F_SETLKW, 0) : 0); +} + +/* actually delete an entry in the database given the offset */ +static int do_delete(TDB_CONTEXT *tdb, tdb_off rec_ptr, struct list_struct*rec) +{ + tdb_off last_ptr, i; + struct list_struct lastrec; + + if (tdb->read_only) return -1; + + if (write_lock_record(tdb, rec_ptr) == -1) { + /* Someone traversing here: mark it as dead */ + rec->magic = TDB_DEAD_MAGIC; + return rec_write(tdb, rec_ptr, rec); + } + if (write_unlock_record(tdb, rec_ptr) != 0) + return -1; + + /* find previous record in hash chain */ + if (ofs_read(tdb, TDB_HASH_TOP(rec->full_hash), &i) == -1) + return -1; + for (last_ptr = 0; i != rec_ptr; last_ptr = i, i = lastrec.next) + if (rec_read(tdb, i, &lastrec) == -1) + return -1; + + /* unlink it: next ptr is at start of record. */ + if (last_ptr == 0) + last_ptr = TDB_HASH_TOP(rec->full_hash); + if (ofs_write(tdb, last_ptr, &rec->next) == -1) + return -1; + + /* recover the space */ + if (tdb_free(tdb, rec_ptr, rec) == -1) + return -1; + return 0; +} + +/* Uses traverse lock: 0 = finish, -1 = error, other = record offset */ +static int tdb_next_lock(TDB_CONTEXT *tdb, struct tdb_traverse_lock *tlock, + struct list_struct *rec) +{ + int want_next = (tlock->off != 0); + + /* No traversal allows if you've called tdb_lockkeys() */ + if (tdb->lockedkeys) + return TDB_ERRCODE(TDB_ERR_NOLOCK, -1); + + /* Lock each chain from the start one. */ + for (; tlock->hash < tdb->header.hash_size; tlock->hash++) { + if (tdb_lock(tdb, tlock->hash, F_WRLCK) == -1) + return -1; + + /* No previous record? Start at top of chain. */ + if (!tlock->off) { + if (ofs_read(tdb, TDB_HASH_TOP(tlock->hash), + &tlock->off) == -1) + goto fail; + } else { + /* Otherwise unlock the previous record. */ + if (unlock_record(tdb, tlock->off) != 0) + goto fail; + } + + if (want_next) { + /* We have offset of old record: grab next */ + if (rec_read(tdb, tlock->off, rec) == -1) + goto fail; + tlock->off = rec->next; + } + + /* Iterate through chain */ + while( tlock->off) { + tdb_off current; + if (rec_read(tdb, tlock->off, rec) == -1) + goto fail; + if (!TDB_DEAD(rec)) { + /* Woohoo: we found one! */ + if (lock_record(tdb, tlock->off) != 0) + goto fail; + return tlock->off; + } + /* Try to clean dead ones from old traverses */ + current = tlock->off; + tlock->off = rec->next; + if (do_delete(tdb, current, rec) != 0) + goto fail; + } + tdb_unlock(tdb, tlock->hash, F_WRLCK); + want_next = 0; + } + /* We finished iteration without finding anything */ + return TDB_ERRCODE(TDB_SUCCESS, 0); + + fail: + tlock->off = 0; + if (tdb_unlock(tdb, tlock->hash, F_WRLCK) != 0) + TDB_LOG((tdb, 0, "tdb_next_lock: On error unlock failed!\n")); + return -1; +} + +/* traverse the entire database - calling fn(tdb, key, data) on each element. + return -1 on error or the record count traversed + if fn is NULL then it is not called + a non-zero return value from fn() indicates that the traversal should stop + */ +int tdb_traverse(TDB_CONTEXT *tdb, tdb_traverse_func fn, void *state) +{ + TDB_DATA key, dbuf; + struct list_struct rec; + struct tdb_traverse_lock tl = { NULL, 0, 0 }; + int ret, count = 0; + + /* This was in the initializaton, above, but the IRIX compiler + * did not like it. crh + */ + tl.next = tdb->travlocks.next; + + /* fcntl locks don't stack: beware traverse inside traverse */ + tdb->travlocks.next = &tl; + + /* tdb_next_lock places locks on the record returned, and its chain */ + while ((ret = tdb_next_lock(tdb, &tl, &rec)) > 0) { + count++; + /* now read the full record */ + key.dptr = tdb_alloc_read(tdb, tl.off + sizeof(rec), + rec.key_len + rec.data_len); + if (!key.dptr) { + ret = -1; + if (tdb_unlock(tdb, tl.hash, F_WRLCK) != 0) + goto out; + if (unlock_record(tdb, tl.off) != 0) + TDB_LOG((tdb, 0, "tdb_traverse: key.dptr == NULL and unlock_record failed!\n")); + goto out; + } + key.dsize = rec.key_len; + dbuf.dptr = key.dptr + rec.key_len; + dbuf.dsize = rec.data_len; + + /* Drop chain lock, call out */ + if (tdb_unlock(tdb, tl.hash, F_WRLCK) != 0) { + ret = -1; + goto out; + } + if (fn && fn(tdb, key, dbuf, state)) { + /* They want us to terminate traversal */ + ret = count; + if (unlock_record(tdb, tl.off) != 0) { + TDB_LOG((tdb, 0, "tdb_traverse: unlock_record failed!\n"));; + ret = -1; + } + tdb->travlocks.next = tl.next; + SAFE_FREE(key.dptr); + return count; + } + SAFE_FREE(key.dptr); + } +out: + tdb->travlocks.next = tl.next; + if (ret < 0) + return -1; + else + return count; +} + +/* find the first entry in the database and return its key */ +TDB_DATA tdb_firstkey(TDB_CONTEXT *tdb) +{ + TDB_DATA key; + struct list_struct rec; + + /* release any old lock */ + if (unlock_record(tdb, tdb->travlocks.off) != 0) + return tdb_null; + tdb->travlocks.off = tdb->travlocks.hash = 0; + + if (tdb_next_lock(tdb, &tdb->travlocks, &rec) <= 0) + return tdb_null; + /* now read the key */ + key.dsize = rec.key_len; + key.dptr =tdb_alloc_read(tdb,tdb->travlocks.off+sizeof(rec),key.dsize); + if (tdb_unlock(tdb, BUCKET(tdb->travlocks.hash), F_WRLCK) != 0) + TDB_LOG((tdb, 0, "tdb_firstkey: error occurred while tdb_unlocking!\n")); + return key; +} + +/* find the next entry in the database, returning its key */ +TDB_DATA tdb_nextkey(TDB_CONTEXT *tdb, TDB_DATA oldkey) +{ + u32 oldhash; + TDB_DATA key = tdb_null; + struct list_struct rec; + char *k = NULL; + + /* Is locked key the old key? If so, traverse will be reliable. */ + if (tdb->travlocks.off) { + if (tdb_lock(tdb,tdb->travlocks.hash,F_WRLCK)) + return tdb_null; + if (rec_read(tdb, tdb->travlocks.off, &rec) == -1 + || !(k = tdb_alloc_read(tdb,tdb->travlocks.off+sizeof(rec), + rec.key_len)) + || memcmp(k, oldkey.dptr, oldkey.dsize) != 0) { + /* No, it wasn't: unlock it and start from scratch */ + if (unlock_record(tdb, tdb->travlocks.off) != 0) + return tdb_null; + if (tdb_unlock(tdb, tdb->travlocks.hash, F_WRLCK) != 0) + return tdb_null; + tdb->travlocks.off = 0; + } + + SAFE_FREE(k); + } + + if (!tdb->travlocks.off) { + /* No previous element: do normal find, and lock record */ + tdb->travlocks.off = tdb_find_lock(tdb, oldkey, F_WRLCK, &rec); + if (!tdb->travlocks.off) + return tdb_null; + tdb->travlocks.hash = BUCKET(rec.full_hash); + if (lock_record(tdb, tdb->travlocks.off) != 0) { + TDB_LOG((tdb, 0, "tdb_nextkey: lock_record failed (%s)!\n", strerror(errno))); + return tdb_null; + } + } + oldhash = tdb->travlocks.hash; + + /* Grab next record: locks chain and returned record, + unlocks old record */ + if (tdb_next_lock(tdb, &tdb->travlocks, &rec) > 0) { + key.dsize = rec.key_len; + key.dptr = tdb_alloc_read(tdb, tdb->travlocks.off+sizeof(rec), + key.dsize); + /* Unlock the chain of this new record */ + if (tdb_unlock(tdb, tdb->travlocks.hash, F_WRLCK) != 0) + TDB_LOG((tdb, 0, "tdb_nextkey: WARNING tdb_unlock failed!\n")); + } + /* Unlock the chain of old record */ + if (tdb_unlock(tdb, BUCKET(oldhash), F_WRLCK) != 0) + TDB_LOG((tdb, 0, "tdb_nextkey: WARNING tdb_unlock failed!\n")); + return key; +} + +/* delete an entry in the database given a key */ +int tdb_delete(TDB_CONTEXT *tdb, TDB_DATA key) +{ + tdb_off rec_ptr; + struct list_struct rec; + int ret; + + if (!(rec_ptr = tdb_find_lock(tdb, key, F_WRLCK, &rec))) + return -1; + ret = do_delete(tdb, rec_ptr, &rec); + if (tdb_unlock(tdb, BUCKET(rec.full_hash), F_WRLCK) != 0) + TDB_LOG((tdb, 0, "tdb_delete: WARNING tdb_unlock failed!\n")); + return ret; +} + +/* store an element in the database, replacing any existing element + with the same key + + return 0 on success, -1 on failure +*/ +int tdb_store(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, int flag) +{ + struct list_struct rec; + u32 hash; + tdb_off rec_ptr; + char *p = NULL; + int ret = 0; + + /* find which hash bucket it is in */ + hash = tdb_hash(&key); + if (!tdb_keylocked(tdb, hash)) + return -1; + if (tdb_lock(tdb, BUCKET(hash), F_WRLCK) == -1) + return -1; + + /* check for it existing, on insert. */ + if (flag == TDB_INSERT) { + if (tdb_exists(tdb, key)) { + tdb->ecode = TDB_ERR_EXISTS; + goto fail; + } + } else { + /* first try in-place update, on modify or replace. */ + if (tdb_update(tdb, key, dbuf) == 0) + goto out; + if (flag == TDB_MODIFY && tdb->ecode == TDB_ERR_NOEXIST) + goto fail; + } + /* reset the error code potentially set by the tdb_update() */ + tdb->ecode = TDB_SUCCESS; + + /* delete any existing record - if it doesn't exist we don't + care. Doing this first reduces fragmentation, and avoids + coalescing with `allocated' block before it's updated. */ + if (flag != TDB_INSERT) + tdb_delete(tdb, key); + + /* Copy key+value *before* allocating free space in case malloc + fails and we are left with a dead spot in the tdb. */ + + if (!(p = (char *)malloc(key.dsize + dbuf.dsize))) { + tdb->ecode = TDB_ERR_OOM; + goto fail; + } + + memcpy(p, key.dptr, key.dsize); + if (dbuf.dsize) + memcpy(p+key.dsize, dbuf.dptr, dbuf.dsize); + + /* now we're into insert / modify / replace of a record which + * we know could not be optimised by an in-place store (for + * various reasons). */ + if (!(rec_ptr = tdb_allocate(tdb, key.dsize + dbuf.dsize, &rec))) + goto fail; + + /* Read hash top into next ptr */ + if (ofs_read(tdb, TDB_HASH_TOP(hash), &rec.next) == -1) + goto fail; + + rec.key_len = key.dsize; + rec.data_len = dbuf.dsize; + rec.full_hash = hash; + rec.magic = TDB_MAGIC; + + /* write out and point the top of the hash chain at it */ + if (rec_write(tdb, rec_ptr, &rec) == -1 + || tdb_write(tdb, rec_ptr+sizeof(rec), p, key.dsize+dbuf.dsize)==-1 + || ofs_write(tdb, TDB_HASH_TOP(hash), &rec_ptr) == -1) { + /* Need to tdb_unallocate() here */ + goto fail; + } + out: + SAFE_FREE(p); + tdb_unlock(tdb, BUCKET(hash), F_WRLCK); + return ret; +fail: + ret = -1; + goto out; +} + +/* Attempt to append data to an entry in place - this only works if the new data size + is <= the old data size and the key exists. + on failure return -1. Record must be locked before calling. +*/ +static int tdb_append_inplace(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA new_dbuf) +{ + struct list_struct rec; + tdb_off rec_ptr; + + /* find entry */ + if (!(rec_ptr = tdb_find(tdb, key, tdb_hash(&key), &rec))) + return -1; + + /* Append of 0 is always ok. */ + if (new_dbuf.dsize == 0) + return 0; + + /* must be long enough for key, old data + new data and tailer */ + if (rec.rec_len < key.dsize + rec.data_len + new_dbuf.dsize + sizeof(tdb_off)) { + /* No room. */ + tdb->ecode = TDB_SUCCESS; /* Not really an error */ + return -1; + } + + if (tdb_write(tdb, rec_ptr + sizeof(rec) + rec.key_len + rec.data_len, + new_dbuf.dptr, new_dbuf.dsize) == -1) + return -1; + + /* update size */ + rec.data_len += new_dbuf.dsize; + return rec_write(tdb, rec_ptr, &rec); +} + +/* Append to an entry. Create if not exist. */ + +int tdb_append(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA new_dbuf) +{ + struct list_struct rec; + u32 hash; + tdb_off rec_ptr; + char *p = NULL; + int ret = 0; + size_t new_data_size = 0; + + /* find which hash bucket it is in */ + hash = tdb_hash(&key); + if (!tdb_keylocked(tdb, hash)) + return -1; + if (tdb_lock(tdb, BUCKET(hash), F_WRLCK) == -1) + return -1; + + /* first try in-place. */ + if (tdb_append_inplace(tdb, key, new_dbuf) == 0) + goto out; + + /* reset the error code potentially set by the tdb_append_inplace() */ + tdb->ecode = TDB_SUCCESS; + + /* find entry */ + if (!(rec_ptr = tdb_find(tdb, key, hash, &rec))) { + if (tdb->ecode != TDB_ERR_NOEXIST) + goto fail; + + /* Not found - create. */ + + ret = tdb_store(tdb, key, new_dbuf, TDB_INSERT); + goto out; + } + + new_data_size = rec.data_len + new_dbuf.dsize; + + /* Copy key+old_value+value *before* allocating free space in case malloc + fails and we are left with a dead spot in the tdb. */ + + if (!(p = (char *)malloc(key.dsize + new_data_size))) { + tdb->ecode = TDB_ERR_OOM; + goto fail; + } + + /* Copy the key in place. */ + memcpy(p, key.dptr, key.dsize); + + /* Now read the old data into place. */ + if (rec.data_len && + tdb_read(tdb, rec_ptr + sizeof(rec) + rec.key_len, p + key.dsize, rec.data_len, 0) == -1) + goto fail; + + /* Finally append the new data. */ + if (new_dbuf.dsize) + memcpy(p+key.dsize+rec.data_len, new_dbuf.dptr, new_dbuf.dsize); + + /* delete any existing record - if it doesn't exist we don't + care. Doing this first reduces fragmentation, and avoids + coalescing with `allocated' block before it's updated. */ + + tdb_delete(tdb, key); + + if (!(rec_ptr = tdb_allocate(tdb, key.dsize + new_data_size, &rec))) + goto fail; + + /* Read hash top into next ptr */ + if (ofs_read(tdb, TDB_HASH_TOP(hash), &rec.next) == -1) + goto fail; + + rec.key_len = key.dsize; + rec.data_len = new_data_size; + rec.full_hash = hash; + rec.magic = TDB_MAGIC; + + /* write out and point the top of the hash chain at it */ + if (rec_write(tdb, rec_ptr, &rec) == -1 + || tdb_write(tdb, rec_ptr+sizeof(rec), p, key.dsize+new_data_size)==-1 + || ofs_write(tdb, TDB_HASH_TOP(hash), &rec_ptr) == -1) { + /* Need to tdb_unallocate() here */ + goto fail; + } + + out: + SAFE_FREE(p); + tdb_unlock(tdb, BUCKET(hash), F_WRLCK); + return ret; + +fail: + ret = -1; + goto out; +} + +static int tdb_already_open(dev_t device, + ino_t ino) +{ + TDB_CONTEXT *i; + + for (i = tdbs; i; i = i->next) { + if (i->device == device && i->inode == ino) { + return 1; + } + } + + return 0; +} + +/* open the database, creating it if necessary + + The open_flags and mode are passed straight to the open call on the + database file. A flags value of O_WRONLY is invalid. The hash size + is advisory, use zero for a default value. + + Return is NULL on error, in which case errno is also set. Don't + try to call tdb_error or tdb_errname, just do strerror(errno). + + @param name may be NULL for internal databases. */ +TDB_CONTEXT *tdb_open(const char *name, int hash_size, int tdb_flags, + int open_flags, mode_t mode) +{ + return tdb_open_ex(name, hash_size, tdb_flags, open_flags, mode, NULL); +} + + +TDB_CONTEXT *tdb_open_ex(const char *name, int hash_size, int tdb_flags, + int open_flags, mode_t mode, + tdb_log_func log_fn) +{ + TDB_CONTEXT *tdb; + struct stat st; + int rev = 0, locked; + unsigned char *vp; + u32 vertest; + + if (!(tdb = calloc(1, sizeof *tdb))) { + /* Can't log this */ + errno = ENOMEM; + goto fail; + } + tdb->fd = -1; + tdb->name = NULL; + tdb->map_ptr = NULL; + tdb->lockedkeys = NULL; + tdb->flags = tdb_flags; + tdb->open_flags = open_flags; + tdb->log_fn = log_fn; + + if ((open_flags & O_ACCMODE) == O_WRONLY) { + TDB_LOG((tdb, 0, "tdb_open_ex: can't open tdb %s write-only\n", + name)); + errno = EINVAL; + goto fail; + } + + if (hash_size == 0) + hash_size = DEFAULT_HASH_SIZE; + if ((open_flags & O_ACCMODE) == O_RDONLY) { + tdb->read_only = 1; + /* read only databases don't do locking or clear if first */ + tdb->flags |= TDB_NOLOCK; + tdb->flags &= ~TDB_CLEAR_IF_FIRST; + } + + /* internal databases don't mmap or lock, and start off cleared */ + if (tdb->flags & TDB_INTERNAL) { + tdb->flags |= (TDB_NOLOCK | TDB_NOMMAP); + tdb->flags &= ~TDB_CLEAR_IF_FIRST; + if (tdb_new_database(tdb, hash_size) != 0) { + TDB_LOG((tdb, 0, "tdb_open_ex: tdb_new_database failed!")); + goto fail; + } + goto internal; + } + + if ((tdb->fd = open(name, open_flags, mode)) == -1) { + TDB_LOG((tdb, 5, "tdb_open_ex: could not open file %s: %s\n", + name, strerror(errno))); + goto fail; /* errno set by open(2) */ + } + + /* ensure there is only one process initialising at once */ + if (tdb_brlock(tdb, GLOBAL_LOCK, F_WRLCK, F_SETLKW, 0) == -1) { + TDB_LOG((tdb, 0, "tdb_open_ex: failed to get global lock on %s: %s\n", + name, strerror(errno))); + goto fail; /* errno set by tdb_brlock */ + } + + /* we need to zero database if we are the only one with it open */ + if ((locked = (tdb_brlock(tdb, ACTIVE_LOCK, F_WRLCK, F_SETLK, 0) == 0)) + && (tdb_flags & TDB_CLEAR_IF_FIRST)) { + open_flags |= O_CREAT; + if (ftruncate(tdb->fd, 0) == -1) { + TDB_LOG((tdb, 0, "tdb_open_ex: " + "failed to truncate %s: %s\n", + name, strerror(errno))); + goto fail; /* errno set by ftruncate */ + } + } + + if (read(tdb->fd, &tdb->header, sizeof(tdb->header)) != sizeof(tdb->header) + || strcmp(tdb->header.magic_food, TDB_MAGIC_FOOD) != 0 + || tdb->header.version != TDB_VERSION + || (tdb->header.hash_size != hash_size + && !(rev = (tdb->header.version==TDB_BYTEREV(TDB_VERSION))))) { + /* its not a valid database - possibly initialise it */ + if (!(open_flags & O_CREAT) || tdb_new_database(tdb, hash_size) == -1) { + errno = EIO; /* ie bad format or something */ + goto fail; + } + rev = (tdb->flags & TDB_CONVERT); + } + vp = (unsigned char *)&tdb->header.version; + vertest = (((u32)vp[0]) << 24) | (((u32)vp[1]) << 16) | + (((u32)vp[2]) << 8) | (u32)vp[3]; + tdb->flags |= (vertest==TDB_VERSION) ? TDB_BIGENDIAN : 0; + if (!rev) + tdb->flags &= ~TDB_CONVERT; + else { + tdb->flags |= TDB_CONVERT; + convert(&tdb->header, sizeof(tdb->header)); + } + if (fstat(tdb->fd, &st) == -1) + goto fail; + + /* Is it already in the open list? If so, fail. */ + if (tdb_already_open(st.st_dev, st.st_ino)) { + TDB_LOG((tdb, 2, "tdb_open_ex: " + "%s (%d,%d) is already open in this process\n", + name, st.st_dev, st.st_ino)); + errno = EBUSY; + goto fail; + } + + if (!(tdb->name = (char *)strdup(name))) { + errno = ENOMEM; + goto fail; + } + + tdb->map_size = st.st_size; + tdb->device = st.st_dev; + tdb->inode = st.st_ino; + tdb->locked = calloc(tdb->header.hash_size+1, sizeof(tdb->locked[0])); + if (!tdb->locked) { + TDB_LOG((tdb, 2, "tdb_open_ex: " + "failed to allocate lock structure for %s\n", + name)); + errno = ENOMEM; + goto fail; + } + tdb_mmap(tdb); + if (locked) { + if (!tdb->read_only) + if (tdb_clear_spinlocks(tdb) != 0) { + TDB_LOG((tdb, 0, "tdb_open_ex: " + "failed to clear spinlock\n")); + goto fail; + } + if (tdb_brlock(tdb, ACTIVE_LOCK, F_UNLCK, F_SETLK, 0) == -1) { + TDB_LOG((tdb, 0, "tdb_open_ex: " + "failed to take ACTIVE_LOCK on %s: %s\n", + name, strerror(errno))); + goto fail; + } + } + /* leave this lock in place to indicate it's in use */ + if (tdb_brlock(tdb, ACTIVE_LOCK, F_RDLCK, F_SETLKW, 0) == -1) + goto fail; + + internal: + /* Internal (memory-only) databases skip all the code above to + * do with disk files, and resume here by releasing their + * global lock and hooking into the active list. */ + if (tdb_brlock(tdb, GLOBAL_LOCK, F_UNLCK, F_SETLKW, 0) == -1) + goto fail; + tdb->next = tdbs; + tdbs = tdb; + return tdb; + + fail: + { int save_errno = errno; + + if (!tdb) + return NULL; + + if (tdb->map_ptr) { + if (tdb->flags & TDB_INTERNAL) + SAFE_FREE(tdb->map_ptr); + else + tdb_munmap(tdb); + } + SAFE_FREE(tdb->name); + if (tdb->fd != -1) + if (close(tdb->fd) != 0) + TDB_LOG((tdb, 5, "tdb_open_ex: failed to close tdb->fd on error!\n")); + SAFE_FREE(tdb->locked); + SAFE_FREE(tdb); + errno = save_errno; + return NULL; + } +} + +/** + * Close a database. + * + * @returns -1 for error; 0 for success. + **/ +int tdb_close(TDB_CONTEXT *tdb) +{ + TDB_CONTEXT **i; + int ret = 0; + + if (tdb->map_ptr) { + if (tdb->flags & TDB_INTERNAL) + SAFE_FREE(tdb->map_ptr); + else + tdb_munmap(tdb); + } + SAFE_FREE(tdb->name); + if (tdb->fd != -1) + ret = close(tdb->fd); + SAFE_FREE(tdb->locked); + SAFE_FREE(tdb->lockedkeys); + + /* Remove from contexts list */ + for (i = &tdbs; *i; i = &(*i)->next) { + if (*i == tdb) { + *i = tdb->next; + break; + } + } + + memset(tdb, 0, sizeof(*tdb)); + SAFE_FREE(tdb); + + return ret; +} + +/* lock/unlock entire database */ +int tdb_lockall(TDB_CONTEXT *tdb) +{ + u32 i; + + /* There are no locks on read-only dbs */ + if (tdb->read_only) + return TDB_ERRCODE(TDB_ERR_LOCK, -1); + if (tdb->lockedkeys) + return TDB_ERRCODE(TDB_ERR_NOLOCK, -1); + for (i = 0; i < tdb->header.hash_size; i++) + if (tdb_lock(tdb, i, F_WRLCK)) + break; + + /* If error, release locks we have... */ + if (i < tdb->header.hash_size) { + u32 j; + + for ( j = 0; j < i; j++) + tdb_unlock(tdb, j, F_WRLCK); + return TDB_ERRCODE(TDB_ERR_NOLOCK, -1); + } + + return 0; +} +void tdb_unlockall(TDB_CONTEXT *tdb) +{ + u32 i; + for (i=0; i < tdb->header.hash_size; i++) + tdb_unlock(tdb, i, F_WRLCK); +} + +int tdb_lockkeys(TDB_CONTEXT *tdb, u32 number, TDB_DATA keys[]) +{ + u32 i, j, hash; + + /* Can't lock more keys if already locked */ + if (tdb->lockedkeys) + return TDB_ERRCODE(TDB_ERR_NOLOCK, -1); + if (!(tdb->lockedkeys = malloc(sizeof(u32) * (number+1)))) + return TDB_ERRCODE(TDB_ERR_OOM, -1); + /* First number in array is # keys */ + tdb->lockedkeys[0] = number; + + /* Insertion sort by bucket */ + for (i = 0; i < number; i++) { + hash = tdb_hash(&keys[i]); + for (j = 0; j < i && BUCKET(tdb->lockedkeys[j+1]) < BUCKET(hash); j++); + memmove(&tdb->lockedkeys[j+2], &tdb->lockedkeys[j+1], sizeof(u32) * (i-j)); + tdb->lockedkeys[j+1] = hash; + } + /* Finally, lock in order */ + for (i = 0; i < number; i++) + if (tdb_lock(tdb, i, F_WRLCK)) + break; + + /* If error, release locks we have... */ + if (i < number) { + for ( j = 0; j < i; j++) + tdb_unlock(tdb, j, F_WRLCK); + SAFE_FREE(tdb->lockedkeys); + return TDB_ERRCODE(TDB_ERR_NOLOCK, -1); + } + return 0; +} + +/* Unlock the keys previously locked by tdb_lockkeys() */ +void tdb_unlockkeys(TDB_CONTEXT *tdb) +{ + u32 i; + for (i = 0; i < tdb->lockedkeys[0]; i++) + tdb_unlock(tdb, tdb->lockedkeys[i+1], F_WRLCK); + SAFE_FREE(tdb->lockedkeys); +} + +/* lock/unlock one hash chain. This is meant to be used to reduce + contention - it cannot guarantee how many records will be locked */ +int tdb_chainlock(TDB_CONTEXT *tdb, TDB_DATA key) +{ + return tdb_lock(tdb, BUCKET(tdb_hash(&key)), F_WRLCK); +} + +int tdb_chainunlock(TDB_CONTEXT *tdb, TDB_DATA key) +{ + return tdb_unlock(tdb, BUCKET(tdb_hash(&key)), F_WRLCK); +} + +int tdb_chainlock_read(TDB_CONTEXT *tdb, TDB_DATA key) +{ + return tdb_lock(tdb, BUCKET(tdb_hash(&key)), F_RDLCK); +} + +int tdb_chainunlock_read(TDB_CONTEXT *tdb, TDB_DATA key) +{ + return tdb_unlock(tdb, BUCKET(tdb_hash(&key)), F_RDLCK); +} + + +/* register a loging function */ +void tdb_logging_function(TDB_CONTEXT *tdb, void (*fn)(TDB_CONTEXT *, int , const char *, ...)) +{ + tdb->log_fn = fn; +} + + +/* reopen a tdb - this is used after a fork to ensure that we have an independent + seek pointer from our parent and to re-establish locks */ +int tdb_reopen(TDB_CONTEXT *tdb) +{ + struct stat st; + + if (tdb_munmap(tdb) != 0) { + TDB_LOG((tdb, 0, "tdb_reopen: munmap failed (%s)\n", strerror(errno))); + goto fail; + } + if (close(tdb->fd) != 0) + TDB_LOG((tdb, 0, "tdb_reopen: WARNING closing tdb->fd failed!\n")); + tdb->fd = open(tdb->name, tdb->open_flags & ~(O_CREAT|O_TRUNC), 0); + if (tdb->fd == -1) { + TDB_LOG((tdb, 0, "tdb_reopen: open failed (%s)\n", strerror(errno))); + goto fail; + } + if (fstat(tdb->fd, &st) != 0) { + TDB_LOG((tdb, 0, "tdb_reopen: fstat failed (%s)\n", strerror(errno))); + goto fail; + } + if (st.st_ino != tdb->inode || st.st_dev != tdb->device) { + TDB_LOG((tdb, 0, "tdb_reopen: file dev/inode has changed!\n")); + goto fail; + } + tdb_mmap(tdb); + if (tdb_brlock(tdb, ACTIVE_LOCK, F_RDLCK, F_SETLKW, 0) == -1) { + TDB_LOG((tdb, 0, "tdb_reopen: failed to obtain active lock\n")); + goto fail; + } + + return 0; + +fail: + tdb_close(tdb); + return -1; +} + +/* reopen all tdb's */ +int tdb_reopen_all(void) +{ + TDB_CONTEXT *tdb; + + for (tdb=tdbs; tdb; tdb = tdb->next) { + if (tdb_reopen(tdb) != 0) return -1; + } + + return 0; +} diff --git a/source4/lib/tdb/tdb.h b/source4/lib/tdb/tdb.h new file mode 100644 index 0000000000..6f3b1ff756 --- /dev/null +++ b/source4/lib/tdb/tdb.h @@ -0,0 +1,144 @@ +#ifndef __TDB_H__ +#define __TDB_H__ + +/* + Unix SMB/CIFS implementation. + Samba database functions + Copyright (C) Andrew Tridgell 1999 + + 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. +*/ + +#ifdef __cplusplus +extern "C" { +#endif + + +/* flags to tdb_store() */ +#define TDB_REPLACE 1 +#define TDB_INSERT 2 +#define TDB_MODIFY 3 + +/* flags for tdb_open() */ +#define TDB_DEFAULT 0 /* just a readability place holder */ +#define TDB_CLEAR_IF_FIRST 1 +#define TDB_INTERNAL 2 /* don't store on disk */ +#define TDB_NOLOCK 4 /* don't do any locking */ +#define TDB_NOMMAP 8 /* don't use mmap */ +#define TDB_CONVERT 16 /* convert endian (internal use) */ +#define TDB_BIGENDIAN 32 /* header is big-endian (internal use) */ + +#define TDB_ERRCODE(code, ret) ((tdb->ecode = (code)), ret) + +/* error codes */ +enum TDB_ERROR {TDB_SUCCESS=0, TDB_ERR_CORRUPT, TDB_ERR_IO, TDB_ERR_LOCK, + TDB_ERR_OOM, TDB_ERR_EXISTS, TDB_ERR_NOEXIST, TDB_ERR_NOLOCK, TDB_ERR_LOCK_TIMEOUT }; + +#ifndef u32 +#define u32 unsigned +#endif + +typedef struct { + char *dptr; + size_t dsize; +} TDB_DATA; + +typedef u32 tdb_len; +typedef u32 tdb_off; + +/* this is stored at the front of every database */ +struct tdb_header { + char magic_food[32]; /* for /etc/magic */ + u32 version; /* version of the code */ + u32 hash_size; /* number of hash entries */ + tdb_off rwlocks; + tdb_off reserved[31]; +}; + +struct tdb_lock_type { + u32 count; + u32 ltype; +}; + +struct tdb_traverse_lock { + struct tdb_traverse_lock *next; + u32 off; + u32 hash; +}; + +/* this is the context structure that is returned from a db open */ +typedef struct tdb_context { + char *name; /* the name of the database */ + void *map_ptr; /* where it is currently mapped */ + int fd; /* open file descriptor for the database */ + tdb_len map_size; /* how much space has been mapped */ + int read_only; /* opened read-only */ + struct tdb_lock_type *locked; /* array of chain locks */ + enum TDB_ERROR ecode; /* error code for last tdb error */ + struct tdb_header header; /* a cached copy of the header */ + u32 flags; /* the flags passed to tdb_open */ + u32 *lockedkeys; /* array of locked keys: first is #keys */ + struct tdb_traverse_lock travlocks; /* current traversal locks */ + struct tdb_context *next; /* all tdbs to avoid multiple opens */ + dev_t device; /* uniquely identifies this tdb */ + ino_t inode; /* uniquely identifies this tdb */ + void (*log_fn)(struct tdb_context *tdb, int level, const char *, ...); /* logging function */ + int open_flags; /* flags used in the open - needed by reopen */ +} TDB_CONTEXT; + +typedef int (*tdb_traverse_func)(TDB_CONTEXT *, TDB_DATA, TDB_DATA, void *); +typedef void (*tdb_log_func)(TDB_CONTEXT *, int , const char *, ...); + +TDB_CONTEXT *tdb_open(const char *name, int hash_size, int tdb_flags, + int open_flags, mode_t mode); +TDB_CONTEXT *tdb_open_ex(const char *name, int hash_size, int tdb_flags, + int open_flags, mode_t mode, + tdb_log_func log_fn); + +int tdb_reopen(TDB_CONTEXT *tdb); +int tdb_reopen_all(void); +void tdb_logging_function(TDB_CONTEXT *tdb, tdb_log_func); +enum TDB_ERROR tdb_error(TDB_CONTEXT *tdb); +const char *tdb_errorstr(TDB_CONTEXT *tdb); +TDB_DATA tdb_fetch(TDB_CONTEXT *tdb, TDB_DATA key); +int tdb_delete(TDB_CONTEXT *tdb, TDB_DATA key); +int tdb_store(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, int flag); +int tdb_append(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA new_dbuf); +int tdb_close(TDB_CONTEXT *tdb); +TDB_DATA tdb_firstkey(TDB_CONTEXT *tdb); +TDB_DATA tdb_nextkey(TDB_CONTEXT *tdb, TDB_DATA key); +int tdb_traverse(TDB_CONTEXT *tdb, tdb_traverse_func fn, void *state); +int tdb_exists(TDB_CONTEXT *tdb, TDB_DATA key); +int tdb_lockkeys(TDB_CONTEXT *tdb, u32 number, TDB_DATA keys[]); +void tdb_unlockkeys(TDB_CONTEXT *tdb); +int tdb_lockall(TDB_CONTEXT *tdb); +void tdb_unlockall(TDB_CONTEXT *tdb); + +/* Low level locking functions: use with care */ +void tdb_set_lock_alarm(sig_atomic_t *palarm); +int tdb_chainlock(TDB_CONTEXT *tdb, TDB_DATA key); +int tdb_chainunlock(TDB_CONTEXT *tdb, TDB_DATA key); + +/* Debug functions. Not used in production. */ +void tdb_dump_all(TDB_CONTEXT *tdb); +int tdb_printfreelist(TDB_CONTEXT *tdb); + +extern TDB_DATA tdb_null; + +#ifdef __cplusplus +} +#endif + +#endif /* tdb.h */ diff --git a/source4/lib/tdb/tdb.magic b/source4/lib/tdb/tdb.magic new file mode 100644 index 0000000000..f5619e7327 --- /dev/null +++ b/source4/lib/tdb/tdb.magic @@ -0,0 +1,10 @@ +# Magic file(1) information about tdb files. +# +# Install this into /etc/magic or the corresponding location for your +# system, or pass as a -m argument to file(1). + +# You may use and redistribute this file without restriction. + +0 string TDB\ file TDB database +>32 lelong =0x2601196D version 6, little-endian +>>36 lelong x hash size %d bytes diff --git a/source4/lib/tdb/tdbutil.c b/source4/lib/tdb/tdbutil.c new file mode 100644 index 0000000000..0d8f6128cc --- /dev/null +++ b/source4/lib/tdb/tdbutil.c @@ -0,0 +1,687 @@ +/* + Unix SMB/CIFS implementation. + tdb utility functions + Copyright (C) Andrew Tridgell 1992-1998 + + 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" +#include <fnmatch.h> + +/* these are little tdb utility functions that are meant to make + dealing with a tdb database a little less cumbersome in Samba */ + +static SIG_ATOMIC_T gotalarm; + +/*************************************************************** + Signal function to tell us we timed out. +****************************************************************/ + +static void gotalarm_sig(void) +{ + gotalarm = 1; +} + +/*************************************************************** + Make a TDB_DATA and keep the const warning in one place +****************************************************************/ + +static TDB_DATA make_tdb_data(const char *dptr, size_t dsize) +{ + TDB_DATA ret; + ret.dptr = dptr; + ret.dsize = dsize; + return ret; +} + +/**************************************************************************** + Lock a chain with timeout (in seconds). +****************************************************************************/ + +static int tdb_chainlock_with_timeout_internal( TDB_CONTEXT *tdb, TDB_DATA key, unsigned int timeout, int rw_type) +{ + /* Allow tdb_chainlock to be interrupted by an alarm. */ + int ret; + gotalarm = 0; + tdb_set_lock_alarm(&gotalarm); + + if (timeout) { + CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig); + alarm(timeout); + } + + if (rw_type == F_RDLCK) + ret = tdb_chainlock_read(tdb, key); + else + ret = tdb_chainlock(tdb, key); + + if (timeout) { + alarm(0); + CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN); + if (gotalarm) { + DEBUG(0,("tdb_chainlock_with_timeout_internal: alarm (%u) timed out for key %s in tdb %s\n", + timeout, key.dptr, tdb->name )); + /* TODO: If we time out waiting for a lock, it might + * be nice to use F_GETLK to get the pid of the + * process currently holding the lock and print that + * as part of the debugging message. -- mbp */ + return -1; + } + } + + return ret; +} + +/**************************************************************************** + Write lock a chain. Return -1 if timeout or lock failed. +****************************************************************************/ + +int tdb_chainlock_with_timeout( TDB_CONTEXT *tdb, TDB_DATA key, unsigned int timeout) +{ + return tdb_chainlock_with_timeout_internal(tdb, key, timeout, F_WRLCK); +} + +/**************************************************************************** + Lock a chain by string. Return -1 if timeout or lock failed. +****************************************************************************/ + +int tdb_lock_bystring(TDB_CONTEXT *tdb, const char *keyval, unsigned int timeout) +{ + TDB_DATA key = make_tdb_data(keyval, strlen(keyval)+1); + + return tdb_chainlock_with_timeout_internal(tdb, key, timeout, F_WRLCK); +} + +/**************************************************************************** + Unlock a chain by string. +****************************************************************************/ + +void tdb_unlock_bystring(TDB_CONTEXT *tdb, const char *keyval) +{ + TDB_DATA key = make_tdb_data(keyval, strlen(keyval)+1); + + tdb_chainunlock(tdb, key); +} + +/**************************************************************************** + Read lock a chain by string. Return -1 if timeout or lock failed. +****************************************************************************/ + +int tdb_read_lock_bystring(TDB_CONTEXT *tdb, const char *keyval, unsigned int timeout) +{ + TDB_DATA key = make_tdb_data(keyval, strlen(keyval)+1); + + return tdb_chainlock_with_timeout_internal(tdb, key, timeout, F_RDLCK); +} + +/**************************************************************************** + Read unlock a chain by string. +****************************************************************************/ + +void tdb_read_unlock_bystring(TDB_CONTEXT *tdb, const char *keyval) +{ + TDB_DATA key = make_tdb_data(keyval, strlen(keyval)+1); + + tdb_chainunlock_read(tdb, key); +} + + +/**************************************************************************** + Fetch a int32 value by a arbitrary blob key, return -1 if not found. + Output is int32 in native byte order. +****************************************************************************/ + +int32 tdb_fetch_int32_byblob(TDB_CONTEXT *tdb, const char *keyval, size_t len) +{ + TDB_DATA key = make_tdb_data(keyval, len); + TDB_DATA data; + int32 ret; + + data = tdb_fetch(tdb, key); + if (!data.dptr || data.dsize != sizeof(int32)) { + SAFE_FREE(data.dptr); + return -1; + } + + ret = IVAL(data.dptr,0); + SAFE_FREE(data.dptr); + return ret; +} + +/**************************************************************************** + Fetch a int32 value by string key, return -1 if not found. + Output is int32 in native byte order. +****************************************************************************/ + +int32 tdb_fetch_int32(TDB_CONTEXT *tdb, const char *keystr) +{ + return tdb_fetch_int32_byblob(tdb, keystr, strlen(keystr) + 1); +} + +/**************************************************************************** + Store a int32 value by an arbitary blob key, return 0 on success, -1 on failure. + Input is int32 in native byte order. Output in tdb is in little-endian. +****************************************************************************/ + +int tdb_store_int32_byblob(TDB_CONTEXT *tdb, const char *keystr, size_t len, int32 v) +{ + TDB_DATA key = make_tdb_data(keystr, len); + TDB_DATA data; + int32 v_store; + + SIVAL(&v_store,0,v); + data.dptr = (void *)&v_store; + data.dsize = sizeof(int32); + + return tdb_store(tdb, key, data, TDB_REPLACE); +} + +/**************************************************************************** + Store a int32 value by string key, return 0 on success, -1 on failure. + Input is int32 in native byte order. Output in tdb is in little-endian. +****************************************************************************/ + +int tdb_store_int32(TDB_CONTEXT *tdb, const char *keystr, int32 v) +{ + return tdb_store_int32_byblob(tdb, keystr, strlen(keystr) + 1, v); +} + +/**************************************************************************** + Fetch a uint32 value by a arbitrary blob key, return -1 if not found. + Output is uint32 in native byte order. +****************************************************************************/ + +BOOL tdb_fetch_uint32_byblob(TDB_CONTEXT *tdb, const char *keyval, size_t len, uint32 *value) +{ + TDB_DATA key = make_tdb_data(keyval, len); + TDB_DATA data; + + data = tdb_fetch(tdb, key); + if (!data.dptr || data.dsize != sizeof(uint32)) { + SAFE_FREE(data.dptr); + return False; + } + + *value = IVAL(data.dptr,0); + SAFE_FREE(data.dptr); + return True; +} + +/**************************************************************************** + Fetch a uint32 value by string key, return -1 if not found. + Output is uint32 in native byte order. +****************************************************************************/ + +BOOL tdb_fetch_uint32(TDB_CONTEXT *tdb, const char *keystr, uint32 *value) +{ + return tdb_fetch_uint32_byblob(tdb, keystr, strlen(keystr) + 1, value); +} + +/**************************************************************************** + Store a uint32 value by an arbitary blob key, return 0 on success, -1 on failure. + Input is uint32 in native byte order. Output in tdb is in little-endian. +****************************************************************************/ + +BOOL tdb_store_uint32_byblob(TDB_CONTEXT *tdb, const char *keystr, size_t len, uint32 value) +{ + TDB_DATA key = make_tdb_data(keystr, len); + TDB_DATA data; + uint32 v_store; + BOOL ret = True; + + SIVAL(&v_store, 0, value); + data.dptr = (void *)&v_store; + data.dsize = sizeof(uint32); + + if (tdb_store(tdb, key, data, TDB_REPLACE) == -1) + ret = False; + + return ret; +} + +/**************************************************************************** + Store a uint32 value by string key, return 0 on success, -1 on failure. + Input is uint32 in native byte order. Output in tdb is in little-endian. +****************************************************************************/ + +BOOL tdb_store_uint32(TDB_CONTEXT *tdb, const char *keystr, uint32 value) +{ + return tdb_store_uint32_byblob(tdb, keystr, strlen(keystr) + 1, value); +} +/**************************************************************************** + Store a buffer by a null terminated string key. Return 0 on success, -1 + on failure. +****************************************************************************/ + +int tdb_store_by_string(TDB_CONTEXT *tdb, const char *keystr, TDB_DATA data, int flags) +{ + TDB_DATA key = make_tdb_data(keystr, strlen(keystr)+1); + + return tdb_store(tdb, key, data, flags); +} + +/**************************************************************************** + Fetch a buffer using a null terminated string key. Don't forget to call + free() on the result dptr. +****************************************************************************/ + +TDB_DATA tdb_fetch_by_string(TDB_CONTEXT *tdb, const char *keystr) +{ + TDB_DATA key = make_tdb_data(keystr, strlen(keystr)+1); + + return tdb_fetch(tdb, key); +} + +/**************************************************************************** + Delete an entry using a null terminated string key. +****************************************************************************/ + +int tdb_delete_by_string(TDB_CONTEXT *tdb, const char *keystr) +{ + TDB_DATA key = make_tdb_data(keystr, strlen(keystr)+1); + + return tdb_delete(tdb, key); +} + +/**************************************************************************** + Atomic integer change. Returns old value. To create, set initial value in *oldval. +****************************************************************************/ + +int32 tdb_change_int32_atomic(TDB_CONTEXT *tdb, const char *keystr, int32 *oldval, int32 change_val) +{ + int32 val; + int32 ret = -1; + + if (tdb_lock_bystring(tdb, keystr,0) == -1) + return -1; + + if ((val = tdb_fetch_int32(tdb, keystr)) == -1) { + /* The lookup failed */ + if (tdb_error(tdb) != TDB_ERR_NOEXIST) { + /* but not becouse it didn't exist */ + goto err_out; + } + + /* Start with 'old' value */ + val = *oldval; + + } else { + /* It worked, set return value (oldval) to tdb data */ + *oldval = val; + } + + /* Increment value for storage and return next time */ + val += change_val; + + if (tdb_store_int32(tdb, keystr, val) == -1) + goto err_out; + + ret = 0; + + err_out: + + tdb_unlock_bystring(tdb, keystr); + return ret; +} + +/**************************************************************************** + Atomic unsigned integer change. Returns old value. To create, set initial value in *oldval. +****************************************************************************/ + +BOOL tdb_change_uint32_atomic(TDB_CONTEXT *tdb, const char *keystr, uint32 *oldval, uint32 change_val) +{ + uint32 val; + BOOL ret = False; + + if (tdb_lock_bystring(tdb, keystr,0) == -1) + return False; + + if (!tdb_fetch_uint32(tdb, keystr, &val)) { + /* It failed */ + if (tdb_error(tdb) != TDB_ERR_NOEXIST) { + /* and not becouse it didn't exist */ + goto err_out; + } + + /* Start with 'old' value */ + val = *oldval; + + } else { + /* it worked, set return value (oldval) to tdb data */ + *oldval = val; + + } + + /* get a new value to store */ + val += change_val; + + if (!tdb_store_uint32(tdb, keystr, val)) + goto err_out; + + ret = True; + + err_out: + + tdb_unlock_bystring(tdb, keystr); + return ret; +} + +/**************************************************************************** + Useful pair of routines for packing/unpacking data consisting of + integers and strings. +****************************************************************************/ + +size_t tdb_pack(char *buf, int bufsize, const char *fmt, ...) +{ + va_list ap; + uint16 w; + uint32 d; + int i; + void *p; + int len; + char *s; + char c; + char *buf0 = buf; + const char *fmt0 = fmt; + int bufsize0 = bufsize; + + va_start(ap, fmt); + + while (*fmt) { + switch ((c = *fmt++)) { + case 'w': + len = 2; + w = (uint16)va_arg(ap, int); + if (bufsize >= len) + SSVAL(buf, 0, w); + break; + case 'd': + len = 4; + d = va_arg(ap, uint32); + if (bufsize >= len) + SIVAL(buf, 0, d); + break; + case 'p': + len = 4; + p = va_arg(ap, void *); + d = p?1:0; + if (bufsize >= len) + SIVAL(buf, 0, d); + break; + case 'P': + s = va_arg(ap,char *); + w = strlen(s); + len = w + 1; + if (bufsize >= len) + memcpy(buf, s, len); + break; + case 'f': + s = va_arg(ap,char *); + w = strlen(s); + len = w + 1; + if (bufsize >= len) + memcpy(buf, s, len); + break; + case 'B': + i = va_arg(ap, int); + s = va_arg(ap, char *); + len = 4+i; + if (bufsize >= len) { + SIVAL(buf, 0, i); + memcpy(buf+4, s, i); + } + break; + default: + DEBUG(0,("Unknown tdb_pack format %c in %s\n", + c, fmt)); + len = 0; + break; + } + + buf += len; + bufsize -= len; + } + + va_end(ap); + + DEBUG(18,("tdb_pack(%s, %d) -> %d\n", + fmt0, bufsize0, (int)PTR_DIFF(buf, buf0))); + + return PTR_DIFF(buf, buf0); +} + +/**************************************************************************** + Useful pair of routines for packing/unpacking data consisting of + integers and strings. +****************************************************************************/ + +int tdb_unpack(char *buf, int bufsize, const char *fmt, ...) +{ + va_list ap; + uint16 *w; + uint32 *d; + int len; + int *i; + void **p; + char *s, **b; + char c; + char *buf0 = buf; + const char *fmt0 = fmt; + int bufsize0 = bufsize; + + va_start(ap, fmt); + + while (*fmt) { + switch ((c=*fmt++)) { + case 'w': + len = 2; + w = va_arg(ap, uint16 *); + if (bufsize < len) + goto no_space; + *w = SVAL(buf, 0); + break; + case 'd': + len = 4; + d = va_arg(ap, uint32 *); + if (bufsize < len) + goto no_space; + *d = IVAL(buf, 0); + break; + case 'p': + len = 4; + p = va_arg(ap, void **); + if (bufsize < len) + goto no_space; + *p = (void *)IVAL(buf, 0); + break; + case 'P': + s = va_arg(ap,char *); + len = strlen(buf) + 1; + if (bufsize < len || len > sizeof(pstring)) + goto no_space; + memcpy(s, buf, len); + break; + case 'f': + s = va_arg(ap,char *); + len = strlen(buf) + 1; + if (bufsize < len || len > sizeof(fstring)) + goto no_space; + memcpy(s, buf, len); + break; + case 'B': + i = va_arg(ap, int *); + b = va_arg(ap, char **); + len = 4; + if (bufsize < len) + goto no_space; + *i = IVAL(buf, 0); + if (! *i) { + *b = NULL; + break; + } + len += *i; + if (bufsize < len) + goto no_space; + *b = (char *)malloc(*i); + if (! *b) + goto no_space; + memcpy(*b, buf+4, *i); + break; + default: + DEBUG(0,("Unknown tdb_unpack format %c in %s\n", + c, fmt)); + + len = 0; + break; + } + + buf += len; + bufsize -= len; + } + + va_end(ap); + + DEBUG(18,("tdb_unpack(%s, %d) -> %d\n", + fmt0, bufsize0, (int)PTR_DIFF(buf, buf0))); + + return PTR_DIFF(buf, buf0); + + no_space: + return -1; +} + +/**************************************************************************** + Log tdb messages via DEBUG(). +****************************************************************************/ + +static void tdb_log(TDB_CONTEXT *tdb, int level, const char *format, ...) +{ + va_list ap; + char *ptr = NULL; + + va_start(ap, format); + vasprintf(&ptr, format, ap); + va_end(ap); + + if (!ptr || !*ptr) + return; + + DEBUG(level, ("tdb(%s): %s", tdb->name ? tdb->name : "unnamed", ptr)); + SAFE_FREE(ptr); +} + +/**************************************************************************** + Like tdb_open() but also setup a logging function that redirects to + the samba DEBUG() system. +****************************************************************************/ + +TDB_CONTEXT *tdb_open_log(const char *name, int hash_size, int tdb_flags, + int open_flags, mode_t mode) +{ + TDB_CONTEXT *tdb; + + if (!lp_use_mmap()) + tdb_flags |= TDB_NOMMAP; + + tdb = tdb_open_ex(name, hash_size, tdb_flags, + open_flags, mode, tdb_log); + if (!tdb) + return NULL; + + return tdb; +} + + +/**************************************************************************** + Allow tdb_delete to be used as a tdb_traversal_fn. +****************************************************************************/ + +int tdb_traverse_delete_fn(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf, + void *state) +{ + return tdb_delete(the_tdb, key); +} + + + +/** + * Search across the whole tdb for keys that match the given pattern + * return the result as a list of keys + * + * @param tdb pointer to opened tdb file context + * @param pattern searching pattern used by fnmatch(3) functions + * + * @return list of keys found by looking up with given pattern + **/ +TDB_LIST_NODE *tdb_search_keys(TDB_CONTEXT *tdb, const char* pattern) +{ + TDB_DATA key, next; + TDB_LIST_NODE *list = NULL; + TDB_LIST_NODE *rec = NULL; + TDB_LIST_NODE *tmp = NULL; + + for (key = tdb_firstkey(tdb); key.dptr; key = next) { + /* duplicate key string to ensure null-termination */ + char *key_str = (char*) strndup(key.dptr, key.dsize); + if (!key_str) { + DEBUG(0, ("tdb_search_keys: strndup() failed!\n")); + smb_panic("strndup failed!\n"); + } + + DEBUG(18, ("checking %s for match to pattern %s\n", key_str, pattern)); + + next = tdb_nextkey(tdb, key); + + /* do the pattern checking */ + if (fnmatch(pattern, key_str, 0) == 0) { + rec = (TDB_LIST_NODE*) malloc(sizeof(*rec)); + ZERO_STRUCTP(rec); + + rec->node_key = key; + + DLIST_ADD_END(list, rec, tmp); + + DEBUG(18, ("checking %s matched pattern %s\n", key_str, pattern)); + } else { + free(key.dptr); + } + + /* free duplicated key string */ + free(key_str); + } + + return list; + +}; + + +/** + * Free the list returned by tdb_search_keys + * + * @param node list of results found by tdb_search_keys + **/ +void tdb_search_list_free(TDB_LIST_NODE* node) +{ + TDB_LIST_NODE *next_node; + + while (node) { + next_node = node->next; + SAFE_FREE(node); + node = next_node; + }; +}; + + diff --git a/source4/lib/tdb/tdbutil.h b/source4/lib/tdb/tdbutil.h new file mode 100644 index 0000000000..01473446a1 --- /dev/null +++ b/source4/lib/tdb/tdbutil.h @@ -0,0 +1,37 @@ +/* + Unix SMB/CIFS implementation. + tdb utility functions + Copyright (C) Andrew Tridgell 1999 + + 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. +*/ + +#ifndef __TDBUTIL_H__ +#define __TDBUTIL_H__ + + +/* single node of a list returned by tdb_search_keys */ +typedef struct keys_node +{ + struct keys_node *prev, *next; + TDB_DATA node_key; +} TDB_LIST_NODE; + + +TDB_LIST_NODE *tdb_search_keys(TDB_CONTEXT*, const char*); +void tdb_search_list_free(TDB_LIST_NODE*); + + +#endif /* __TDBUTIL_H__ */ diff --git a/source4/lib/time.c b/source4/lib/time.c new file mode 100644 index 0000000000..2844da004d --- /dev/null +++ b/source4/lib/time.c @@ -0,0 +1,754 @@ +/* + Unix SMB/CIFS implementation. + time handling functions + Copyright (C) Andrew Tridgell 1992-1998 + Copyright (C) Stefan (metze) Metzmacher 2002 + 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" + +/* + This stuff was largely rewritten by Paul Eggert <eggert@twinsun.com> + in May 1996 + */ + + +int extra_time_offset = 0; + +#ifndef CHAR_BIT +#define CHAR_BIT 8 +#endif + +#ifndef TIME_T_MIN +#define TIME_T_MIN ((time_t)0 < (time_t) -1 ? (time_t) 0 \ + : ~ (time_t) 0 << (sizeof (time_t) * CHAR_BIT - 1)) +#endif +#ifndef TIME_T_MAX +#define TIME_T_MAX (~ (time_t) 0 - TIME_T_MIN) +#endif + +void get_nttime_max(NTTIME *t) +{ + /* FIXME: This is incorrect */ + unix_to_nt_time(t, get_time_t_max()); +} + +/******************************************************************* + External access to time_t_min and time_t_max. +********************************************************************/ + +time_t get_time_t_max(void) +{ + return TIME_T_MAX; +} + +/******************************************************************* +a gettimeofday wrapper +********************************************************************/ +void GetTimeOfDay(struct timeval *tval) +{ +#ifdef HAVE_GETTIMEOFDAY_TZ + gettimeofday(tval,NULL); +#else + gettimeofday(tval); +#endif +} + +#define TM_YEAR_BASE 1900 + +/******************************************************************* +yield the difference between *A and *B, in seconds, ignoring leap seconds +********************************************************************/ +static int tm_diff(struct tm *a, struct tm *b) +{ + int ay = a->tm_year + (TM_YEAR_BASE - 1); + int by = b->tm_year + (TM_YEAR_BASE - 1); + int intervening_leap_days = + (ay/4 - by/4) - (ay/100 - by/100) + (ay/400 - by/400); + int years = ay - by; + int days = 365*years + intervening_leap_days + (a->tm_yday - b->tm_yday); + int hours = 24*days + (a->tm_hour - b->tm_hour); + int minutes = 60*hours + (a->tm_min - b->tm_min); + int seconds = 60*minutes + (a->tm_sec - b->tm_sec); + + return seconds; +} + +/******************************************************************* + return the UTC offset in seconds west of UTC, or 0 if it cannot be determined + ******************************************************************/ +static int TimeZone(time_t t) +{ + struct tm *tm = gmtime(&t); + struct tm tm_utc; + if (!tm) + return 0; + tm_utc = *tm; + tm = localtime(&t); + if (!tm) + return 0; + return tm_diff(&tm_utc,tm); + +} + +static BOOL done_serverzone_init; + +/* Return the smb serverzone value */ + +static int get_serverzone(void) +{ + static int serverzone; + + if (!done_serverzone_init) { + serverzone = TimeZone(time(NULL)); + + if ((serverzone % 60) != 0) { + DEBUG(1,("WARNING: Your timezone is not a multiple of 1 minute.\n")); + } + + DEBUG(4,("Serverzone is %d\n",serverzone)); + + done_serverzone_init = True; + } + + return serverzone; +} + +/* Re-read the smb serverzone value */ + +static struct timeval start_time_hires; + +void TimeInit(void) +{ + done_serverzone_init = False; + get_serverzone(); + /* Save the start time of this process. */ + if (start_time_hires.tv_sec == 0 && start_time_hires.tv_usec == 0) + GetTimeOfDay(&start_time_hires); +} + +/********************************************************************** + Return a timeval struct of the uptime of this process. As TimeInit is + done before a daemon fork then this is the start time from the parent + daemon start. JRA. +***********************************************************************/ + +void get_process_uptime(struct timeval *ret_time) +{ + struct timeval time_now_hires; + + GetTimeOfDay(&time_now_hires); + ret_time->tv_sec = time_now_hires.tv_sec - start_time_hires.tv_sec; + ret_time->tv_usec = time_now_hires.tv_usec - start_time_hires.tv_usec; + if (time_now_hires.tv_usec < start_time_hires.tv_usec) { + ret_time->tv_sec -= 1; + ret_time->tv_usec = 1000000 + (time_now_hires.tv_usec - start_time_hires.tv_usec); + } else + ret_time->tv_usec = time_now_hires.tv_usec - start_time_hires.tv_usec; +} + +/******************************************************************* +return the same value as TimeZone, but it should be more efficient. + +We keep a table of DST offsets to prevent calling localtime() on each +call of this function. This saves a LOT of time on many unixes. + +Updated by Paul Eggert <eggert@twinsun.com> +********************************************************************/ +static int TimeZoneFaster(time_t t) +{ + static struct dst_table {time_t start,end; int zone;} *tdt, *dst_table = NULL; + static int table_size = 0; + int i; + int zone = 0; + + if (t == 0) t = time(NULL); + + /* Tunis has a 8 day DST region, we need to be careful ... */ +#define MAX_DST_WIDTH (365*24*60*60) +#define MAX_DST_SKIP (7*24*60*60) + + for (i=0;i<table_size;i++) + if (t >= dst_table[i].start && t <= dst_table[i].end) break; + + if (i<table_size) { + zone = dst_table[i].zone; + } else { + time_t low,high; + + zone = TimeZone(t); + tdt = (struct dst_table *)Realloc(dst_table, + sizeof(dst_table[0])*(i+1)); + if (!tdt) { + DEBUG(0,("TimeZoneFaster: out of memory!\n")); + SAFE_FREE(dst_table); + table_size = 0; + } else { + dst_table = tdt; + table_size++; + + dst_table[i].zone = zone; + dst_table[i].start = dst_table[i].end = t; + + /* no entry will cover more than 6 months */ + low = t - MAX_DST_WIDTH/2; + if (t < low) + low = TIME_T_MIN; + + high = t + MAX_DST_WIDTH/2; + if (high < t) + high = TIME_T_MAX; + + /* widen the new entry using two bisection searches */ + while (low+60*60 < dst_table[i].start) { + if (dst_table[i].start - low > MAX_DST_SKIP*2) + t = dst_table[i].start - MAX_DST_SKIP; + else + t = low + (dst_table[i].start-low)/2; + if (TimeZone(t) == zone) + dst_table[i].start = t; + else + low = t; + } + + while (high-60*60 > dst_table[i].end) { + if (high - dst_table[i].end > MAX_DST_SKIP*2) + t = dst_table[i].end + MAX_DST_SKIP; + else + t = high - (high-dst_table[i].end)/2; + if (TimeZone(t) == zone) + dst_table[i].end = t; + else + high = t; + } +#if 0 + DEBUG(1,("Added DST entry from %s ", + asctime(localtime(&dst_table[i].start)))); + DEBUG(1,("to %s (%d)\n",asctime(localtime(&dst_table[i].end)), + dst_table[i].zone)); +#endif + } + } + return zone; +} + +/**************************************************************************** + return the UTC offset in seconds west of UTC, adjusted for extra time offset + **************************************************************************/ +int TimeDiff(time_t t) +{ + return TimeZoneFaster(t) + 60 * lp_time_offset(); +} + + +/**************************************************************************** + return the UTC offset in seconds west of UTC, adjusted for extra time + offset, for a local time value. If ut = lt + LocTimeDiff(lt), then + lt = ut - TimeDiff(ut), but the converse does not necessarily hold near + daylight savings transitions because some local times are ambiguous. + LocTimeDiff(t) equals TimeDiff(t) except near daylight savings transitions. + +**************************************************************************/ +static int LocTimeDiff(time_t lte) +{ + time_t lt = lte - 60 * lp_time_offset(); + int d = TimeZoneFaster(lt); + time_t t = lt + d; + + /* if overflow occurred, ignore all the adjustments so far */ + if (((lte < lt) ^ (lp_time_offset() < 0)) | ((t < lt) ^ (d < 0))) + t = lte; + + /* now t should be close enough to the true UTC to yield the right answer */ + return TimeDiff(t); +} + + +/**************************************************************************** +try to optimise the localtime call, it can be quite expensive on some machines +****************************************************************************/ +struct tm *LocalTime(time_t *t) +{ + time_t t2 = *t; + + t2 -= TimeDiff(t2); + + return(gmtime(&t2)); +} + +#define TIME_FIXUP_CONSTANT (369.0*365.25*24*60*60-(3.0*24*60*60+6.0*60*60)) + +/**************************************************************************** +interpret an 8 byte "filetime" structure to a time_t +It's originally in "100ns units since jan 1st 1601" + +It appears to be kludge-GMT (at least for file listings). This means +its the GMT you get by taking a localtime and adding the +serverzone. This is NOT the same as GMT in some cases. This routine +converts this to real GMT. +****************************************************************************/ +time_t nt_time_to_unix(const NTTIME *nt) +{ + double d; + time_t ret; + /* The next two lines are a fix needed for the + broken SCO compiler. JRA. */ + time_t l_time_min = TIME_T_MIN; + time_t l_time_max = TIME_T_MAX; + + if (nt->high == 0) return(0); + + d = ((double)nt->high)*4.0*(double)(1<<30); + d += (nt->low&0xFFF00000); + d *= 1.0e-7; + + /* now adjust by 369 years to make the secs since 1970 */ + d -= TIME_FIXUP_CONSTANT; + + if (!(l_time_min <= d && d <= l_time_max)) + return(0); + + ret = (time_t)(d+0.5); + + /* this takes us from kludge-GMT to real GMT */ + ret -= get_serverzone(); + ret += LocTimeDiff(ret); + + return(ret); +} + +/**************************************************************************** + Convert a NTTIME structure to a time_t. + It's originally in "100ns units". + + This is an absolute version of the one above. + By absolute I mean, it doesn't adjust from 1/1/1601 to 1/1/1970 + if the NTTIME was 5 seconds, the time_t is 5 seconds. JFM +****************************************************************************/ + +time_t nt_time_to_unix_abs(NTTIME *nt) +{ + double d; + time_t ret; + /* The next two lines are a fix needed for the + broken SCO compiler. JRA. */ + time_t l_time_min = TIME_T_MIN; + time_t l_time_max = TIME_T_MAX; + + if (nt->high == 0) + return(0); + + if (nt->high==0x80000000 && nt->low==0) + return -1; + + /* reverse the time */ + /* it's a negative value, turn it to positive */ + nt->high=~nt->high; + nt->low=~nt->low; + + d = ((double)nt->high)*4.0*(double)(1<<30); + d += (nt->low&0xFFF00000); + d *= 1.0e-7; + + if (!(l_time_min <= d && d <= l_time_max)) + return(0); + + ret = (time_t)(d+0.5); + + return(ret); +} + +/**************************************************************************** +put a 8 byte filetime from a time_t +This takes real GMT as input and converts to kludge-GMT +****************************************************************************/ +void unix_to_nt_time(NTTIME *nt, time_t t) +{ + double d; + + if (t==0) { + nt->low = 0; + nt->high = 0; + return; + } + if (t == TIME_T_MAX) { + nt->low = 0xffffffff; + nt->high = 0x7fffffff; + return; + } + if (t == -1) { + nt->low = 0xffffffff; + nt->high = 0xffffffff; + return; + } + + /* this converts GMT to kludge-GMT */ + t -= TimeDiff(t) - get_serverzone(); + + d = (double)(t); + d += TIME_FIXUP_CONSTANT; + d *= 1.0e7; + + nt->high = (uint32)(d * (1.0/(4.0*(double)(1<<30)))); + nt->low = (uint32)(d - ((double)nt->high)*4.0*(double)(1<<30)); +} + +/**************************************************************************** + Convert a time_t to a NTTIME structure + + This is an absolute version of the one above. + By absolute I mean, it doesn't adjust from 1/1/1970 to 1/1/1601 + If the nttime_t was 5 seconds, the NTTIME is 5 seconds. JFM +****************************************************************************/ + +void unix_to_nt_time_abs(NTTIME *nt, time_t t) +{ + double d; + + if (t==0) { + nt->low = 0; + nt->high = 0; + return; + } + + if (t == TIME_T_MAX) { + nt->low = 0xffffffff; + nt->high = 0x7fffffff; + return; + } + + if (t == -1) { + /* that's what NT uses for infinite */ + nt->low = 0x0; + nt->high = 0x80000000; + return; + } + + d = (double)(t); + d *= 1.0e7; + + nt->high = (uint32)(d * (1.0/(4.0*(double)(1<<30)))); + nt->low = (uint32)(d - ((double)nt->high)*4.0*(double)(1<<30)); + + /* convert to a negative value */ + nt->high=~nt->high; + nt->low=~nt->low; +} + + +/**************************************************************************** +take an NTTIME structure, containing high / low time. convert to unix time. +lkclXXXX this may need 2 SIVALs not a memcpy. we'll see... +****************************************************************************/ +void put_long_date(char *p,time_t t) +{ + NTTIME nt; + unix_to_nt_time(&nt, t); + SIVAL(p, 0, nt.low); + SIVAL(p, 4, nt.high); +} + +/**************************************************************************** +check if it's a null mtime +****************************************************************************/ +BOOL null_mtime(time_t mtime) +{ + if (mtime == 0 || mtime == (time_t)0xFFFFFFFF || mtime == (time_t)-1) + return True; + return False; +} + +/******************************************************************* + create a 16 bit dos packed date +********************************************************************/ +static uint16 make_dos_date1(struct tm *t) +{ + uint16 ret=0; + ret = (((unsigned)(t->tm_mon+1)) >> 3) | ((t->tm_year-80) << 1); + ret = ((ret&0xFF)<<8) | (t->tm_mday | (((t->tm_mon+1) & 0x7) << 5)); + return ret; +} + +/******************************************************************* + create a 16 bit dos packed time +********************************************************************/ +static uint16 make_dos_time1(struct tm *t) +{ + uint16 ret=0; + ret = ((((unsigned)t->tm_min >> 3)&0x7) | (((unsigned)t->tm_hour) << 3)); + ret = ((ret&0xFF)<<8) | ((t->tm_sec/2) | ((t->tm_min & 0x7) << 5)); + return ret; +} + +/******************************************************************* + create a 32 bit dos packed date/time from some parameters + This takes a GMT time and returns a packed localtime structure +********************************************************************/ +static uint32 make_dos_date(time_t unixdate) +{ + struct tm *t; + uint32 ret=0; + + if (unixdate == 0) { + return 0; + } + + t = LocalTime(&unixdate); + if (!t) { + return 0xFFFFFFFF; + } + + ret = make_dos_date1(t); + ret = ((ret&0xFFFF)<<16) | make_dos_time1(t); + + return ret; +} + +/******************************************************************* +put a dos date into a buffer (time/date format) +This takes GMT time and puts local time in the buffer +********************************************************************/ +void put_dos_date(char *buf,int offset,time_t unixdate) +{ + uint32 x = make_dos_date(unixdate); + SIVAL(buf,offset,x); +} + +/******************************************************************* +put a dos date into a buffer (date/time format) +This takes GMT time and puts local time in the buffer +********************************************************************/ +void put_dos_date2(char *buf,int offset,time_t unixdate) +{ + uint32 x; + x = make_dos_date(unixdate); + x = ((x&0xFFFF)<<16) | ((x&0xFFFF0000)>>16); + SIVAL(buf,offset,x); +} + +/******************************************************************* +put a dos 32 bit "unix like" date into a buffer. This routine takes +GMT and converts it to LOCAL time before putting it (most SMBs assume +localtime for this sort of date) +********************************************************************/ +void put_dos_date3(char *buf,int offset,time_t unixdate) +{ + if (!null_mtime(unixdate)) + unixdate -= TimeDiff(unixdate); + SIVAL(buf,offset,unixdate); +} + +/******************************************************************* + interpret a 32 bit dos packed date/time to some parameters +********************************************************************/ +static void interpret_dos_date(uint32 date,int *year,int *month,int *day,int *hour,int *minute,int *second) +{ + uint32 p0,p1,p2,p3; + + p0=date&0xFF; p1=((date&0xFF00)>>8)&0xFF; + p2=((date&0xFF0000)>>16)&0xFF; p3=((date&0xFF000000)>>24)&0xFF; + + *second = 2*(p0 & 0x1F); + *minute = ((p0>>5)&0xFF) + ((p1&0x7)<<3); + *hour = (p1>>3)&0xFF; + *day = (p2&0x1F); + *month = ((p2>>5)&0xFF) + ((p3&0x1)<<3) - 1; + *year = ((p3>>1)&0xFF) + 80; +} + +/******************************************************************* + create a unix date (int GMT) from a dos date (which is actually in + localtime) +********************************************************************/ +time_t make_unix_date(void *date_ptr) +{ + uint32 dos_date=0; + struct tm t; + time_t ret; + + dos_date = IVAL(date_ptr,0); + + if (dos_date == 0) return (time_t)0; + + interpret_dos_date(dos_date,&t.tm_year,&t.tm_mon, + &t.tm_mday,&t.tm_hour,&t.tm_min,&t.tm_sec); + t.tm_isdst = -1; + + /* mktime() also does the local to GMT time conversion for us */ + ret = mktime(&t); + + return(ret); +} + +/******************************************************************* +like make_unix_date() but the words are reversed +********************************************************************/ +time_t make_unix_date2(void *date_ptr) +{ + uint32 x,x2; + + x = IVAL(date_ptr,0); + x2 = ((x&0xFFFF)<<16) | ((x&0xFFFF0000)>>16); + SIVAL(&x,0,x2); + + return make_unix_date((void *)&x); +} + +/******************************************************************* + create a unix GMT date from a dos date in 32 bit "unix like" format + these generally arrive as localtimes, with corresponding DST + ******************************************************************/ +time_t make_unix_date3(void *date_ptr) +{ + time_t t = (time_t)IVAL(date_ptr,0); + if (!null_mtime(t)) + t += LocTimeDiff(t); + return t; +} + + +/*************************************************************************** +return a HTTP/1.0 time string + ***************************************************************************/ +char *http_timestring(TALLOC_CTX *mem_ctx, time_t t) +{ + char *buf; + fstring tempTime; + struct tm *tm = LocalTime(&t); + + if (!tm) + buf = talloc_asprintf(mem_ctx,"%ld seconds since the Epoch",(long)t); + else +#ifndef HAVE_STRFTIME + buf = talloc_strdup(mem_ctx, asctime(tm)); + if(buf[strlen(buf)-1] == '\n') + buf[strlen(buf)-1] = 0; +#else /* !HAVE_STRFTIME */ + strftime(tempTime, sizeof(tempTime)-1, "%a, %d %b %Y %H:%M:%S %Z", tm); + buf = talloc_strdup(mem_ctx, tempTime); +#endif /* !HAVE_STRFTIME */ + return buf; +} + + + +/**************************************************************************** + Return the date and time as a string +****************************************************************************/ + +char *timestring(TALLOC_CTX *mem_ctx, BOOL hires) +{ + char *TimeBuf; + fstring tempTime; + struct timeval tp; + time_t t; + struct tm *tm; + + if (hires) { + GetTimeOfDay(&tp); + t = (time_t)tp.tv_sec; + } else { + t = time(NULL); + } + tm = LocalTime(&t); + if (!tm) { + if (hires) { + TimeBuf = talloc_asprintf(mem_ctx, + "%ld.%06ld seconds since the Epoch", + (long)tp.tv_sec, + (long)tp.tv_usec); + } else { + TimeBuf = talloc_asprintf(mem_ctx, + "%ld seconds since the Epoch", + (long)t); + } + } else { +#ifdef HAVE_STRFTIME + if (hires) { + strftime(tempTime,sizeof(tempTime)-1,"%Y/%m/%d %H:%M:%S",tm); + TimeBuf = talloc_asprintf(mem_ctx, "%s.%06ld", + tempTime, (long)tp.tv_usec); + } else { + strftime(tempTime,100,"%Y/%m/%d %H:%M:%S",tm); + TimeBuf = talloc_strdup(mem_ctx, tempTime); + } +#else + if (hires) { + TimeBuf = talloc_asprintf(mem_ctx, + "%s.%06ld", + asctime(tm), + (long)tp.tv_usec); + } else { + TimeBuf = talloc_strdup(mem_ctx, asctime(tm)); + } +#endif + } + return(TimeBuf); +} + +/**************************************************************************** + return the best approximation to a 'create time' under UNIX from a stat + structure. +****************************************************************************/ + +time_t get_create_time(SMB_STRUCT_STAT *st,BOOL fake_dirs) +{ + time_t ret, ret1; + + if(S_ISDIR(st->st_mode) && fake_dirs) + return (time_t)315493200L; /* 1/1/1980 */ + + ret = MIN(st->st_ctime, st->st_mtime); + ret1 = MIN(ret, st->st_atime); + + if(ret1 != (time_t)0) + return ret1; + + /* + * One of ctime, mtime or atime was zero (probably atime). + * Just return MIN(ctime, mtime). + */ + return ret; +} + +/**************************************************************************** +initialise an NTTIME to -1, which means "unknown" or "don't expire" +****************************************************************************/ + +void init_nt_time(NTTIME *nt) +{ + nt->high = 0x7FFFFFFF; + nt->low = 0xFFFFFFFF; +} + +/**************************************************************************** +check if NTTIME is 0 +****************************************************************************/ +BOOL nt_time_is_zero(NTTIME *nt) +{ + if(nt->high==0) + return True; + return False; +} + +/* + return a talloced string representing a NTTIME for human consumption +*/ +const char *nt_time_string(TALLOC_CTX *mem_ctx, const NTTIME *nt) +{ + time_t t = nt_time_to_unix(nt); + return talloc_strdup(mem_ctx, http_timestring(mem_ctx, t)); +} + diff --git a/source4/lib/username.c b/source4/lib/username.c new file mode 100644 index 0000000000..c00ac666d4 --- /dev/null +++ b/source4/lib/username.c @@ -0,0 +1,537 @@ +/* + Unix SMB/CIFS implementation. + Username handling + Copyright (C) Andrew Tridgell 1992-1998 + Copyright (C) Jeremy Allison 1997-2001. + + 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" + +/* internal functions */ +static struct passwd *uname_string_combinations(char *s, struct passwd * (*fn) (const char *), int N); +static struct passwd *uname_string_combinations2(char *s, int offset, struct passwd * (*fn) (const char *), int N); + +/***************************************************************** + Check if a user or group name is local (this is a *local* name for + *local* people, there's nothing for you here...). +*****************************************************************/ + +static BOOL name_is_local(const char *name) +{ + return !(strchr_m(name, *lp_winbind_separator())); +} + +/***************************************************************** + Splits passed user or group name to domain and user/group name parts + Returns True if name was splitted and False otherwise. +*****************************************************************/ + +BOOL split_domain_and_name(const char *name, char *domain, char* username) +{ + char *p = strchr(name,*lp_winbind_separator()); + + + /* Parse a string of the form DOMAIN/user into a domain and a user */ + DEBUG(10,("split_domain_and_name: checking whether name |%s| local or not\n", name)); + + if (p) { + fstrcpy(username, p+1); + fstrcpy(domain, name); + domain[PTR_DIFF(p, name)] = 0; + } else if (lp_winbind_use_default_domain()) { + fstrcpy(username, name); + fstrcpy(domain, lp_workgroup()); + } else { + return False; + } + + DEBUG(10,("split_domain_and_name: all is fine, domain is |%s| and name is |%s|\n", domain, username)); + return True; +} + +/**************************************************************************** + Get a users home directory. +****************************************************************************/ + +char *get_user_home_dir(const char *user) +{ + struct passwd *pass; + + /* Ensure the user exists. */ + + pass = Get_Pwnam(user); + + if (!pass) + return(NULL); + /* Return home directory from struct passwd. */ + + return(pass->pw_dir); +} + + +/**************************************************************************** + * A wrapper for sys_getpwnam(). The following variations are tried: + * - as transmitted + * - in all lower case if this differs from transmitted + * - in all upper case if this differs from transmitted + * - using lp_usernamelevel() for permutations. +****************************************************************************/ + +static struct passwd *Get_Pwnam_ret = NULL; + +static struct passwd *Get_Pwnam_internals(const char *user, char *user2) +{ + struct passwd *ret = NULL; + + if (!user2 || !(*user2)) + return(NULL); + + if (!user || !(*user)) + return(NULL); + + /* Try in all lower case first as this is the most + common case on UNIX systems */ + strlower(user2); + DEBUG(5,("Trying _Get_Pwnam(), username as lowercase is %s\n",user2)); + ret = getpwnam_alloc(user2); + if(ret) + goto done; + + /* Try as given, if username wasn't originally lowercase */ + if(strcmp(user, user2) != 0) { + DEBUG(5,("Trying _Get_Pwnam(), username as given is %s\n", user)); + ret = getpwnam_alloc(user); + if(ret) + goto done; + } + + /* Try as uppercase, if username wasn't originally uppercase */ + strupper(user2); + if(strcmp(user, user2) != 0) { + DEBUG(5,("Trying _Get_Pwnam(), username as uppercase is %s\n", user2)); + ret = getpwnam_alloc(user2); + if(ret) + goto done; + } + + /* Try all combinations up to usernamelevel */ + strlower(user2); + DEBUG(5,("Checking combinations of %d uppercase letters in %s\n", lp_usernamelevel(), user2)); + ret = uname_string_combinations(user2, getpwnam_alloc, lp_usernamelevel()); + +done: + DEBUG(5,("Get_Pwnam_internals %s find user [%s]!\n",ret ? "did":"didn't", 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) { + passwd_free(&Get_Pwnam_ret); + } + + Get_Pwnam_ret = ret; + + return ret; +} + +/**************************************************************************** + Get_Pwnam wrapper without modification. + NOTE: This with NOT modify 'user'! +****************************************************************************/ + +struct passwd *Get_Pwnam(const char *user) +{ + fstring user2; + struct passwd *ret; + + fstrcpy(user2, user); + + DEBUG(5,("Finding user %s\n", user)); + + ret = Get_Pwnam_internals(user, user2); + + return ret; +} + +/**************************************************************************** + Check if a user is in a netgroup user list. +****************************************************************************/ + +static BOOL user_in_netgroup_list(const char *user, const char *ngname) +{ +#ifdef HAVE_NETGROUP + //static char *mydomain = NULL; + /* REWRITE: make thread safe if caching */ + char *mydomain = NULL; + //if (mydomain == NULL) + yp_get_default_domain(&mydomain); + + if(mydomain == NULL) { + DEBUG(5,("Unable to get default yp domain\n")); + return False; + } + + DEBUG(5,("looking for user %s of domain %s in netgroup %s\n", + user, mydomain, ngname)); + DEBUG(5,("innetgr is %s\n", innetgr(ngname, NULL, user, mydomain) + ? "TRUE" : "FALSE")); + + if (innetgr(ngname, NULL, user, mydomain)) + return (True); +#endif /* HAVE_NETGROUP */ + return False; +} + +/**************************************************************************** + Check if a user is in a winbind group. +****************************************************************************/ + +static BOOL user_in_winbind_group_list(const char *user, const char *gname, BOOL *winbind_answered) +{ + int num_groups; + int i; + gid_t *groups = NULL; + gid_t gid, gid_low, gid_high; + BOOL ret = False; + + *winbind_answered = False; + + if ((gid = nametogid(gname)) == (gid_t)-1) { + DEBUG(0,("user_in_winbind_group_list: nametogid for group %s failed.\n", + gname )); + goto err; + } + + if (!lp_winbind_gid(&gid_low, &gid_high)) { + DEBUG(4, ("winbind gid range not configured, therefore %s cannot be a winbind group\n", gname)); + goto err; + } + + if (gid < gid_low || gid > gid_high) { + DEBUG(4, ("group %s is not a winbind group\n", gname)); + goto err; + } + + /* + * Get the gid's that this user belongs to. + */ + + if ((num_groups = winbind_getgroups(user, 0, NULL)) == -1) + return False; + + if (num_groups == 0) { + *winbind_answered = True; + return False; + } + + if ((groups = (gid_t *)malloc(sizeof(gid_t) * num_groups )) == NULL) { + DEBUG(0,("user_in_winbind_group_list: malloc fail.\n")); + goto err; + } + + if ((num_groups = winbind_getgroups(user, num_groups, groups)) == -1) { + DEBUG(0,("user_in_winbind_group_list: second winbind_getgroups call \ +failed with error %s\n", strerror(errno) )); + goto err; + } + + /* + * Now we have the gid list for this user - convert the gname + * to a gid_t via either winbind or the local UNIX lookup and do the comparison. + */ + + for (i = 0; i < num_groups; i++) { + if (gid == groups[i]) { + ret = True; + break; + } + } + + *winbind_answered = True; + SAFE_FREE(groups); + return ret; + + err: + + *winbind_answered = False; + SAFE_FREE(groups); + return False; +} + +/**************************************************************************** + Check if a user is in a UNIX group. +****************************************************************************/ +static BOOL user_in_unix_group_list(const char *user,const char *gname) +{ + struct passwd *pass = Get_Pwnam(user); + struct sys_userlist *user_list; + struct sys_userlist *member; + TALLOC_CTX *mem_ctx; + + DEBUG(10,("user_in_unix_group_list: checking user %s in group %s\n", user, gname)); + + /* + * We need to check the users primary group as this + * group is implicit and often not listed in the group database. + */ + + mem_ctx = talloc_init("smbgroupedit talloc"); + if (!mem_ctx) return -1; + if (pass) { + if (strequal(gname,gidtoname(mem_ctx, pass->pw_gid))) { + DEBUG(10,("user_in_unix_group_list: group %s is primary group.\n", gname )); + goto exit; + } + } + + user_list = get_users_in_group(gname); + if (user_list == NULL) { + DEBUG(10,("user_in_unix_group_list: no such group %s\n", gname )); + return False; + } + + for (member = user_list; member; member = member->next) { + DEBUG(10,("user_in_unix_group_list: checking user %s against member %s\n", + user, member->unix_name )); + if (strequal(member->unix_name,user)) { + free_userlist(user_list); + goto exit; + } + } + + free_userlist(user_list); + talloc_destroy(mem_ctx); + return False; +exit: + talloc_destroy(mem_ctx); + return True; +} + +/**************************************************************************** + Check if a user is in a group list. Ask winbind first, then use UNIX. +****************************************************************************/ +static BOOL user_in_group_list(const char *user, const char *gname, gid_t *groups, size_t n_groups) +{ + BOOL winbind_answered = False; + BOOL ret; + gid_t gid; + unsigned i; + + gid = nametogid(gname); + if (gid == (gid_t)-1) + return False; + + if (groups && n_groups > 0) { + for (i=0; i < n_groups; i++) { + if (groups[i] == gid) { + return True; + } + } + return False; + } + + /* fallback if we don't yet have the group list */ + + ret = user_in_winbind_group_list(user, gname, &winbind_answered); + if (!winbind_answered) + ret = user_in_unix_group_list(user, gname); + + if (ret) + DEBUG(10,("user_in_group_list: user |%s| is in group |%s|\n", user, gname)); + return ret; +} + +/**************************************************************************** + Check if a user is in a user list - can check combinations of UNIX + and netgroup lists. +****************************************************************************/ + +BOOL user_in_list(const char *user,const char **list, gid_t *groups, size_t n_groups) +{ + if (!list || !*list) + return False; + + DEBUG(10,("user_in_list: checking user %s in list\n", user)); + + while (*list) { + + DEBUG(10,("user_in_list: checking user |%s| against |%s|\n", user, *list)); + + /* + * Check raw username. + */ + if (strequal(user, *list)) + return(True); + + /* + * Now check to see if any combination + * of UNIX and netgroups has been specified. + */ + + if(**list == '@') { + /* + * Old behaviour. Check netgroup list + * followed by UNIX list. + */ + if(user_in_netgroup_list(user, *list +1)) + return True; + if(user_in_group_list(user, *list +1, groups, n_groups)) + return True; + } else if (**list == '+') { + + if((*(*list +1)) == '&') { + /* + * Search UNIX list followed by netgroup. + */ + if(user_in_group_list(user, *list +2, groups, n_groups)) + return True; + if(user_in_netgroup_list(user, *list +2)) + return True; + + } else { + + /* + * Just search UNIX list. + */ + + if(user_in_group_list(user, *list +1, groups, n_groups)) + return True; + } + + } else if (**list == '&') { + + if(*(*list +1) == '+') { + /* + * Search netgroup list followed by UNIX list. + */ + if(user_in_netgroup_list(user, *list +2)) + return True; + if(user_in_group_list(user, *list +2, groups, n_groups)) + return True; + } else { + /* + * Just search netgroup list. + */ + if(user_in_netgroup_list(user, *list +1)) + return True; + } + } else if (!name_is_local(*list)) { + /* + * If user name did not match and token is not + * a unix group and the token has a winbind separator in the + * name then see if it is a Windows group. + */ + + DOM_SID g_sid; + enum SID_NAME_USE name_type; + BOOL winbind_answered = False; + BOOL ret; + fstring groupname, domain; + + /* Parse a string of the form DOMAIN/user into a domain and a user */ + + char *p = strchr(*list,*lp_winbind_separator()); + + DEBUG(10,("user_in_list: checking if user |%s| is in winbind group |%s|\n", user, *list)); + + if (p) { + fstrcpy(groupname, p+1); + fstrcpy(domain, *list); + domain[PTR_DIFF(p, *list)] = 0; + + /* Check to see if name is a Windows group */ + if (winbind_lookup_name(domain, groupname, &g_sid, &name_type) && name_type == SID_NAME_DOM_GRP) { + + /* Check if user name is in the Windows group */ + ret = user_in_winbind_group_list(user, *list, &winbind_answered); + + if (winbind_answered && ret == True) { + DEBUG(10,("user_in_list: user |%s| is in winbind group |%s|\n", user, *list)); + return ret; + } + } + } + } + + list++; + } + return(False); +} + +/* The functions below have been taken from password.c and slightly modified */ +/**************************************************************************** + Apply a function to upper/lower case combinations + of a string and return true if one of them returns true. + Try all combinations with N uppercase letters. + offset is the first char to try and change (start with 0) + it assumes the string starts lowercased +****************************************************************************/ + +static struct passwd *uname_string_combinations2(char *s,int offset,struct passwd *(*fn)(const char *),int N) +{ + ssize_t len = (ssize_t)strlen(s); + int i; + struct passwd *ret; + + if (N <= 0 || offset >= len) + return(fn(s)); + + for (i=offset;i<(len-(N-1));i++) { + char c = s[i]; + if (!islower((int)c)) + continue; + s[i] = toupper(c); + ret = uname_string_combinations2(s,i+1,fn,N-1); + if(ret) + return(ret); + s[i] = c; + } + return(NULL); +} + +/**************************************************************************** + Apply a function to upper/lower case combinations + of a string and return true if one of them returns true. + Try all combinations with up to N uppercase letters. + offset is the first char to try and change (start with 0) + it assumes the string starts lowercased +****************************************************************************/ + +static struct passwd * uname_string_combinations(char *s,struct passwd * (*fn)(const char *),int N) +{ + int n; + struct passwd *ret; + + for (n=1;n<=N;n++) { + ret = uname_string_combinations2(s,0,fn,n); + if(ret) + return(ret); + } + return(NULL); +} + diff --git a/source4/lib/util.c b/source4/lib/util.c new file mode 100644 index 0000000000..64e3dfe88c --- /dev/null +++ b/source4/lib/util.c @@ -0,0 +1,1000 @@ +/* + Unix SMB/CIFS implementation. + Samba utility functions + Copyright (C) Andrew Tridgell 1992-1998 + Copyright (C) Jeremy Allison 2001-2002 + Copyright (C) Simo Sorce 2001 + Copyright (C) Anthony Liguori 2003 + Copyright (C) James J Myers 2003 + + 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" + +#if (defined(HAVE_NETGROUP) && defined (WITH_AUTOMOUNT)) +#ifdef WITH_NISPLUS_HOME +#ifdef BROKEN_NISPLUS_INCLUDE_FILES +/* + * The following lines are needed due to buggy include files + * in Solaris 2.6 which define GROUP in both /usr/include/sys/acl.h and + * also in /usr/include/rpcsvc/nis.h. The definitions conflict. JRA. + * Also GROUP_OBJ is defined as 0x4 in /usr/include/sys/acl.h and as + * an enum in /usr/include/rpcsvc/nis.h. + */ + +#if defined(GROUP) +#undef GROUP +#endif + +#if defined(GROUP_OBJ) +#undef GROUP_OBJ +#endif + +#endif /* BROKEN_NISPLUS_INCLUDE_FILES */ + +#include <rpcsvc/nis.h> + +#else /* !WITH_NISPLUS_HOME */ + +#include "rpcsvc/ypclnt.h" + +#endif /* WITH_NISPLUS_HOME */ +#endif /* HAVE_NETGROUP && WITH_AUTOMOUNT */ + + +/**************************************************************************n + Find a suitable temporary directory. The result should be copied immediately + as it may be overwritten by a subsequent call. +****************************************************************************/ + +const char *tmpdir(void) +{ + char *p; + if ((p = getenv("TMPDIR"))) + return p; + return "/tmp"; +} + +/**************************************************************************** + Determine whether we are in the specified group. +****************************************************************************/ + +BOOL in_group(gid_t group, gid_t current_gid, int ngroups, const gid_t *groups) +{ + int i; + + if (group == current_gid) + return(True); + + for (i=0;i<ngroups;i++) + if (group == groups[i]) + return(True); + + return(False); +} + +/******************************************************************* + Check if a file exists - call vfs_file_exist for samba files. +********************************************************************/ + +BOOL file_exist(const char *fname,SMB_STRUCT_STAT *sbuf) +{ + SMB_STRUCT_STAT st; + if (!sbuf) + sbuf = &st; + + if (sys_stat(fname,sbuf) != 0) + return(False); + + return((S_ISREG(sbuf->st_mode)) || (S_ISFIFO(sbuf->st_mode))); +} + +/******************************************************************* + Check a files mod time. +********************************************************************/ + +time_t file_modtime(const char *fname) +{ + SMB_STRUCT_STAT st; + + if (sys_stat(fname,&st) != 0) + return(0); + + return(st.st_mtime); +} + +/******************************************************************* + Check if a directory exists. +********************************************************************/ + +BOOL directory_exist(char *dname,SMB_STRUCT_STAT *st) +{ + SMB_STRUCT_STAT st2; + BOOL ret; + + if (!st) + st = &st2; + + if (sys_stat(dname,st) != 0) + return(False); + + ret = S_ISDIR(st->st_mode); + if(!ret) + errno = ENOTDIR; + return ret; +} + +/******************************************************************* + Returns the size in bytes of the named file. +********************************************************************/ +SMB_OFF_T get_file_size(char *file_name) +{ + SMB_STRUCT_STAT buf; + buf.st_size = 0; + if(sys_stat(file_name,&buf) != 0) + return (SMB_OFF_T)-1; + return(buf.st_size); +} + +/******************************************************************* + Close the low 3 fd's and open dev/null in their place. +********************************************************************/ +void close_low_fds(BOOL stderr_too) +{ +#ifndef VALGRIND + int fd; + int i; + + close(0); + close(1); + + if (stderr_too) + close(2); + + /* try and use up these file descriptors, so silly + library routines writing to stdout etc won't cause havoc */ + for (i=0;i<3;i++) { + if (i == 2 && !stderr_too) + continue; + + fd = sys_open("/dev/null",O_RDWR,0); + if (fd < 0) + fd = sys_open("/dev/null",O_WRONLY,0); + if (fd < 0) { + DEBUG(0,("Can't open /dev/null\n")); + return; + } + if (fd != i) { + DEBUG(0,("Didn't get file descriptor %d\n",i)); + return; + } + } +#endif +} + +/**************************************************************************** + Set a fd into blocking/nonblocking mode. Uses POSIX O_NONBLOCK if available, + else + if SYSV use O_NDELAY + if BSD use FNDELAY +****************************************************************************/ + +int set_blocking(int fd, BOOL set) +{ + int val; +#ifdef O_NONBLOCK +#define FLAG_TO_SET O_NONBLOCK +#else +#ifdef SYSV +#define FLAG_TO_SET O_NDELAY +#else /* BSD */ +#define FLAG_TO_SET FNDELAY +#endif +#endif + + if((val = sys_fcntl_long(fd, F_GETFL, 0)) == -1) + return -1; + if(set) /* Turn blocking on - ie. clear nonblock flag */ + val &= ~FLAG_TO_SET; + else + val |= FLAG_TO_SET; + return sys_fcntl_long( fd, F_SETFL, val); +#undef FLAG_TO_SET +} + + +/******************************************************************* + Sleep for a specified number of milliseconds. +********************************************************************/ + +void msleep(unsigned int t) +{ + struct timeval tval; + + tval.tv_sec = t/1000; + tval.tv_usec = 1000*(t%1000); + /* this should be the real select - do NOT replace + with sys_select() */ + select(0,NULL,NULL,NULL,&tval); +} + +/**************************************************************************** + Become a daemon, discarding the controlling terminal. +****************************************************************************/ + +void become_daemon(BOOL Fork) +{ + if (Fork) { + if (fork()) { + _exit(0); + } + } + + /* detach from the terminal */ +#ifdef HAVE_SETSID + setsid(); +#elif defined(TIOCNOTTY) + { + int i = sys_open("/dev/tty", O_RDWR, 0); + if (i != -1) { + ioctl(i, (int) TIOCNOTTY, (char *)0); + close(i); + } + } +#endif /* HAVE_SETSID */ + + /* Close fd's 0,1,2. Needed if started by rsh */ + close_low_fds(False); /* Don't close stderr, let the debug system + attach it to the logfile */ +} + + +/**************************************************************************** + Expand a pointer to be a particular size. +****************************************************************************/ + +void *Realloc(void *p,size_t size) +{ + void *ret=NULL; + + if (size == 0) { + SAFE_FREE(p); + DEBUG(5,("Realloc asked for 0 bytes\n")); + return NULL; + } + + if (!p) + ret = (void *)malloc(size); + else + ret = (void *)realloc(p,size); + + if (!ret) + DEBUG(0,("Memory allocation error: failed to expand to %d bytes\n",(int)size)); + + return(ret); +} + +/**************************************************************************** + Free memory, checks for NULL. + Use directly SAFE_FREE() + Exists only because we need to pass a function pointer somewhere --SSS +****************************************************************************/ + +void safe_free(void *p) +{ + SAFE_FREE(p); +} + + +/* + see if a string matches either our primary or one of our secondary + netbios aliases. do a case insensitive match +*/ +BOOL is_myname(const char *name) +{ + const char **aliases; + int i; + + if (strcasecmp(name, lp_netbios_name()) == 0) { + return True; + } + + aliases = lp_netbios_aliases(); + for (i=0; aliases && aliases[i]; i++) { + if (strcasecmp(name, aliases[i]) == 0) { + return True; + } + } + + return False; +} + + +/**************************************************************************** + Get my own name, return in malloc'ed storage. +****************************************************************************/ + +char* get_myname(void) +{ + char *hostname; + const int host_name_max = 255; + char *p; + + hostname = malloc(host_name_max+1); + *hostname = 0; + + /* get my host name */ + if (gethostname(hostname, host_name_max+1) == -1) { + DEBUG(0,("gethostname failed\n")); + return NULL; + } + + /* Ensure null termination. */ + hostname[host_name_max] = '\0'; + + /* split off any parts after an initial . */ + p = strchr_m(hostname,'.'); + + if (p) + *p = 0; + + return hostname; +} + +/**************************************************************************** + Get my own name, including domain. +****************************************************************************/ + +BOOL get_myfullname(char *my_name) +{ + pstring hostname; + + *hostname = 0; + + /* get my host name */ + if (gethostname(hostname, sizeof(hostname)) == -1) { + DEBUG(0,("gethostname failed\n")); + return False; + } + + /* Ensure null termination. */ + hostname[sizeof(hostname)-1] = '\0'; + + if (my_name) + fstrcpy(my_name, hostname); + return True; +} + +/**************************************************************************** + Get my own domain name. +****************************************************************************/ + +BOOL get_mydomname(fstring my_domname) +{ + pstring hostname; + char *p; + + *hostname = 0; + /* get my host name */ + if (gethostname(hostname, sizeof(hostname)) == -1) { + DEBUG(0,("gethostname failed\n")); + return False; + } + + /* Ensure null termination. */ + hostname[sizeof(hostname)-1] = '\0'; + + p = strchr_m(hostname, '.'); + + if (!p) + return False; + + p++; + + if (my_domname) + fstrcpy(my_domname, p); + + return True; +} + +/**************************************************************************** + Interpret a protocol description string, with a default. +****************************************************************************/ + +int interpret_protocol(char *str,int def) +{ + if (strequal(str,"NT1")) + return(PROTOCOL_NT1); + if (strequal(str,"LANMAN2")) + return(PROTOCOL_LANMAN2); + if (strequal(str,"LANMAN1")) + return(PROTOCOL_LANMAN1); + if (strequal(str,"CORE")) + return(PROTOCOL_CORE); + if (strequal(str,"COREPLUS")) + return(PROTOCOL_COREPLUS); + if (strequal(str,"CORE+")) + return(PROTOCOL_COREPLUS); + + DEBUG(0,("Unrecognised protocol level %s\n",str)); + + return(def); +} + +/**************************************************************************** + Return true if a string could be a pure IP address. +****************************************************************************/ + +BOOL is_ipaddress(const char *str) +{ + BOOL pure_address = True; + int i; + + for (i=0; pure_address && str[i]; i++) + if (!(isdigit((int)str[i]) || str[i] == '.')) + pure_address = False; + + /* Check that a pure number is not misinterpreted as an IP */ + pure_address = pure_address && (strchr_m(str, '.') != NULL); + + return pure_address; +} + +/**************************************************************************** + Interpret an internet address or name into an IP address in 4 byte form. +****************************************************************************/ + +uint32 interpret_addr(const char *str) +{ + struct hostent *hp; + uint32 res; + + if (strcmp(str,"0.0.0.0") == 0) + return(0); + if (strcmp(str,"255.255.255.255") == 0) + return(0xFFFFFFFF); + + /* if it's in the form of an IP address then get the lib to interpret it */ + if (is_ipaddress(str)) { + res = inet_addr(str); + } else { + /* otherwise assume it's a network name of some sort and use + sys_gethostbyname */ + if ((hp = sys_gethostbyname(str)) == 0) { + DEBUG(3,("sys_gethostbyname: Unknown host. %s\n",str)); + return 0; + } + + if(hp->h_addr == NULL) { + DEBUG(3,("sys_gethostbyname: host address is invalid for host %s\n",str)); + return 0; + } + putip((char *)&res,(char *)hp->h_addr); + } + + if (res == (uint32)-1) + return(0); + + return(res); +} + +/******************************************************************* + A convenient addition to interpret_addr(). +******************************************************************/ + +struct in_addr *interpret_addr2(TALLOC_CTX *mem_ctx, const char *str) +{ + struct in_addr *ret; + uint32 a = interpret_addr(str); + + ret = talloc(mem_ctx, sizeof(struct in_addr)); + if (!ret) return NULL; + ret->s_addr = a; + return(ret); +} + +/******************************************************************* + Check if an IP is the 0.0.0.0. +******************************************************************/ + +BOOL is_zero_ip(struct in_addr ip) +{ + uint32 a; + putip((char *)&a,(char *)&ip); + return(a == 0); +} + +/******************************************************************* + Set an IP to 0.0.0.0. +******************************************************************/ + +void zero_ip(struct in_addr *ip) +{ + *ip = inet_makeaddr(0,0); + return; +} + + +/******************************************************************* + Are two IPs on the same subnet? +********************************************************************/ + +BOOL same_net(struct in_addr ip1,struct in_addr ip2,struct in_addr mask) +{ + uint32 net1,net2,nmask; + + nmask = ntohl(mask.s_addr); + net1 = ntohl(ip1.s_addr); + net2 = ntohl(ip2.s_addr); + + return((net1 & nmask) == (net2 & nmask)); +} + + +/**************************************************************************** + Check if a process exists. Does this work on all unixes? +****************************************************************************/ + +BOOL process_exists(pid_t pid) +{ + /* Doing kill with a non-positive pid causes messages to be + * sent to places we don't want. */ + SMB_ASSERT(pid > 0); + return(kill(pid,0) == 0 || errno != ESRCH); +} + +/******************************************************************* + Convert a gid into a group name. +********************************************************************/ + +char *gidtoname(TALLOC_CTX *mem_ctx, gid_t gid) +{ + char *name; + struct group *grp; + + grp = getgrgid(gid); + if (grp) + return(grp->gr_name); + name = talloc_asprintf(mem_ctx, "%d",(int)gid); + return(name); +} + + +/******************************************************************* + Convert a name to a gid_t if possible. Return -1 if not a group. +********************************************************************/ + +gid_t nametogid(const char *name) +{ + struct group *grp; + char *p; + gid_t g; + + g = (gid_t)strtol(name, &p, 0); + if ((p != name) && (*p == '\0')) + return g; + + grp = sys_getgrnam(name); + if (grp) + return(grp->gr_gid); + return (gid_t)-1; +} + +/******************************************************************* + Something really nasty happened - panic ! +********************************************************************/ + +void smb_panic(const char *why) +{ + char *cmd = lp_panic_action(); + int result; + + if (cmd && *cmd) { + DEBUG(0, ("smb_panic(): calling panic action [%s]\n", cmd)); + result = system(cmd); + + if (result == -1) + DEBUG(0, ("smb_panic(): fork failed in panic action: %s\n", + strerror(errno))); + else + DEBUG(0, ("smb_panic(): action returned status %d\n", + WEXITSTATUS(result))); + } + DEBUG(0,("PANIC: %s\n", why)); + abort(); +} + +/**************************************************************************** + Simple routine to do POSIX file locking. Cruft in NFS and 64->32 bit mapping + is dealt with in posix.c +****************************************************************************/ + +BOOL fcntl_lock(int fd, int op, SMB_OFF_T offset, SMB_OFF_T count, int type) +{ + SMB_STRUCT_FLOCK lock; + int ret; + + DEBUG(8,("fcntl_lock %d %d %.0f %.0f %d\n",fd,op,(double)offset,(double)count,type)); + + lock.l_type = type; + lock.l_whence = SEEK_SET; + lock.l_start = offset; + lock.l_len = count; + lock.l_pid = 0; + + ret = sys_fcntl_ptr(fd,op,&lock); + + if (ret == -1 && errno != 0) + DEBUG(3,("fcntl_lock: fcntl lock gave errno %d (%s)\n",errno,strerror(errno))); + + /* a lock query */ + if (op == SMB_F_GETLK) { + if ((ret != -1) && + (lock.l_type != F_UNLCK) && + (lock.l_pid != 0) && + (lock.l_pid != getpid())) { + DEBUG(3,("fcntl_lock: fd %d is locked by pid %d\n",fd,(int)lock.l_pid)); + return(True); + } + + /* it must be not locked or locked by me */ + return(False); + } + + /* a lock set or unset */ + if (ret == -1) { + DEBUG(3,("fcntl_lock: lock failed at offset %.0f count %.0f op %d type %d (%s)\n", + (double)offset,(double)count,op,type,strerror(errno))); + return(False); + } + + /* everything went OK */ + DEBUG(8,("fcntl_lock: Lock call successful\n")); + + return(True); +} + +/******************************************************************* + Set the remote_arch string based on an enum. This is used in places +where we desperately need to distinguish client type. +********************************************************************/ +void set_remote_arch(struct server_context *smb, enum remote_arch_types type) +{ + const char *arch; + + smb->negotiate.ra_type = type; + switch (type) { + case RA_WFWG: + arch = "WfWg"; + return; + case RA_OS2: + arch = "OS2"; + return; + case RA_WIN95: + arch = "Win95"; + return; + case RA_WINNT: + arch = "WinNT"; + return; + case RA_WIN2K: + arch = "Win2K"; + return; + case RA_WINXP: + arch = "WinXP"; + return; + case RA_SAMBA: + arch = "Samba"; + return; + default: + smb->negotiate.ra_type = RA_UNKNOWN; + arch = "UNKNOWN"; + break; + } + + sub_set_remote_arch(arch); +} + + +void print_asc(int level, const unsigned char *buf,int len) +{ + int i; + for (i=0;i<len;i++) + DEBUGADD(level,("%c", isprint(buf[i])?buf[i]:'.')); +} + +void dump_data(int level, const char *buf1,int len) +{ + const unsigned char *buf = (const unsigned char *)buf1; + int i=0; + if (len<=0) return; + + if (!DEBUGLVL(level)) return; + + DEBUGADD(level,("[%03X] ",i)); + for (i=0;i<len;) { + DEBUGADD(level,("%02X ",(int)buf[i])); + i++; + if (i%8 == 0) DEBUGADD(level,(" ")); + if (i%16 == 0) { + print_asc(level,&buf[i-16],8); DEBUGADD(level,(" ")); + print_asc(level,&buf[i-8],8); DEBUGADD(level,("\n")); + if (i<len) DEBUGADD(level,("[%03X] ",i)); + } + } + if (i%16) { + int n; + n = 16 - (i%16); + DEBUGADD(level,(" ")); + if (n>8) DEBUGADD(level,(" ")); + while (n--) DEBUGADD(level,(" ")); + n = MIN(8,i%16); + print_asc(level,&buf[i-(i%16)],n); DEBUGADD(level,( " " )); + n = (i%16) - n; + if (n>0) print_asc(level,&buf[i-n],n); + DEBUGADD(level,("\n")); + } +} + +/***************************************************************** + Possibly replace mkstemp if it is broken. +*****************************************************************/ + +int smb_mkstemp(char *template) +{ +#if HAVE_SECURE_MKSTEMP + return mkstemp(template); +#else + /* have a reasonable go at emulating it. Hope that + the system mktemp() isn't completly hopeless */ + char *p = mktemp(template); + if (!p) + return -1; + return open(p, O_CREAT|O_EXCL|O_RDWR, 0600); +#endif +} + +/***************************************************************** + malloc that aborts with smb_panic on fail or zero size. + *****************************************************************/ + +void *smb_xmalloc(size_t size) +{ + void *p; + if (size == 0) + smb_panic("smb_xmalloc: called with zero size.\n"); + if ((p = malloc(size)) == NULL) + smb_panic("smb_xmalloc: malloc fail.\n"); + return p; +} + +/** + Memdup with smb_panic on fail. +**/ + +void *smb_xmemdup(const void *p, size_t size) +{ + void *p2; + p2 = smb_xmalloc(size); + memcpy(p2, p, size); + return p2; +} + +/** + strdup that aborts on malloc fail. +**/ + +char *smb_xstrdup(const char *s) +{ + char *s1 = strdup(s); + if (!s1) + smb_panic("smb_xstrdup: malloc fail\n"); + return s1; +} + + +/* + vasprintf that aborts on malloc fail +*/ + + int smb_xvasprintf(char **ptr, const char *format, va_list ap) +{ + int n; + va_list ap2; + + VA_COPY(ap2, ap); + + n = vasprintf(ptr, format, ap2); + if (n == -1 || ! *ptr) + smb_panic("smb_xvasprintf: out of memory"); + return n; +} + +/***************************************************************** + Like strdup but for memory. +*****************************************************************/ + +void *memdup(const void *p, size_t size) +{ + void *p2; + if (size == 0) + return NULL; + p2 = malloc(size); + if (!p2) + return NULL; + memcpy(p2, p, size); + return p2; +} + +/***************************************************************** + Get local hostname and cache result. +*****************************************************************/ + +char *myhostname(TALLOC_CTX *mem_ctx) +{ + char *myname, *ret; + myname = get_myname(); + ret = talloc_strdup(mem_ctx, myname); + free(myname); + return ret; + +} + +/***************************************************************** + A useful function for returning a path in the Samba lock directory. +*****************************************************************/ + +char *lock_path(TALLOC_CTX* mem_ctx, const char *name) +{ + char *fname; + + fname = talloc_strdup(mem_ctx, lp_lockdir()); + trim_string(fname,"","/"); + + if (!directory_exist(fname,NULL)) + mkdir(fname,0755); + + fname = talloc_asprintf(mem_ctx, "%s/%s", fname, name); + + return fname; +} + +/** + * @brief Returns an absolute path to a file in the Samba lib directory. + * + * @param name File to find, relative to LIBDIR. + * + * @retval Pointer to a talloc'ed string containing the full path. + **/ + +char *lib_path(TALLOC_CTX* mem_ctx, const char *name) +{ + char *fname; + fname = talloc_asprintf(mem_ctx, "%s/%s", dyn_LIBDIR, name); + return fname; +} + +/** + * @brief Returns the platform specific shared library extension. + * + * @retval Pointer to a static #fstring containing the extension. + **/ + +const char *shlib_ext(void) +{ + return dyn_SHLIBEXT; +} + + +/********************************************************* + Recursive routine that is called by unix_wild_match. +*********************************************************/ + +static BOOL unix_do_match(char *regexp, char *str) +{ + char *p; + + for( p = regexp; *p && *str; ) { + + switch(*p) { + case '?': + str++; + p++; + break; + + case '*': + + /* + * Look for a character matching + * the one after the '*'. + */ + p++; + if(!*p) + return True; /* Automatic match */ + while(*str) { + + while(*str && (*p != *str)) + str++; + + /* + * Patch from weidel@multichart.de. In the case of the regexp + * '*XX*' we want to ensure there are at least 2 'X' characters + * in the string after the '*' for a match to be made. + */ + + { + int matchcount=0; + + /* + * Eat all the characters that match, but count how many there were. + */ + + while(*str && (*p == *str)) { + str++; + matchcount++; + } + + /* + * Now check that if the regexp had n identical characters that + * matchcount had at least that many matches. + */ + + while ( *(p+1) && (*(p+1) == *p)) { + p++; + matchcount--; + } + + if ( matchcount <= 0 ) + return False; + } + + str--; /* We've eaten the match char after the '*' */ + + if(unix_do_match(p, str)) + return True; + + if(!*str) + return False; + else + str++; + } + return False; + + default: + if(*str != *p) + return False; + str++; + p++; + break; + } + } + + if(!*p && !*str) + return True; + + if (!*p && str[0] == '.' && str[1] == 0) + return(True); + + if (!*str && *p == '?') { + while (*p == '?') + p++; + return(!*p); + } + + if(!*str && (*p == '*' && p[1] == '\0')) + return True; + + return False; +} diff --git a/source4/lib/util_file.c b/source4/lib/util_file.c new file mode 100644 index 0000000000..0eb8046227 --- /dev/null +++ b/source4/lib/util_file.c @@ -0,0 +1,531 @@ +/* + * Unix SMB/CIFS implementation. + * SMB parameters and setup + * Copyright (C) Andrew Tridgell 1992-1998 Modified by Jeremy Allison 1995. + * + * 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" + +static int gotalarm; + +/*************************************************************** + Signal function to tell us we timed out. +****************************************************************/ + +static void gotalarm_sig(void) +{ + gotalarm = 1; +} + +/*************************************************************** + Lock or unlock a fd for a known lock type. Abandon after waitsecs + seconds. +****************************************************************/ + +BOOL do_file_lock(int fd, int waitsecs, int type) +{ + SMB_STRUCT_FLOCK lock; + int ret; + void (*oldsig_handler)(int); + + gotalarm = 0; + oldsig_handler = CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig); + + lock.l_type = type; + lock.l_whence = SEEK_SET; + lock.l_start = 0; + lock.l_len = 1; + lock.l_pid = 0; + + alarm(waitsecs); + /* Note we must *NOT* use sys_fcntl here ! JRA */ + ret = fcntl(fd, SMB_F_SETLKW, &lock); + alarm(0); + CatchSignal(SIGALRM, SIGNAL_CAST oldsig_handler); + + if (gotalarm) { + DEBUG(0, ("do_file_lock: failed to %s file.\n", + type == F_UNLCK ? "unlock" : "lock")); + return False; + } + + return (ret == 0); +} + + +/*************************************************************** + Lock an fd. Abandon after waitsecs seconds. +****************************************************************/ + +BOOL file_lock(int fd, int type, int secs, int *plock_depth) +{ + if (fd < 0) + return False; + + (*plock_depth)++; + + if ((*plock_depth) == 0) + { + if (!do_file_lock(fd, secs, type)) { + DEBUG(10,("file_lock: locking file failed, error = %s.\n", + strerror(errno))); + return False; + } + } + + return True; +} + +/*************************************************************** + Unlock an fd. Abandon after waitsecs seconds. +****************************************************************/ + +BOOL file_unlock(int fd, int *plock_depth) +{ + BOOL ret=True; + + if(*plock_depth == 1) + ret = do_file_lock(fd, 5, F_UNLCK); + + (*plock_depth)--; + + if(!ret) + DEBUG(10,("file_unlock: unlocking file failed, error = %s.\n", + strerror(errno))); + return ret; +} + + +/************************************************************************* + gets a line out of a file. + line is of format "xxxx:xxxxxx:xxxxx:". + lines with "#" at the front are ignored. +*************************************************************************/ +int getfileline(void *vp, char *linebuf, int linebuf_size) +{ + /* Static buffers we will return. */ + FILE *fp = (FILE *)vp; + unsigned char c; + unsigned char *p; + size_t linebuf_len; + + if (fp == NULL) + { + DEBUG(0,("getfileline: Bad file pointer.\n")); + return -1; + } + + /* + * Scan the file, a line at a time. + */ + while (!feof(fp)) + { + linebuf[0] = '\0'; + + fgets(linebuf, linebuf_size, fp); + if (ferror(fp)) + { + return -1; + } + + /* + * Check if the string is terminated with a newline - if not + * then we must keep reading and discard until we get one. + */ + + linebuf_len = strlen(linebuf); + if (linebuf_len == 0) + { + linebuf[0] = '\0'; + return 0; + } + + if (linebuf[linebuf_len - 1] != '\n') + { + c = '\0'; + while (!ferror(fp) && !feof(fp)) + { + c = fgetc(fp); + if (c == '\n') + { + break; + } + } + } + else + { + linebuf[linebuf_len - 1] = '\0'; + } + +#ifdef DEBUG_PASSWORD + DEBUG(100, ("getfileline: got line |%s|\n", linebuf)); +#endif + if ((linebuf[0] == 0) && feof(fp)) + { + DEBUG(4, ("getfileline: end of file reached\n")); + return 0; + } + + if (linebuf[0] == '#' || linebuf[0] == '\0') + { + DEBUG(6, ("getfileline: skipping comment or blank line\n")); + continue; + } + + p = (unsigned char *) strchr_m(linebuf, ':'); + if (p == NULL) + { + DEBUG(0, ("getfileline: malformed line entry (no :)\n")); + continue; + } + return linebuf_len; + } + return -1; +} + + +/**************************************************************************** +read a line from a file with possible \ continuation chars. +Blanks at the start or end of a line are stripped. +The string will be allocated if s2 is NULL +****************************************************************************/ +char *fgets_slash(char *s2,int maxlen,XFILE *f) +{ + char *s=s2; + int len = 0; + int c; + BOOL start_of_line = True; + + if (x_feof(f)) + return(NULL); + + if (maxlen <2) return(NULL); + + if (!s2) + { + maxlen = MIN(maxlen,8); + s = (char *)malloc(maxlen); + } + + if (!s) return(NULL); + + *s = 0; + + while (len < maxlen-1) + { + c = x_getc(f); + switch (c) + { + case '\r': + break; + case '\n': + while (len > 0 && s[len-1] == ' ') + { + s[--len] = 0; + } + if (len > 0 && s[len-1] == '\\') + { + s[--len] = 0; + start_of_line = True; + break; + } + return(s); + case EOF: + if (len <= 0 && !s2) + SAFE_FREE(s); + return(len>0?s:NULL); + case ' ': + if (start_of_line) + break; + default: + start_of_line = False; + s[len++] = c; + s[len] = 0; + } + if (!s2 && len > maxlen-3) + { + char *t; + + maxlen *= 2; + t = (char *)Realloc(s,maxlen); + if (!t) { + DEBUG(0,("fgets_slash: failed to expand buffer!\n")); + SAFE_FREE(s); + return(NULL); + } else s = t; + } + } + return(s); +} + + +/**************************************************************************** +load from a pipe into memory +****************************************************************************/ +char *file_pload(char *syscmd, size_t *size) +{ + int fd, n; + char *p, *tp; + pstring buf; + size_t total; + + fd = sys_popen(syscmd); + if (fd == -1) return NULL; + + p = NULL; + total = 0; + + while ((n = read(fd, buf, sizeof(buf))) > 0) { + tp = Realloc(p, total + n + 1); + if (!tp) { + DEBUG(0,("file_pload: failed to expand buffer!\n")); + close(fd); + SAFE_FREE(p); + return NULL; + } else p = tp; + memcpy(p+total, buf, n); + total += n; + } + if (p) p[total] = 0; + + /* FIXME: Perhaps ought to check that the command completed + * successfully (returned 0); if not the data may be + * truncated. */ + sys_pclose(fd); + + if (size) *size = total; + + return p; +} + +/**************************************************************************** +load a file into memory from a fd. +****************************************************************************/ + +char *fd_load(int fd, size_t *size) +{ + SMB_STRUCT_STAT sbuf; + char *p; + + if (sys_fstat(fd, &sbuf) != 0) return NULL; + + p = (char *)malloc(sbuf.st_size+1); + if (!p) return NULL; + + if (read(fd, p, sbuf.st_size) != sbuf.st_size) { + SAFE_FREE(p); + return NULL; + } + p[sbuf.st_size] = 0; + + if (size) *size = sbuf.st_size; + + return p; +} + +/**************************************************************************** +load a file into memory +****************************************************************************/ +char *file_load(const char *fname, size_t *size) +{ + int fd; + char *p; + + if (!fname || !*fname) return NULL; + + fd = open(fname,O_RDONLY); + if (fd == -1) return NULL; + + p = fd_load(fd, size); + + close(fd); + + return p; +} + + +/******************************************************************* +mmap (if possible) or read a file +********************************************************************/ +void *map_file(char *fname, size_t size) +{ + size_t s2 = 0; + void *p = NULL; +#ifdef HAVE_MMAP + if (lp_use_mmap()) { + int fd; + fd = open(fname, O_RDONLY, 0); + if (fd == -1) { + DEBUG(2,("Failed to load %s - %s\n", fname, strerror(errno))); + return NULL; + } + p = mmap(NULL, size, PROT_READ, MAP_SHARED|MAP_FILE, fd, 0); + close(fd); + if (p == MAP_FAILED) { + DEBUG(1,("Failed to mmap %s - %s\n", fname, strerror(errno))); + return NULL; + } + } +#endif + if (!p) { + p = file_load(fname, &s2); + if (!p) return NULL; + if (s2 != size) { + DEBUG(1,("incorrect size for %s - got %d expected %d\n", + fname, s2, size)); + if (p) free(p); + return NULL; + } + } + + return p; +} + + +/**************************************************************************** +parse a buffer into lines +****************************************************************************/ +static char **file_lines_parse(char *p, size_t size, int *numlines) +{ + int i; + char *s, **ret; + + if (!p) return NULL; + + for (s = p, i=0; s < p+size; s++) { + if (s[0] == '\n') i++; + } + + ret = (char **)malloc(sizeof(ret[0])*(i+2)); + if (!ret) { + SAFE_FREE(p); + return NULL; + } + memset(ret, 0, sizeof(ret[0])*(i+2)); + if (numlines) *numlines = i; + + ret[0] = p; + for (s = p, i=0; s < p+size; s++) { + if (s[0] == '\n') { + s[0] = 0; + i++; + ret[i] = s+1; + } + if (s[0] == '\r') s[0] = 0; + } + + return ret; +} + + +/**************************************************************************** +load a file into memory and return an array of pointers to lines in the file +must be freed with file_lines_free(). +****************************************************************************/ +char **file_lines_load(const char *fname, int *numlines) +{ + char *p; + size_t size; + + p = file_load(fname, &size); + if (!p) return NULL; + + return file_lines_parse(p, size, numlines); +} + +/**************************************************************************** +load a fd into memory and return an array of pointers to lines in the file +must be freed with file_lines_free(). If convert is true calls unix_to_dos on +the list. +****************************************************************************/ +char **fd_lines_load(int fd, int *numlines) +{ + char *p; + size_t size; + + p = fd_load(fd, &size); + if (!p) return NULL; + + return file_lines_parse(p, size, numlines); +} + + +/**************************************************************************** +load a pipe into memory and return an array of pointers to lines in the data +must be freed with file_lines_free(). +****************************************************************************/ +char **file_lines_pload(char *syscmd, int *numlines) +{ + char *p; + size_t size; + + p = file_pload(syscmd, &size); + if (!p) return NULL; + + return file_lines_parse(p, size, numlines); +} + +/**************************************************************************** +free lines loaded with file_lines_load +****************************************************************************/ +void file_lines_free(char **lines) +{ + if (!lines) return; + SAFE_FREE(lines[0]); + SAFE_FREE(lines); +} + + +/**************************************************************************** +take a lislist of lines and modify them to produce a list where \ continues +a line +****************************************************************************/ +void file_lines_slashcont(char **lines) +{ + int i, j; + + for (i=0; lines[i];) { + int len = strlen(lines[i]); + if (lines[i][len-1] == '\\') { + lines[i][len-1] = ' '; + if (lines[i+1]) { + char *p = &lines[i][len]; + while (p < lines[i+1]) *p++ = ' '; + for (j = i+1; lines[j]; j++) lines[j] = lines[j+1]; + } + } else { + i++; + } + } +} + +/* + save a lump of data into a file. Mostly used for debugging +*/ +BOOL file_save(const char *fname, void *packet, size_t length) +{ + int fd; + fd = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0644); + if (fd == -1) { + return False; + } + if (write(fd, packet, length) != (size_t)length) { + return False; + } + close(fd); + return True; +} diff --git a/source4/lib/util_getent.c b/source4/lib/util_getent.c new file mode 100644 index 0000000000..32641dbf83 --- /dev/null +++ b/source4/lib/util_getent.c @@ -0,0 +1,306 @@ +/* + Unix SMB/CIFS implementation. + Samba utility functions + Copyright (C) Simo Sorce 2001 + Copyright (C) Jeremy Allison 2001 + + 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" + + +/**************************************************************** + Returns a single linked list of group entries. + Use grent_free() to free it after use. +****************************************************************/ + +struct sys_grent * getgrent_list(void) +{ + struct sys_grent *glist; + struct sys_grent *gent; + struct group *grp; + + gent = (struct sys_grent *) malloc(sizeof(struct sys_grent)); + if (gent == NULL) { + DEBUG (0, ("Out of memory in getgrent_list!\n")); + return NULL; + } + memset(gent, '\0', sizeof(struct sys_grent)); + glist = gent; + + setgrent(); + grp = getgrent(); + if (grp == NULL) { + endgrent(); + SAFE_FREE(glist); + return NULL; + } + + while (grp != NULL) { + int i,num; + + if (grp->gr_name) { + if ((gent->gr_name = strdup(grp->gr_name)) == NULL) + goto err; + } + if (grp->gr_passwd) { + if ((gent->gr_passwd = strdup(grp->gr_passwd)) == NULL) + goto err; + } + gent->gr_gid = grp->gr_gid; + + /* number of strings in gr_mem */ + for (num = 0; grp->gr_mem[num]; num++) + ; + + /* alloc space for gr_mem string pointers */ + if ((gent->gr_mem = (char **) malloc((num+1) * sizeof(char *))) == NULL) + goto err; + + memset(gent->gr_mem, '\0', (num+1) * sizeof(char *)); + + for (i=0; i < num; i++) { + if ((gent->gr_mem[i] = strdup(grp->gr_mem[i])) == NULL) + goto err; + } + gent->gr_mem[num] = NULL; + + grp = getgrent(); + if (grp) { + gent->next = (struct sys_grent *) malloc(sizeof(struct sys_grent)); + if (gent->next == NULL) + goto err; + gent = gent->next; + memset(gent, '\0', sizeof(struct sys_grent)); + } + } + + endgrent(); + return glist; + + err: + + endgrent(); + DEBUG(0, ("Out of memory in getgrent_list!\n")); + grent_free(glist); + return NULL; +} + +/**************************************************************** + Free the single linked list of group entries made by + getgrent_list() +****************************************************************/ + +void grent_free (struct sys_grent *glist) +{ + while (glist) { + struct sys_grent *prev; + + SAFE_FREE(glist->gr_name); + SAFE_FREE(glist->gr_passwd); + if (glist->gr_mem) { + int i; + for (i = 0; glist->gr_mem[i]; i++) + SAFE_FREE(glist->gr_mem[i]); + SAFE_FREE(glist->gr_mem); + } + prev = glist; + glist = glist->next; + SAFE_FREE(prev); + } +} + +/**************************************************************** + Returns a single linked list of passwd entries. + Use pwent_free() to free it after use. +****************************************************************/ + +struct sys_pwent * getpwent_list(void) +{ + struct sys_pwent *plist; + struct sys_pwent *pent; + struct passwd *pwd; + + pent = (struct sys_pwent *) malloc(sizeof(struct sys_pwent)); + if (pent == NULL) { + DEBUG (0, ("Out of memory in getpwent_list!\n")); + return NULL; + } + plist = pent; + + setpwent(); + pwd = getpwent(); + while (pwd != NULL) { + memset(pent, '\0', sizeof(struct sys_pwent)); + if (pwd->pw_name) { + if ((pent->pw_name = strdup(pwd->pw_name)) == NULL) + goto err; + } + if (pwd->pw_passwd) { + if ((pent->pw_passwd = strdup(pwd->pw_passwd)) == NULL) + goto err; + } + pent->pw_uid = pwd->pw_uid; + pent->pw_gid = pwd->pw_gid; + if (pwd->pw_gecos) { + if ((pent->pw_name = strdup(pwd->pw_gecos)) == NULL) + goto err; + } + if (pwd->pw_dir) { + if ((pent->pw_name = strdup(pwd->pw_dir)) == NULL) + goto err; + } + if (pwd->pw_shell) { + if ((pent->pw_name = strdup(pwd->pw_shell)) == NULL) + goto err; + } + + pwd = getpwent(); + if (pwd) { + pent->next = (struct sys_pwent *) malloc(sizeof(struct sys_pwent)); + if (pent->next == NULL) + goto err; + pent = pent->next; + } + } + + endpwent(); + return plist; + + err: + + endpwent(); + DEBUG(0, ("Out of memory in getpwent_list!\n")); + pwent_free(plist); + return NULL; +} + +/**************************************************************** + Free the single linked list of passwd entries made by + getpwent_list() +****************************************************************/ + +void pwent_free (struct sys_pwent *plist) +{ + while (plist) { + struct sys_pwent *prev; + + SAFE_FREE(plist->pw_name); + SAFE_FREE(plist->pw_passwd); + SAFE_FREE(plist->pw_gecos); + SAFE_FREE(plist->pw_dir); + SAFE_FREE(plist->pw_shell); + + prev = plist; + plist = plist->next; + SAFE_FREE(prev); + } +} + +/**************************************************************** + Add the individual group users onto the list. +****************************************************************/ + +static struct sys_userlist *add_members_to_userlist(struct sys_userlist *list_head, const struct group *grp) +{ + size_t num_users, i; + + /* Count the number of users. */ + for (num_users = 0; grp->gr_mem[num_users]; num_users++) + ; + + for (i = 0; i < num_users; i++) { + struct sys_userlist *entry = (struct sys_userlist *)malloc(sizeof(*entry)); + if (entry == NULL) { + free_userlist(list_head); + return NULL; + } + entry->unix_name = (char *)strdup(grp->gr_mem[i]); + if (entry->unix_name == NULL) { + SAFE_FREE(entry); + free_userlist(list_head); + return NULL; + } + DLIST_ADD(list_head, entry); + } + return list_head; +} + +/**************************************************************** + Get the list of UNIX users in a group. + We have to enumerate the /etc/group file as some UNIX getgrnam() + calls won't do that for us (notably Tru64 UNIX). +****************************************************************/ + +struct sys_userlist *get_users_in_group(const char *gname) +{ + struct sys_userlist *list_head = NULL; + struct group *gptr; + fstring domain; + fstring groupname; + DOM_SID sid; + enum SID_NAME_USE name_type; + + /* No point using winbind if we can't split it in the + first place */ + if (split_domain_and_name(gname, domain, groupname)) { + + /* + * If we're doing this via winbindd, don't do the + * entire group list enumeration as we know this is + * pointless (and slow). + */ + + if (winbind_lookup_name(domain, groupname, &sid, &name_type) + && name_type == SID_NAME_DOM_GRP) { + if ((gptr = (struct group *)getgrnam(gname)) == NULL) + return NULL; + return add_members_to_userlist(list_head, gptr); + } + } + +#if !defined(BROKEN_GETGRNAM) + if ((gptr = (struct group *)getgrnam(gname)) == NULL) + return NULL; + return add_members_to_userlist(list_head, gptr); +#else + /* BROKEN_GETGRNAM - True64 */ + setgrent(); + while((gptr = getgrent()) != NULL) { + if (strequal(gname, gptr->gr_name)) { + list_head = add_members_to_userlist(list_head, gptr); + if (list_head == NULL) + return NULL; + } + } + endgrent(); + return list_head; +#endif +} + +/**************************************************************** + Free list allocated above. +****************************************************************/ + +void free_userlist(struct sys_userlist *list_head) +{ + while (list_head) { + struct sys_userlist *old_head = list_head; + DLIST_REMOVE(list_head, list_head); + SAFE_FREE(old_head->unix_name); + SAFE_FREE(old_head); + } +} diff --git a/source4/lib/util_pw.c b/source4/lib/util_pw.c new file mode 100644 index 0000000000..9d075a05e8 --- /dev/null +++ b/source4/lib/util_pw.c @@ -0,0 +1,89 @@ +/* + Unix SMB/CIFS implementation. + + Safe versions of getpw* calls + + Copyright (C) Andrew Bartlett 2002 + + 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" + +static struct passwd *alloc_copy_passwd(const struct passwd *from) +{ + struct passwd *ret = smb_xmalloc(sizeof(struct passwd)); + ZERO_STRUCTP(ret); + ret->pw_name = smb_xstrdup(from->pw_name); + ret->pw_passwd = smb_xstrdup(from->pw_passwd); + ret->pw_uid = from->pw_uid; + ret->pw_gid = from->pw_gid; + ret->pw_gecos = smb_xstrdup(from->pw_gecos); + ret->pw_dir = smb_xstrdup(from->pw_dir); + ret->pw_shell = smb_xstrdup(from->pw_shell); + return ret; +} + +void passwd_free (struct passwd **buf) +{ + if (!*buf) { + DEBUG(0, ("attempted double-free of allocated passwd\n")); + return; + } + + SAFE_FREE((*buf)->pw_name); + SAFE_FREE((*buf)->pw_passwd); + SAFE_FREE((*buf)->pw_gecos); + SAFE_FREE((*buf)->pw_dir); + SAFE_FREE((*buf)->pw_shell); + + SAFE_FREE(*buf); +} + +struct passwd *getpwnam_alloc(const char *name) +{ + struct passwd *temp; + + temp = sys_getpwnam(name); + + if (!temp) { +#if 0 + if (errno == ENOMEM) { + /* what now? */ + } +#endif + return NULL; + } + + return alloc_copy_passwd(temp); +} + +struct passwd *getpwuid_alloc(uid_t uid) +{ + struct passwd *temp; + + temp = sys_getpwuid(uid); + + if (!temp) { +#if 0 + if (errno == ENOMEM) { + /* what now? */ + } +#endif + return NULL; + } + + return alloc_copy_passwd(temp); +} diff --git a/source4/lib/util_seaccess.c b/source4/lib/util_seaccess.c new file mode 100644 index 0000000000..eba8cab7fb --- /dev/null +++ b/source4/lib/util_seaccess.c @@ -0,0 +1,486 @@ +/* + Unix SMB/CIFS implementation. + Copyright (C) Luke Kenneth Casson Leighton 1996-2000. + Copyright (C) Tim Potter 2000. + Copyright (C) Re-written by Jeremy Allison 2000. + + 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" + +extern DOM_SID global_sid_Builtin; + +/********************************************************************************** + Check if this ACE has a SID in common with the token. +**********************************************************************************/ + +static BOOL token_sid_in_ace(const NT_USER_TOKEN *token, const SEC_ACE *ace) +{ + size_t i; + + for (i = 0; i < token->num_sids; i++) { + if (sid_equal(&ace->trustee, &token->user_sids[i])) + return True; + } + + return False; +} + +/********************************************************************************* + Check an ACE against a SID. We return the remaining needed permission + bits not yet granted. Zero means permission allowed (no more needed bits). +**********************************************************************************/ + +static uint32 check_ace(SEC_ACE *ace, const NT_USER_TOKEN *token, uint32 acc_desired, + NTSTATUS *status) +{ + uint32 mask = ace->info.mask; + + /* + * Inherit only is ignored. + */ + + if (ace->flags & SEC_ACE_FLAG_INHERIT_ONLY) { + return acc_desired; + } + + /* + * If this ACE has no SID in common with the token, + * ignore it as it cannot be used to make an access + * determination. + */ + + if (!token_sid_in_ace( token, ace)) + return acc_desired; + + switch (ace->type) { + case SEC_ACE_TYPE_ACCESS_ALLOWED: + /* + * This is explicitly allowed. + * Remove the bits from the remaining + * access required. Return the remaining + * bits needed. + */ + acc_desired &= ~mask; + break; + case SEC_ACE_TYPE_ACCESS_DENIED: + /* + * This is explicitly denied. + * If any bits match terminate here, + * we are denied. + */ + if (acc_desired & mask) { + *status = NT_STATUS_ACCESS_DENIED; + return 0xFFFFFFFF; + } + break; + case SEC_ACE_TYPE_SYSTEM_ALARM: + case SEC_ACE_TYPE_SYSTEM_AUDIT: + *status = NT_STATUS_NOT_IMPLEMENTED; + return 0xFFFFFFFF; + default: + *status = NT_STATUS_INVALID_PARAMETER; + return 0xFFFFFFFF; + } + + return acc_desired; +} + +/********************************************************************************* + Maximum access was requested. Calculate the max possible. Fail if it doesn't + include other bits requested. +**********************************************************************************/ + +static BOOL get_max_access( SEC_ACL *the_acl, const NT_USER_TOKEN *token, uint32 *granted, + uint32 desired, + NTSTATUS *status) +{ + uint32 acc_denied = 0; + uint32 acc_granted = 0; + size_t i; + + for ( i = 0 ; i < the_acl->num_aces; i++) { + SEC_ACE *ace = &the_acl->ace[i]; + uint32 mask = ace->info.mask; + + if (!token_sid_in_ace( token, ace)) + continue; + + switch (ace->type) { + case SEC_ACE_TYPE_ACCESS_ALLOWED: + acc_granted |= (mask & ~acc_denied); + break; + case SEC_ACE_TYPE_ACCESS_DENIED: + acc_denied |= (mask & ~acc_granted); + break; + case SEC_ACE_TYPE_SYSTEM_ALARM: + case SEC_ACE_TYPE_SYSTEM_AUDIT: + *status = NT_STATUS_NOT_IMPLEMENTED; + *granted = 0; + return False; + default: + *status = NT_STATUS_INVALID_PARAMETER; + *granted = 0; + return False; + } + } + + /* + * If we were granted no access, or we desired bits that we + * didn't get, then deny. + */ + + if ((acc_granted == 0) || ((acc_granted & desired) != desired)) { + *status = NT_STATUS_ACCESS_DENIED; + *granted = 0; + return False; + } + + /* + * Return the access we did get. + */ + + *granted = acc_granted; + *status = NT_STATUS_OK; + return True; +} + +/* Map generic access rights to object specific rights. This technique is + used to give meaning to assigning read, write, execute and all access to + objects. Each type of object has its own mapping of generic to object + specific access rights. */ + +void se_map_generic(uint32 *access_mask, struct generic_mapping *mapping) +{ + uint32 old_mask = *access_mask; + + if (*access_mask & GENERIC_READ_ACCESS) { + *access_mask &= ~GENERIC_READ_ACCESS; + *access_mask |= mapping->generic_read; + } + + if (*access_mask & GENERIC_WRITE_ACCESS) { + *access_mask &= ~GENERIC_WRITE_ACCESS; + *access_mask |= mapping->generic_write; + } + + if (*access_mask & GENERIC_EXECUTE_ACCESS) { + *access_mask &= ~GENERIC_EXECUTE_ACCESS; + *access_mask |= mapping->generic_execute; + } + + if (*access_mask & GENERIC_ALL_ACCESS) { + *access_mask &= ~GENERIC_ALL_ACCESS; + *access_mask |= mapping->generic_all; + } + + if (old_mask != *access_mask) { + DEBUG(10, ("se_map_generic(): mapped mask 0x%08x to 0x%08x\n", + old_mask, *access_mask)); + } +} + +/* Map standard access rights to object specific rights. This technique is + used to give meaning to assigning read, write, execute and all access to + objects. Each type of object has its own mapping of standard to object + specific access rights. */ + +void se_map_standard(uint32 *access_mask, struct standard_mapping *mapping) +{ + uint32 old_mask = *access_mask; + + if (*access_mask & READ_CONTROL_ACCESS) { + *access_mask &= ~READ_CONTROL_ACCESS; + *access_mask |= mapping->std_read; + } + + if (*access_mask & (DELETE_ACCESS|WRITE_DAC_ACCESS|WRITE_OWNER_ACCESS|SYNCHRONIZE_ACCESS)) { + *access_mask &= ~(DELETE_ACCESS|WRITE_DAC_ACCESS|WRITE_OWNER_ACCESS|SYNCHRONIZE_ACCESS); + *access_mask |= mapping->std_all; + } + + if (old_mask != *access_mask) { + DEBUG(10, ("se_map_standard(): mapped mask 0x%08x to 0x%08x\n", + old_mask, *access_mask)); + } +} + +/***************************************************************************** + Check access rights of a user against a security descriptor. Look at + each ACE in the security descriptor until an access denied ACE denies + any of the desired rights to the user or any of the users groups, or one + or more ACEs explicitly grant all requested access rights. See + "Access-Checking" document in MSDN. +*****************************************************************************/ + +BOOL se_access_check(const SEC_DESC *sd, const NT_USER_TOKEN *token, + uint32 acc_desired, uint32 *acc_granted, + NTSTATUS *status) +{ + extern NT_USER_TOKEN anonymous_token; + size_t i; + SEC_ACL *the_acl; + fstring sid_str; + uint32 tmp_acc_desired = acc_desired; + + if (!status || !acc_granted) + return False; + + if (!token) + token = &anonymous_token; + + *status = NT_STATUS_OK; + *acc_granted = 0; + + DEBUG(10,("se_access_check: requested access 0x%08x, for NT token with %u entries and first sid %s.\n", + (unsigned int)acc_desired, (unsigned int)token->num_sids, + sid_to_string(sid_str, &token->user_sids[0]))); + + /* + * No security descriptor or security descriptor with no DACL + * present allows all access. + */ + + /* ACL must have something in it */ + + if (!sd || (sd && (!(sd->type & SEC_DESC_DACL_PRESENT) || sd->dacl == NULL))) { + *status = NT_STATUS_OK; + *acc_granted = acc_desired; + DEBUG(5, ("se_access_check: no sd or blank DACL, access allowed\n")); + return True; + } + + /* The user sid is the first in the token */ + if (DEBUGLVL(3)) { + DEBUG(3, ("se_access_check: user sid is %s\n", sid_to_string(sid_str, &token->user_sids[PRIMARY_USER_SID_INDEX]) )); + + for (i = 1; i < token->num_sids; i++) { + DEBUGADD(3, ("se_access_check: also %s\n", + sid_to_string(sid_str, &token->user_sids[i]))); + } + } + + /* Is the token the owner of the SID ? */ + + if (sd->owner_sid) { + for (i = 0; i < token->num_sids; i++) { + if (sid_equal(&token->user_sids[i], sd->owner_sid)) { + /* + * The owner always has SEC_RIGHTS_WRITE_DAC & READ_CONTROL. + */ + if (tmp_acc_desired & WRITE_DAC_ACCESS) + tmp_acc_desired &= ~WRITE_DAC_ACCESS; + if (tmp_acc_desired & READ_CONTROL_ACCESS) + tmp_acc_desired &= ~READ_CONTROL_ACCESS; + } + } + } + + the_acl = sd->dacl; + + if (tmp_acc_desired & MAXIMUM_ALLOWED_ACCESS) { + tmp_acc_desired &= ~MAXIMUM_ALLOWED_ACCESS; + return get_max_access( the_acl, token, acc_granted, tmp_acc_desired, + status); + } + + for ( i = 0 ; i < the_acl->num_aces && tmp_acc_desired != 0; i++) { + SEC_ACE *ace = &the_acl->ace[i]; + + DEBUGADD(10,("se_access_check: ACE %u: type %d, flags = 0x%02x, SID = %s mask = %x, current desired = %x\n", + (unsigned int)i, ace->type, ace->flags, + sid_to_string(sid_str, &ace->trustee), + (unsigned int) ace->info.mask, + (unsigned int)tmp_acc_desired )); + + tmp_acc_desired = check_ace( ace, token, tmp_acc_desired, status); + if (NT_STATUS_V(*status)) { + *acc_granted = 0; + DEBUG(5,("se_access_check: ACE %u denied with status %s.\n", (unsigned int)i, nt_errstr(*status))); + return False; + } + } + + /* + * If there are no more desired permissions left then + * access was allowed. + */ + + if (tmp_acc_desired == 0) { + *acc_granted = acc_desired; + *status = NT_STATUS_OK; + DEBUG(5,("se_access_check: access (%x) granted.\n", (unsigned int)acc_desired )); + return True; + } + + *acc_granted = 0; + *status = NT_STATUS_ACCESS_DENIED; + DEBUG(5,("se_access_check: access (%x) denied.\n", (unsigned int)acc_desired )); + return False; +} + +/* Create a child security descriptor using another security descriptor as + the parent container. This child object can either be a container or + non-container object. */ + +SEC_DESC_BUF *se_create_child_secdesc(TALLOC_CTX *ctx, SEC_DESC *parent_ctr, + BOOL child_container) +{ + SEC_DESC_BUF *sdb; + SEC_DESC *sd; + SEC_ACL *new_dacl, *the_acl; + SEC_ACE *new_ace_list = NULL; + unsigned int new_ace_list_ndx = 0, i; + size_t size; + + /* Currently we only process the dacl when creating the child. The + sacl should also be processed but this is left out as sacls are + not implemented in Samba at the moment.*/ + + the_acl = parent_ctr->dacl; + + if (!(new_ace_list = talloc(ctx, sizeof(SEC_ACE) * the_acl->num_aces))) + return NULL; + + for (i = 0; the_acl && i < the_acl->num_aces; i++) { + SEC_ACE *ace = &the_acl->ace[i]; + SEC_ACE *new_ace = &new_ace_list[new_ace_list_ndx]; + uint8 new_flags = 0; + BOOL inherit = False; + fstring sid_str; + + /* The OBJECT_INHERIT_ACE flag causes the ACE to be + inherited by non-container children objects. Container + children objects will inherit it as an INHERIT_ONLY + ACE. */ + + if (ace->flags & SEC_ACE_FLAG_OBJECT_INHERIT) { + + if (!child_container) { + new_flags |= SEC_ACE_FLAG_OBJECT_INHERIT; + } else { + new_flags |= SEC_ACE_FLAG_INHERIT_ONLY; + } + + inherit = True; + } + + /* The CONAINER_INHERIT_ACE flag means all child container + objects will inherit and use the ACE. */ + + if (ace->flags & SEC_ACE_FLAG_CONTAINER_INHERIT) { + if (!child_container) { + inherit = False; + } else { + new_flags |= SEC_ACE_FLAG_CONTAINER_INHERIT; + } + } + + /* The INHERIT_ONLY_ACE is not used by the se_access_check() + function for the parent container, but is inherited by + all child objects as a normal ACE. */ + + if (ace->flags & SEC_ACE_FLAG_INHERIT_ONLY) { + /* Move along, nothing to see here */ + } + + /* The SEC_ACE_FLAG_NO_PROPAGATE_INHERIT flag means the ACE + is inherited by child objects but not grandchildren + objects. We clear the object inherit and container + inherit flags in the inherited ACE. */ + + if (ace->flags & SEC_ACE_FLAG_NO_PROPAGATE_INHERIT) { + new_flags &= ~(SEC_ACE_FLAG_OBJECT_INHERIT | + SEC_ACE_FLAG_CONTAINER_INHERIT); + } + + /* Add ACE to ACE list */ + + if (!inherit) + continue; + + init_sec_access(&new_ace->info, ace->info.mask); + init_sec_ace(new_ace, &ace->trustee, ace->type, + new_ace->info, new_flags); + + sid_to_string(sid_str, &ace->trustee); + + DEBUG(5, ("se_create_child_secdesc(): %s:%d/0x%02x/0x%08x " + " inherited as %s:%d/0x%02x/0x%08x\n", sid_str, + ace->type, ace->flags, ace->info.mask, + sid_str, new_ace->type, new_ace->flags, + new_ace->info.mask)); + + new_ace_list_ndx++; + } + + /* Create child security descriptor to return */ + + new_dacl = make_sec_acl(ctx, ACL_REVISION, new_ace_list_ndx, new_ace_list); + + /* Use the existing user and group sids. I don't think this is + correct. Perhaps the user and group should be passed in as + parameters by the caller? */ + + sd = make_sec_desc(ctx, SEC_DESC_REVISION, + parent_ctr->owner_sid, + parent_ctr->grp_sid, + parent_ctr->sacl, + new_dacl, &size); + + sdb = make_sec_desc_buf(ctx, size, sd); + + return sdb; +} + +/******************************************************************* + samr_make_sam_obj_sd + ********************************************************************/ + +NTSTATUS samr_make_sam_obj_sd(TALLOC_CTX *ctx, SEC_DESC **psd, size_t *sd_size) +{ + extern DOM_SID global_sid_World; + DOM_SID adm_sid; + DOM_SID act_sid; + + SEC_ACE ace[3]; + SEC_ACCESS mask; + + SEC_ACL *psa = NULL; + + sid_copy(&adm_sid, &global_sid_Builtin); + sid_append_rid(&adm_sid, BUILTIN_ALIAS_RID_ADMINS); + + sid_copy(&act_sid, &global_sid_Builtin); + sid_append_rid(&act_sid, BUILTIN_ALIAS_RID_ACCOUNT_OPS); + + /*basic access for every one*/ + init_sec_access(&mask, GENERIC_RIGHTS_SAM_EXECUTE | GENERIC_RIGHTS_SAM_READ); + init_sec_ace(&ace[0], &global_sid_World, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0); + + /*full access for builtin aliases Administrators and Account Operators*/ + init_sec_access(&mask, GENERIC_RIGHTS_SAM_ALL_ACCESS); + init_sec_ace(&ace[1], &adm_sid, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0); + init_sec_ace(&ace[2], &act_sid, SEC_ACE_TYPE_ACCESS_ALLOWED, mask, 0); + + 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, NULL, NULL, NULL, psa, sd_size)) == NULL) + return NT_STATUS_NO_MEMORY; + + return NT_STATUS_OK; +} diff --git a/source4/lib/util_sid.c b/source4/lib/util_sid.c new file mode 100644 index 0000000000..9910a9d261 --- /dev/null +++ b/source4/lib/util_sid.c @@ -0,0 +1,631 @@ +/* + Unix SMB/CIFS implementation. + Samba utility functions + Copyright (C) Andrew Tridgell 1992-1998 + Copyright (C) Luke Kenneth Caseson Leighton 1998-1999 + Copyright (C) Jeremy Allison 1999 + Copyright (C) Stefan (metze) Metzmacher 2002 + Copyright (C) Simo Sorce 2002 + + 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" + +/* + * Some useful sids + */ + +DOM_SID global_sid_World_Domain; /* Everyone domain */ +DOM_SID global_sid_World; /* Everyone */ +DOM_SID global_sid_Creator_Owner_Domain; /* Creator Owner domain */ +DOM_SID global_sid_NT_Authority; /* NT Authority */ +DOM_SID global_sid_System; /* System */ +DOM_SID global_sid_NULL; /* NULL sid */ +DOM_SID global_sid_Authenticated_Users; /* All authenticated rids */ +DOM_SID global_sid_Network; /* Network rids */ + +DOM_SID global_sid_Creator_Owner; /* Creator Owner */ +DOM_SID global_sid_Creator_Group; /* Creator Group */ +DOM_SID global_sid_Anonymous; /* Anonymous login */ + +DOM_SID global_sid_Builtin; /* Local well-known domain */ +DOM_SID global_sid_Builtin_Administrators; /* Builtin administrators */ +DOM_SID global_sid_Builtin_Users; /* Builtin users */ +DOM_SID global_sid_Builtin_Guests; /* Builtin guest users */ +DOM_SID global_sid_Builtin_Power_Users; /* Builtin power users */ +DOM_SID global_sid_Builtin_Account_Operators; /* Builtin account operators */ +DOM_SID global_sid_Builtin_Server_Operators; /* Builtin server operators */ +DOM_SID global_sid_Builtin_Print_Operators; /* Builtin print operators */ +DOM_SID global_sid_Builtin_Backup_Operators; /* Builtin backup operators */ +DOM_SID global_sid_Builtin_Replicator; /* Builtin replicator */ + +#define SECURITY_NULL_SID_AUTHORITY 0 +#define SECURITY_WORLD_SID_AUTHORITY 1 +#define SECURITY_LOCAL_SID_AUTHORITY 2 +#define SECURITY_CREATOR_SID_AUTHORITY 3 +#define SECURITY_NT_AUTHORITY 5 + +/* + * An NT compatible anonymous token. + */ + +static DOM_SID anon_sid_array[3]; + +NT_USER_TOKEN anonymous_token = { + 3, + anon_sid_array +}; + +static DOM_SID system_sid_array[4]; +NT_USER_TOKEN system_token = { + 1, + system_sid_array +}; + +/**************************************************************************** + Lookup string names for SID types. +****************************************************************************/ + +static const struct { + enum SID_NAME_USE sid_type; + const char *string; +} sid_name_type[] = { + {SID_NAME_USER, "User"}, + {SID_NAME_DOM_GRP, "Domain Group"}, + {SID_NAME_DOMAIN, "Domain"}, + {SID_NAME_ALIAS, "Local Group"}, + {SID_NAME_WKN_GRP, "Well-known Group"}, + {SID_NAME_DELETED, "Deleted Account"}, + {SID_NAME_INVALID, "Invalid Account"}, + {SID_NAME_UNKNOWN, "UNKNOWN"}, + + {SID_NAME_USE_NONE, NULL} +}; + +const char *sid_type_lookup(uint32 sid_type) +{ + int i = 0; + + /* Look through list */ + while(sid_name_type[i].sid_type != 0) { + if (sid_name_type[i].sid_type == sid_type) + return sid_name_type[i].string; + i++; + } + + /* Default return */ + return "SID *TYPE* is INVALID"; +} + +/**************************************************************************** + Creates some useful well known sids +****************************************************************************/ + +void generate_wellknown_sids(void) +{ + static BOOL initialised = False; + + if (initialised) + return; + + /* SECURITY_NULL_SID_AUTHORITY */ + string_to_sid(&global_sid_NULL, "S-1-0-0"); + + /* SECURITY_WORLD_SID_AUTHORITY */ + string_to_sid(&global_sid_World_Domain, "S-1-1"); + string_to_sid(&global_sid_World, "S-1-1-0"); + + /* SECURITY_CREATOR_SID_AUTHORITY */ + string_to_sid(&global_sid_Creator_Owner_Domain, "S-1-3"); + string_to_sid(&global_sid_Creator_Owner, "S-1-3-0"); + string_to_sid(&global_sid_Creator_Group, "S-1-3-1"); + + /* SECURITY_NT_AUTHORITY */ + string_to_sid(&global_sid_NT_Authority, "S-1-5"); + string_to_sid(&global_sid_Network, "S-1-5-2"); + string_to_sid(&global_sid_Anonymous, "S-1-5-7"); + string_to_sid(&global_sid_Authenticated_Users, "S-1-5-11"); + string_to_sid(&global_sid_System, "S-1-5-18"); + + /* SECURITY_BUILTIN_DOMAIN_RID */ + string_to_sid(&global_sid_Builtin, "S-1-5-32"); + string_to_sid(&global_sid_Builtin_Administrators, "S-1-5-32-544"); + string_to_sid(&global_sid_Builtin_Users, "S-1-5-32-545"); + string_to_sid(&global_sid_Builtin_Guests, "S-1-5-32-546"); + string_to_sid(&global_sid_Builtin_Power_Users, "S-1-5-32-547"); + string_to_sid(&global_sid_Builtin_Account_Operators, "S-1-5-32-548"); + string_to_sid(&global_sid_Builtin_Server_Operators, "S-1-5-32-549"); + string_to_sid(&global_sid_Builtin_Print_Operators, "S-1-5-32-550"); + string_to_sid(&global_sid_Builtin_Backup_Operators, "S-1-5-32-551"); + string_to_sid(&global_sid_Builtin_Replicator, "S-1-5-32-552"); + + /* Create the anon token. */ + sid_copy( &anonymous_token.user_sids[0], &global_sid_World); + sid_copy( &anonymous_token.user_sids[1], &global_sid_Network); + sid_copy( &anonymous_token.user_sids[2], &global_sid_Anonymous); + + /* Create the system token. */ + sid_copy( &system_token.user_sids[0], &global_sid_System); + + initialised = True; +} + +/************************************************************************** + Create the SYSTEM token. +***************************************************************************/ + +NT_USER_TOKEN *get_system_token(void) +{ + generate_wellknown_sids(); /* The token is initialised here */ + return &system_token; +} + +/************************************************************************** + Splits a name of format \DOMAIN\name or name into its two components. + Sets the DOMAIN name to lp_netbios_name() if it has not been specified. +***************************************************************************/ + +void split_domain_name(const char *fullname, char *domain, char *name) +{ + pstring full_name; + const char *sep; + char *p; + + sep = lp_winbind_separator(); + + *domain = *name = '\0'; + + if (fullname[0] == sep[0] || fullname[0] == '\\') + fullname++; + + pstrcpy(full_name, fullname); + p = strchr_m(full_name+1, '\\'); + if (!p) p = strchr_m(full_name+1, sep[0]); + + if (p != NULL) { + *p = 0; + fstrcpy(domain, full_name); + fstrcpy(name, p+1); + } else { + fstrcpy(domain, lp_netbios_name()); + fstrcpy(name, full_name); + } + + DEBUG(10,("split_domain_name:name '%s' split into domain :'%s' and user :'%s'\n", + fullname, domain, name)); +} + +/**************************************************************************** + Test if a SID is wellknown and resolvable. +****************************************************************************/ + +BOOL resolvable_wellknown_sid(DOM_SID *sid) +{ + uint32 ia = (sid->id_auth[5]) + + (sid->id_auth[4] << 8 ) + + (sid->id_auth[3] << 16) + + (sid->id_auth[2] << 24); + + if (sid->sid_rev_num != SEC_DESC_REVISION || sid->num_auths < 1) + return False; + + return (ia == SECURITY_WORLD_SID_AUTHORITY || + ia == SECURITY_CREATOR_SID_AUTHORITY); +} + +/***************************************************************** + Convert a SID to an ascii string. +*****************************************************************/ + +char *sid_to_string(fstring sidstr_out, const DOM_SID *sid) +{ + char subauth[16]; + int i; + uint32 ia; + + if (!sid) { + fstrcpy(sidstr_out, "(NULL SID)"); + return sidstr_out; + } + + /* + * BIG NOTE: this function only does SIDS where the identauth is not >= 2^32 + * in a range of 2^48. + */ + ia = (sid->id_auth[5]) + + (sid->id_auth[4] << 8 ) + + (sid->id_auth[3] << 16) + + (sid->id_auth[2] << 24); + + slprintf(sidstr_out, sizeof(fstring) - 1, "S-%u-%lu", (unsigned int)sid->sid_rev_num, (unsigned long)ia); + + for (i = 0; i < sid->num_auths; i++) { + slprintf(subauth, sizeof(subauth)-1, "-%lu", (unsigned long)sid->sub_auths[i]); + fstrcat(sidstr_out, subauth); + } + + return sidstr_out; +} + +/***************************************************************** + Useful function for debug lines. +*****************************************************************/ + +const char *sid_string_talloc(TALLOC_CTX *mem_ctx, const DOM_SID *sid) +{ + fstring tempSid; + sid_to_string(tempSid, sid); + return talloc_strdup(mem_ctx, tempSid); +} + +/***************************************************************** + Convert a string to a SID. Returns True on success, False on fail. +*****************************************************************/ + +BOOL string_to_sid(DOM_SID *sidout, const char *sidstr) +{ + pstring tok; + char *q; + const char *p; + /* BIG NOTE: this function only does SIDS where the identauth is not >= 2^32 */ + uint32 ia; + + if (StrnCaseCmp( sidstr, "S-", 2)) { + DEBUG(0,("string_to_sid: Sid %s does not start with 'S-'.\n", sidstr)); + return False; + } + + memset((char *)sidout, '\0', sizeof(DOM_SID)); + + p = q = strdup(sidstr + 2); + if (p == NULL) { + DEBUG(0, ("string_to_sid: out of memory!\n")); + return False; + } + + if (!next_token(&p, tok, "-", sizeof(tok))) { + DEBUG(0,("string_to_sid: Sid %s is not in a valid format.\n", sidstr)); + SAFE_FREE(q); + return False; + } + + /* Get the revision number. */ + sidout->sid_rev_num = (uint8)strtoul(tok, NULL, 10); + + if (!next_token(&p, tok, "-", sizeof(tok))) { + DEBUG(0,("string_to_sid: Sid %s is not in a valid format.\n", sidstr)); + SAFE_FREE(q); + return False; + } + + /* identauth in decimal should be < 2^32 */ + ia = (uint32)strtoul(tok, NULL, 10); + + /* NOTE - the ia value is in big-endian format. */ + sidout->id_auth[0] = 0; + sidout->id_auth[1] = 0; + sidout->id_auth[2] = (ia & 0xff000000) >> 24; + sidout->id_auth[3] = (ia & 0x00ff0000) >> 16; + sidout->id_auth[4] = (ia & 0x0000ff00) >> 8; + sidout->id_auth[5] = (ia & 0x000000ff); + + sidout->num_auths = 0; + + while(next_token(&p, tok, "-", sizeof(tok)) && + sidout->num_auths < MAXSUBAUTHS) { + /* + * NOTE - the subauths are in native machine-endian format. They + * are converted to little-endian when linearized onto the wire. + */ + sid_append_rid(sidout, (uint32)strtoul(tok, NULL, 10)); + } + + SAFE_FREE(q); + return True; +} + +/***************************************************************** + Add a rid to the end of a sid +*****************************************************************/ + +BOOL sid_append_rid(DOM_SID *sid, uint32 rid) +{ + if (sid->num_auths < MAXSUBAUTHS) { + sid->sub_auths[sid->num_auths++] = rid; + return True; + } + return False; +} + +/***************************************************************** + Removes the last rid from the end of a sid +*****************************************************************/ + +BOOL sid_split_rid(DOM_SID *sid, uint32 *rid) +{ + if (sid->num_auths > 0) { + sid->num_auths--; + *rid = sid->sub_auths[sid->num_auths]; + return True; + } + return False; +} + +/***************************************************************** + Return the last rid from the end of a sid +*****************************************************************/ + +BOOL sid_peek_rid(const DOM_SID *sid, uint32 *rid) +{ + if (!sid || !rid) + return False; + + if (sid->num_auths > 0) { + *rid = sid->sub_auths[sid->num_auths - 1]; + return True; + } + return False; +} + +/***************************************************************** + Return the last rid from the end of a sid + and check the sid against the exp_dom_sid +*****************************************************************/ + +BOOL sid_peek_check_rid(const DOM_SID *exp_dom_sid, const DOM_SID *sid, uint32 *rid) +{ + if (!exp_dom_sid || !sid || !rid) + return False; + + + if (sid_compare_domain(exp_dom_sid, sid)!=0){ + *rid=(-1); + return False; + } + + return sid_peek_rid(sid, rid); +} + +/***************************************************************** + Copies a sid +*****************************************************************/ + +void sid_copy(DOM_SID *dst, const DOM_SID *src) +{ + int i; + + ZERO_STRUCTP(dst); + + dst->sid_rev_num = src->sid_rev_num; + dst->num_auths = src->num_auths; + + memcpy(&dst->id_auth[0], &src->id_auth[0], sizeof(src->id_auth)); + + for (i = 0; i < src->num_auths; i++) + dst->sub_auths[i] = src->sub_auths[i]; +} + +/***************************************************************** + Write a sid out into on-the-wire format. +*****************************************************************/ + +BOOL sid_linearize(char *outbuf, size_t len, const DOM_SID *sid) +{ + size_t i; + + if (len < sid_size(sid)) + return False; + + SCVAL(outbuf,0,sid->sid_rev_num); + SCVAL(outbuf,1,sid->num_auths); + memcpy(&outbuf[2], sid->id_auth, 6); + for(i = 0; i < sid->num_auths; i++) + SIVAL(outbuf, 8 + (i*4), sid->sub_auths[i]); + + return True; +} + +/***************************************************************** + Parse a on-the-wire SID to a DOM_SID. +*****************************************************************/ + +BOOL sid_parse(const char *inbuf, size_t len, DOM_SID *sid) +{ + int i; + if (len < 8) + return False; + + ZERO_STRUCTP(sid); + + sid->sid_rev_num = CVAL(inbuf, 0); + sid->num_auths = CVAL(inbuf, 1); + memcpy(sid->id_auth, inbuf+2, 6); + if (len < 8 + sid->num_auths*4) + return False; + for (i=0;i<sid->num_auths;i++) + sid->sub_auths[i] = IVAL(inbuf, 8+i*4); + return True; +} + +/***************************************************************** + Compare the auth portion of two sids. +*****************************************************************/ + +static int sid_compare_auth(const DOM_SID *sid1, const DOM_SID *sid2) +{ + int i; + + if (sid1 == sid2) + return 0; + if (!sid1) + return -1; + if (!sid2) + return 1; + + if (sid1->sid_rev_num != sid2->sid_rev_num) + return sid1->sid_rev_num - sid2->sid_rev_num; + + for (i = 0; i < 6; i++) + if (sid1->id_auth[i] != sid2->id_auth[i]) + return sid1->id_auth[i] - sid2->id_auth[i]; + + return 0; +} + +/***************************************************************** + Compare two sids. +*****************************************************************/ + +int sid_compare(const DOM_SID *sid1, const DOM_SID *sid2) +{ + int i; + + if (sid1 == sid2) + return 0; + if (!sid1) + return -1; + if (!sid2) + return 1; + + /* Compare most likely different rids, first: i.e start at end */ + if (sid1->num_auths != sid2->num_auths) + return sid1->num_auths - sid2->num_auths; + + for (i = sid1->num_auths-1; i >= 0; --i) + if (sid1->sub_auths[i] != sid2->sub_auths[i]) + return sid1->sub_auths[i] - sid2->sub_auths[i]; + + return sid_compare_auth(sid1, sid2); +} + +/***************************************************************** + See if 2 SIDs are in the same domain + this just compares the leading sub-auths +*****************************************************************/ + +int sid_compare_domain(const DOM_SID *sid1, const DOM_SID *sid2) +{ + int n, i; + + n = MIN(sid1->num_auths, sid2->num_auths); + + for (i = n-1; i >= 0; --i) + if (sid1->sub_auths[i] != sid2->sub_auths[i]) + return sid1->sub_auths[i] - sid2->sub_auths[i]; + + return sid_compare_auth(sid1, sid2); +} + +/***************************************************************** + Compare two sids. +*****************************************************************/ + +BOOL sid_equal(const DOM_SID *sid1, const DOM_SID *sid2) +{ + return sid_compare(sid1, sid2) == 0; +} + +/***************************************************************** + Check if the SID is the builtin SID (S-1-5-32). +*****************************************************************/ + +BOOL sid_check_is_builtin(const DOM_SID *sid) +{ + return sid_equal(sid, &global_sid_Builtin); +} + +/***************************************************************** + Check if the SID is one of the builtin SIDs (S-1-5-32-a). +*****************************************************************/ + +BOOL sid_check_is_in_builtin(const DOM_SID *sid) +{ + DOM_SID dom_sid; + uint32 rid; + + sid_copy(&dom_sid, sid); + sid_split_rid(&dom_sid, &rid); + + return sid_equal(&dom_sid, &global_sid_Builtin); +} + +/***************************************************************** + 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). +*****************************************************************/ + +BOOL non_mappable_sid(DOM_SID *sid) +{ + DOM_SID dom; + uint32 rid; + + sid_copy(&dom, sid); + sid_split_rid(&dom, &rid); + + if (sid_equal(&dom, &global_sid_Builtin)) + return True; + + if (sid_equal(&dom, &global_sid_NT_Authority)) + return True; + + return False; +} + +/***************************************************************** + Return the binary string representation of a DOM_SID. + Caller must free. +*****************************************************************/ + +char *sid_binstring(const DOM_SID *sid) +{ + char *buf, *s; + int len = sid_size(sid); + buf = malloc(len); + if (!buf) + return NULL; + sid_linearize(buf, len, sid); + s = binary_string(buf, len); + free(buf); + return s; +} + + +/***************************************************************** + Print a GUID structure for debugging. +*****************************************************************/ + +void print_guid(GUID *guid) +{ + int i; + + d_printf("%08x-%04x-%04x", + IVAL(guid->info, 0), SVAL(guid->info, 4), SVAL(guid->info, 6)); + d_printf("-%02x%02x-", guid->info[8], guid->info[9]); + for (i=10;i<GUID_SIZE;i++) + d_printf("%02x", guid->info[i]); + d_printf("\n"); +} diff --git a/source4/lib/util_smbd.c b/source4/lib/util_smbd.c new file mode 100644 index 0000000000..071f20b416 --- /dev/null +++ b/source4/lib/util_smbd.c @@ -0,0 +1,65 @@ +/* + Unix SMB/CIFS implementation. + Samba utility functions, used in smbd only + Copyright (C) Andrew Tridgell 2002 + + 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" + +/* + This function requires sys_getgrouplist - which is only + available in smbd due to it's use of become_root() in a + legacy systems hack. +*/ + +/* + return a full list of groups for a user + + returns the number of groups the user is a member of. The return will include the + users primary group. + + remember to free the resulting gid_t array + + NOTE! uses become_root() to gain correct priviages on systems + that lack a native getgroups() call (uses initgroups and getgroups) +*/ +int getgroups_user(const char *user, gid_t **groups) +{ + struct passwd *pwd; + int ngrp, max_grp; + + pwd = getpwnam_alloc(user); + if (!pwd) return -1; + + max_grp = groups_max(); + (*groups) = (gid_t *)malloc(sizeof(gid_t) * max_grp); + if (! *groups) { + passwd_free(&pwd); + errno = ENOMEM; + return -1; + } + + ngrp = sys_getgrouplist(user, pwd->pw_gid, *groups, &max_grp); + if (ngrp <= 0) { + passwd_free(&pwd); + free(*groups); + return ngrp; + } + + passwd_free(&pwd); + return ngrp; +} diff --git a/source4/lib/util_sock.c b/source4/lib/util_sock.c new file mode 100644 index 0000000000..42dc04f6c8 --- /dev/null +++ b/source4/lib/util_sock.c @@ -0,0 +1,631 @@ +/* + Unix SMB/CIFS implementation. + Samba utility functions + Copyright (C) Andrew Tridgell 1992-1998 + Copyright (C) Tim Potter 2000-2001 + + 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" + + +/**************************************************************************** + Determine if a file descriptor is in fact a socket. +****************************************************************************/ +BOOL is_a_socket(int fd) +{ + int v,l; + l = sizeof(int); + return getsockopt(fd, SOL_SOCKET, SO_TYPE, (char *)&v, &l) == 0; +} + +enum SOCK_OPT_TYPES {OPT_BOOL,OPT_INT,OPT_ON}; + +typedef struct smb_socket_option { + const char *name; + int level; + int option; + int value; + int opttype; +} smb_socket_option; + +static const smb_socket_option socket_options[] = { + {"SO_KEEPALIVE", SOL_SOCKET, SO_KEEPALIVE, 0, OPT_BOOL}, + {"SO_REUSEADDR", SOL_SOCKET, SO_REUSEADDR, 0, OPT_BOOL}, + {"SO_BROADCAST", SOL_SOCKET, SO_BROADCAST, 0, OPT_BOOL}, +#ifdef TCP_NODELAY + {"TCP_NODELAY", IPPROTO_TCP, TCP_NODELAY, 0, OPT_BOOL}, +#endif +#ifdef IPTOS_LOWDELAY + {"IPTOS_LOWDELAY", IPPROTO_IP, IP_TOS, IPTOS_LOWDELAY, OPT_ON}, +#endif +#ifdef IPTOS_THROUGHPUT + {"IPTOS_THROUGHPUT", IPPROTO_IP, IP_TOS, IPTOS_THROUGHPUT, OPT_ON}, +#endif +#ifdef SO_REUSEPORT + {"SO_REUSEPORT", SOL_SOCKET, SO_REUSEPORT, 0, OPT_BOOL}, +#endif +#ifdef SO_SNDBUF + {"SO_SNDBUF", SOL_SOCKET, SO_SNDBUF, 0, OPT_INT}, +#endif +#ifdef SO_RCVBUF + {"SO_RCVBUF", SOL_SOCKET, SO_RCVBUF, 0, OPT_INT}, +#endif +#ifdef SO_SNDLOWAT + {"SO_SNDLOWAT", SOL_SOCKET, SO_SNDLOWAT, 0, OPT_INT}, +#endif +#ifdef SO_RCVLOWAT + {"SO_RCVLOWAT", SOL_SOCKET, SO_RCVLOWAT, 0, OPT_INT}, +#endif +#ifdef SO_SNDTIMEO + {"SO_SNDTIMEO", SOL_SOCKET, SO_SNDTIMEO, 0, OPT_INT}, +#endif +#ifdef SO_RCVTIMEO + {"SO_RCVTIMEO", SOL_SOCKET, SO_RCVTIMEO, 0, OPT_INT}, +#endif + {NULL,0,0,0,0}}; + +/**************************************************************************** + Print socket options. +****************************************************************************/ + +static void print_socket_options(int s) +{ + int value, vlen = 4; + const smb_socket_option *p = &socket_options[0]; + + for (; p->name != NULL; p++) { + if (getsockopt(s, p->level, p->option, (void *)&value, &vlen) == -1) { + DEBUG(5,("Could not test socket option %s.\n", p->name)); + } else { + DEBUG(5,("socket option %s = %d\n",p->name,value)); + } + } + } + +/**************************************************************************** + Set user socket options. +****************************************************************************/ + +void set_socket_options(int fd, const char *options) +{ + fstring tok; + + while (next_token(&options,tok," \t,", sizeof(tok))) { + int ret=0,i; + int value = 1; + char *p; + BOOL got_value = False; + + if ((p = strchr_m(tok,'='))) { + *p = 0; + value = atoi(p+1); + got_value = True; + } + + for (i=0;socket_options[i].name;i++) + if (strequal(socket_options[i].name,tok)) + break; + + if (!socket_options[i].name) { + DEBUG(0,("Unknown socket option %s\n",tok)); + continue; + } + + switch (socket_options[i].opttype) { + case OPT_BOOL: + case OPT_INT: + ret = setsockopt(fd,socket_options[i].level, + socket_options[i].option,(char *)&value,sizeof(int)); + break; + + case OPT_ON: + if (got_value) + DEBUG(0,("syntax error - %s does not take a value\n",tok)); + + { + int on = socket_options[i].value; + ret = setsockopt(fd,socket_options[i].level, + socket_options[i].option,(char *)&on,sizeof(int)); + } + break; + } + + if (ret != 0) + DEBUG(0,("Failed to set socket option %s (Error %s)\n",tok, strerror(errno) )); + } + + print_socket_options(fd); +} + +/**************************************************************************** + Read from a socket. +****************************************************************************/ + +ssize_t read_udp_socket(int fd, char *buf, size_t len, + struct in_addr *from_addr, int *from_port) +{ + ssize_t ret; + struct sockaddr_in sock; + socklen_t socklen = sizeof(sock); + + ret = (ssize_t)sys_recvfrom(fd,buf,len, 0, (struct sockaddr *)&sock, &socklen); + if (ret <= 0) { + DEBUG(2,("read socket failed. ERRNO=%s\n",strerror(errno))); + return 0; + } + + if (from_addr) { + *from_addr = sock.sin_addr; + } + if (from_port) { + *from_port = ntohs(sock.sin_port); + } + + return ret; +} + + +/**************************************************************************** + read data from the client, reading exactly N bytes. +****************************************************************************/ +ssize_t read_data(int fd, char *buffer, size_t N) +{ + ssize_t ret; + size_t total=0; + + while (total < N) { + ret = sys_read(fd,buffer + total,N - total); + if (ret == 0) { + return 0; + } + if (ret == -1) { + return -1; + } + total += ret; + } + return (ssize_t)total; +} + + +/**************************************************************************** + Write data to a fd. +****************************************************************************/ +ssize_t write_data(int fd, const char *buffer, size_t N) +{ + size_t total=0; + ssize_t ret; + + while (total < N) { + ret = sys_write(fd, buffer + total, N - total); + if (ret == -1) { + return -1; + } + if (ret == 0) + return total; + + total += ret; + } + return (ssize_t)total; +} + + +/**************************************************************************** +send a keepalive packet (rfc1002) +****************************************************************************/ +BOOL send_nbt_keepalive(int sock_fd) +{ + unsigned char buf[4]; + + buf[0] = SMBkeepalive; + buf[1] = buf[2] = buf[3] = 0; + + return write_data(sock_fd,(char *)buf,4) == 4; +} + + +/**************************************************************************** + Open a socket of the specified type, port, and address for incoming data. +****************************************************************************/ +int open_socket_in( int type, int port, int dlevel, uint32 socket_addr, BOOL rebind ) +{ + struct sockaddr_in sock; + int res; + + memset( (char *)&sock, '\0', sizeof(sock) ); + +#ifdef HAVE_SOCK_SIN_LEN + sock.sin_len = sizeof(sock); +#endif + sock.sin_port = htons( port ); + sock.sin_family = AF_INET; + sock.sin_addr.s_addr = socket_addr; + + res = socket( AF_INET, type, 0 ); + if( res == -1 ) { + DEBUG(0,("open_socket_in(): socket() call failed: %s\n", strerror(errno))); + return -1; + } + + /* This block sets/clears the SO_REUSEADDR and possibly SO_REUSEPORT. */ + { + int val = rebind ? 1 : 0; + setsockopt(res,SOL_SOCKET,SO_REUSEADDR,(char *)&val,sizeof(val)); +#ifdef SO_REUSEPORT + setsockopt(res,SOL_SOCKET,SO_REUSEPORT,(char *)&val,sizeof(val)); +#endif + } + + /* now we've got a socket - we need to bind it */ + if( bind( res, (struct sockaddr *)&sock, sizeof(sock) ) == -1 ) { + DEBUG(0,("bind failed on port %d - %s\n", port, strerror(errno))); + close( res ); + return( -1 ); + } + + DEBUG( 10, ( "bind succeeded on port %d\n", port ) ); + + return( res ); + } + + +/**************************************************************************** + create an outgoing socket. timeout is in milliseconds. + **************************************************************************/ +int open_socket_out(int type, struct in_addr *addr, int port, int timeout) +{ + struct sockaddr_in sock_out; + int res,ret; + int connect_loop = 250; /* 250 milliseconds */ + int loops = (timeout) / connect_loop; + + /* create a socket to write to */ + res = socket(PF_INET, type, 0); + if (res == -1) + { DEBUG(0,("socket error\n")); return -1; } + + if (type != SOCK_STREAM) return(res); + + memset((char *)&sock_out,'\0',sizeof(sock_out)); + putip((char *)&sock_out.sin_addr,(char *)addr); + + sock_out.sin_port = htons( port ); + sock_out.sin_family = PF_INET; + + /* set it non-blocking */ + set_blocking(res,False); + + DEBUG(3,("Connecting to %s at port %d\n",inet_ntoa(*addr),port)); + + /* and connect it to the destination */ +connect_again: + ret = connect(res,(struct sockaddr *)&sock_out,sizeof(sock_out)); + + /* Some systems return EAGAIN when they mean EINPROGRESS */ + if (ret < 0 && (errno == EINPROGRESS || errno == EALREADY || + errno == EAGAIN) && loops--) { + msleep(connect_loop); + goto connect_again; + } + + if (ret < 0 && (errno == EINPROGRESS || errno == EALREADY || + errno == EAGAIN)) { + DEBUG(1,("timeout connecting to %s:%d\n",inet_ntoa(*addr),port)); + close(res); + return -1; + } + +#ifdef EISCONN + if (ret < 0 && errno == EISCONN) { + errno = 0; + ret = 0; + } +#endif + + if (ret < 0) { + DEBUG(2,("error connecting to %s:%d (%s)\n", + inet_ntoa(*addr),port,strerror(errno))); + close(res); + return -1; + } + + /* set it blocking again */ + set_blocking(res,True); + + return res; +} + +/* + open a connected UDP socket to host on port +*/ +int open_udp_socket(const char *host, int port) +{ + int type = SOCK_DGRAM; + struct sockaddr_in sock_out; + int res; + struct in_addr *addr; + TALLOC_CTX *mem_ctx; + + mem_ctx = talloc_init("open_udp_socket"); + if (!mem_ctx) { + return -1; + } + addr = interpret_addr2(mem_ctx, host); + + res = socket(PF_INET, type, 0); + if (res == -1) { + return -1; + } + + memset((char *)&sock_out,'\0',sizeof(sock_out)); + putip((char *)&sock_out.sin_addr,(char *)addr); + sock_out.sin_port = htons(port); + sock_out.sin_family = PF_INET; + + talloc_destroy(mem_ctx); + + if (connect(res,(struct sockaddr *)&sock_out,sizeof(sock_out))) { + close(res); + return -1; + } + + return res; +} + + +/******************************************************************* + matchname - determine if host name matches IP address. Used to + confirm a hostname lookup to prevent spoof attacks + ******************************************************************/ +static BOOL matchname(char *remotehost, struct in_addr addr) +{ + struct hostent *hp; + int i; + + if ((hp = sys_gethostbyname(remotehost)) == 0) { + DEBUG(0,("sys_gethostbyname(%s): lookup failure.\n", remotehost)); + return False; + } + + /* + * Make sure that gethostbyname() returns the "correct" host name. + * Unfortunately, gethostbyname("localhost") sometimes yields + * "localhost.domain". Since the latter host name comes from the + * local DNS, we just have to trust it (all bets are off if the local + * DNS is perverted). We always check the address list, though. + */ + + if (strcasecmp(remotehost, hp->h_name) + && strcasecmp(remotehost, "localhost")) { + DEBUG(0,("host name/name mismatch: %s != %s\n", + remotehost, hp->h_name)); + return False; + } + + /* Look up the host address in the address list we just got. */ + for (i = 0; hp->h_addr_list[i]; i++) { + if (memcmp(hp->h_addr_list[i], (char *) & addr, sizeof(addr)) == 0) + return True; + } + + /* + * The host name does not map to the original host address. Perhaps + * someone has compromised a name server. More likely someone botched + * it, but that could be dangerous, too. + */ + + DEBUG(0,("host name/address mismatch: %s != %s\n", + inet_ntoa(addr), hp->h_name)); + return False; +} + + +/******************************************************************* + return the DNS name of the remote end of a socket + ******************************************************************/ +char *get_socket_name(TALLOC_CTX *mem_ctx, int fd, BOOL force_lookup) +{ + char *name_buf; + char *addr_buf; + struct hostent *hp; + struct in_addr addr; + char *p; + + /* reverse lookups can be *very* expensive, and in many + situations won't work because many networks don't link dhcp + with dns. To avoid the delay we avoid the lookup if + possible */ + if (!lp_hostname_lookups() && (force_lookup == False)) { + return get_socket_addr(mem_ctx, fd); + } + + p = get_socket_addr(mem_ctx, fd); + + name_buf = talloc_strdup(mem_ctx, "UNKNOWN"); + if (fd == -1) return name_buf; + + addr_buf = talloc_strdup(mem_ctx, p); + + addr = *interpret_addr2(mem_ctx, p); + + /* Look up the remote host name. */ + if ((hp = gethostbyaddr((char *)&addr.s_addr, sizeof(addr.s_addr), AF_INET)) == 0) { + DEBUG(1,("Gethostbyaddr failed for %s\n",p)); + name_buf = talloc_strdup(mem_ctx, p); + } else { + name_buf = talloc_strdup(mem_ctx, (char *)hp->h_name); + if (!matchname(name_buf, addr)) { + DEBUG(0,("Matchname failed on %s %s\n",name_buf,p)); + name_buf = talloc_strdup(mem_ctx, "UNKNOWN"); + } + } + + alpha_strcpy(name_buf, name_buf, "_-.", sizeof(name_buf)); + if (strstr(name_buf,"..")) { + name_buf = talloc_strdup(mem_ctx, "UNKNOWN"); + } + + return name_buf; +} + +/******************************************************************* + return the IP addr of the remote end of a socket as a string + ******************************************************************/ +char *get_socket_addr(TALLOC_CTX *mem_ctx, int fd) +{ + struct sockaddr sa; + struct sockaddr_in *sockin = (struct sockaddr_in *) (&sa); + int length = sizeof(sa); + char *addr_buf; + + addr_buf = talloc_strdup(mem_ctx, "0.0.0.0"); + + if (fd == -1) { + return addr_buf; + } + + if (getpeername(fd, &sa, &length) < 0) { + DEBUG(0,("getpeername failed. Error was %s\n", strerror(errno) )); + return addr_buf; + } + + addr_buf = talloc_strdup(mem_ctx, (char *)inet_ntoa(sockin->sin_addr)); + + return addr_buf; +} + + + +/******************************************************************* +this is like socketpair but uses tcp. It is used by the Samba +regression test code +The function guarantees that nobody else can attach to the socket, +or if they do that this function fails and the socket gets closed +returns 0 on success, -1 on failure +the resulting file descriptors are symmetrical + ******************************************************************/ +static int socketpair_tcp(int fd[2]) +{ + int listener; + struct sockaddr_in sock; + struct sockaddr_in sock2; + socklen_t socklen = sizeof(sock); + int connect_done = 0; + + fd[0] = fd[1] = listener = -1; + + memset(&sock, 0, sizeof(sock)); + + if ((listener = socket(PF_INET, SOCK_STREAM, 0)) == -1) goto failed; + + memset(&sock2, 0, sizeof(sock2)); +#ifdef HAVE_SOCK_SIN_LEN + sock2.sin_len = sizeof(sock2); +#endif + sock2.sin_family = PF_INET; + + bind(listener, (struct sockaddr *)&sock2, sizeof(sock2)); + + if (listen(listener, 1) != 0) goto failed; + + if (getsockname(listener, (struct sockaddr *)&sock, &socklen) != 0) goto failed; + + if ((fd[1] = socket(PF_INET, SOCK_STREAM, 0)) == -1) goto failed; + + set_blocking(fd[1], 0); + + sock.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + + if (connect(fd[1],(struct sockaddr *)&sock,sizeof(sock)) == -1) { + if (errno != EINPROGRESS) goto failed; + } else { + connect_done = 1; + } + + if ((fd[0] = accept(listener, (struct sockaddr *)&sock, &socklen)) == -1) goto failed; + + close(listener); + if (connect_done == 0) { + if (connect(fd[1],(struct sockaddr *)&sock,sizeof(sock)) != 0 + && errno != EISCONN) goto failed; + } + + set_blocking(fd[1], 1); + + /* all OK! */ + return 0; + + failed: + if (fd[0] != -1) close(fd[0]); + if (fd[1] != -1) close(fd[1]); + if (listener != -1) close(listener); + return -1; +} + + +/******************************************************************* +run a program on a local tcp socket, this is used to launch smbd +when regression testing +the return value is a socket which is attached to a subprocess +running "prog". stdin and stdout are attached. stderr is left +attached to the original stderr + ******************************************************************/ +int sock_exec(const char *prog) +{ + int fd[2]; + if (socketpair_tcp(fd) != 0) { + DEBUG(0,("socketpair_tcp failed (%s)\n", strerror(errno))); + return -1; + } + if (fork() == 0) { + close(fd[0]); + close(0); + close(1); + dup(fd[1]); + dup(fd[1]); + exit(system(prog)); + } + close(fd[1]); + return fd[0]; +} + + +/* + determine if a packet is pending for receive on a socket +*/ +BOOL socket_pending(int fd) +{ + fd_set fds; + int selrtn; + struct timeval timeout; + + FD_ZERO(&fds); + FD_SET(fd,&fds); + + /* immediate timeout */ + timeout.tv_sec = 0; + timeout.tv_usec = 0; + + /* yes, this is supposed to be a normal select not a sys_select() */ + selrtn = select(fd+1,&fds,NULL,NULL,&timeout); + + if (selrtn == 1) { + /* the fd is readable */ + return True; + } + + return False; +} diff --git a/source4/lib/util_str.c b/source4/lib/util_str.c new file mode 100644 index 0000000000..19857cff86 --- /dev/null +++ b/source4/lib/util_str.c @@ -0,0 +1,1619 @@ +/* + Unix SMB/CIFS implementation. + Samba utility functions + Copyright (C) Andrew Tridgell 1992-2001 + Copyright (C) Simo Sorce 2001-2002 + + 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" + +/** + * Get the next token from a string, return False if none found. + * Handles double-quotes. + * + * Based on a routine by GJC@VILLAGE.COM. + * Extensively modified by Andrew.Tridgell@anu.edu.au + **/ +BOOL next_token(const char **ptr,char *buff, const char *sep, size_t bufsize) +{ + const char *s; + BOOL quoted; + size_t len=1; + + if (!ptr) + return(False); + + s = *ptr; + + /* default to simple separators */ + if (!sep) + sep = " \t\n\r"; + + /* find the first non sep char */ + while (*s && strchr_m(sep,*s)) + s++; + + /* nothing left? */ + if (! *s) + return(False); + + /* copy over the token */ + for (quoted = False; len < bufsize && *s && (quoted || !strchr_m(sep,*s)); s++) { + if (*s == '\"') { + quoted = !quoted; + } else { + len++; + *buff++ = *s; + } + } + + *ptr = (*s) ? s+1 : s; + *buff = 0; + + return(True); +} + +/** +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 char *last_ptr=NULL; + +BOOL next_token_nr(const char **ptr, char *buff, const char *sep, size_t bufsize) +{ + BOOL ret; + if (!ptr) + ptr = (const char **)&last_ptr; + + ret = next_token(ptr, buff, sep, bufsize); + last_ptr = *ptr; + return ret; +} + +static uint16 tmpbuf[sizeof(pstring)]; + +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=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=last_ptr; + + if (!(ret=iret=malloc(ictok*sizeof(char *)))) + return NULL; + + while(ictok--) { + *iret++=s; + while(*s++) + ; + while(!*s) + s++; + } + + return ret; +} + +/** + Case insensitive string compararison. +**/ + +int StrCaseCmp(const char *s, const char *t) +{ + pstring buf1, buf2; + unix_strupper(s, strlen(s)+1, buf1, sizeof(buf1)); + unix_strupper(t, strlen(t)+1, buf2, sizeof(buf2)); + return strcmp(buf1,buf2); +} + +/** + Case insensitive string compararison, length limited. +**/ + +int StrnCaseCmp(const char *s, const char *t, size_t n) +{ + pstring buf1, buf2; + unix_strupper(s, strlen(s)+1, buf1, sizeof(buf1)); + unix_strupper(t, strlen(t)+1, buf2, sizeof(buf2)); + return strncmp(buf1,buf2,n); +} + +/** + * Compare 2 strings. + * + * @note The comparison is case-insensitive. + **/ +BOOL strequal(const char *s1, const char *s2) +{ + if (s1 == s2) + return(True); + if (!s1 || !s2) + return(False); + + return(StrCaseCmp(s1,s2)==0); +} + +/** + * Compare 2 strings up to and including the nth char. + * + * @note The comparison is case-insensitive. + **/ +BOOL strnequal(const char *s1,const char *s2,size_t n) +{ + if (s1 == s2) + return(True); + if (!s1 || !s2 || !n) + return(False); + + return(StrnCaseCmp(s1,s2,n)==0); +} + +/** + Compare 2 strings (case sensitive). +**/ + +BOOL strcsequal(const char *s1,const char *s2) +{ + if (s1 == s2) + return(True); + if (!s1 || !s2) + return(False); + + return(strcmp(s1,s2)==0); +} + +/** +Do a case-insensitive, whitespace-ignoring string compare. +**/ + +int strwicmp(const char *psz1, const char *psz2) +{ + /* if BOTH strings are NULL, return TRUE, if ONE is NULL return */ + /* appropriate value. */ + if (psz1 == psz2) + return (0); + else if (psz1 == NULL) + return (-1); + else if (psz2 == NULL) + return (1); + + /* sync the strings on first non-whitespace */ + while (1) { + while (isspace((int)*psz1)) + psz1++; + while (isspace((int)*psz2)) + psz2++; + if (toupper(*psz1) != toupper(*psz2) || *psz1 == '\0' + || *psz2 == '\0') + break; + psz1++; + psz2++; + } + return (*psz1 - *psz2); +} + +/** + Convert a string to upper case, but don't modify it. +**/ + +char *strupper_talloc(TALLOC_CTX *mem_ctx, const char *s) +{ + char *str; + + str = talloc_strdup(mem_ctx, s); + strupper(str); + + return str; +} + + +/** + String replace. + NOTE: oldc and newc must be 7 bit characters +**/ + +void string_replace(char *s,char oldc,char newc) +{ + if (strchr(s, oldc)) { + push_ucs2(NULL, tmpbuf,s, sizeof(tmpbuf), STR_TERMINATE); + string_replace_w(tmpbuf, UCS2_CHAR(oldc), UCS2_CHAR(newc)); + pull_ucs2(NULL, s, tmpbuf, strlen(s)+1, sizeof(tmpbuf), STR_TERMINATE); + } +} + +/** + Skip past some strings in a buffer. +**/ + +char *skip_string(char *buf,size_t n) +{ + while (n--) + buf += strlen(buf) + 1; + return(buf); +} + +/** + Count the number of characters in a string. Normally this will + be the same as the number of bytes in a string for single byte strings, + but will be different for multibyte. +**/ + +size_t str_charnum(const char *s) +{ + uint16 tmpbuf2[sizeof(pstring)]; + push_ucs2(NULL, tmpbuf2,s, sizeof(tmpbuf2), STR_TERMINATE); + return strlen_w(tmpbuf2); +} + +/** + Count the number of characters in a string. Normally this will + be the same as the number of bytes in a string for single byte strings, + but will be different for multibyte. +**/ + +size_t str_ascii_charnum(const char *s) +{ + pstring tmpbuf2; + push_ascii(tmpbuf2, s, sizeof(tmpbuf2), STR_TERMINATE); + return strlen(tmpbuf2); +} + +/** + Trim the specified elements off the front and back of a string. +**/ + +BOOL trim_string(char *s,const char *front,const char *back) +{ + BOOL ret = False; + size_t front_len; + size_t back_len; + size_t len; + + /* Ignore null or empty strings. */ + if (!s || (s[0] == '\0')) + return False; + + front_len = front? strlen(front) : 0; + back_len = back? strlen(back) : 0; + + len = strlen(s); + + if (front_len) { + while (len && strncmp(s, front, front_len)==0) { + memcpy(s, s+front_len, (len-front_len)+1); + len -= front_len; + ret=True; + } + } + + if (back_len) { + while ((len >= back_len) && strncmp(s+len-back_len,back,back_len)==0) { + s[len-back_len]='\0'; + len -= back_len; + ret=True; + } + } + return ret; +} + +/** + Does a string have any uppercase chars in it? +**/ + +BOOL strhasupper(const char *s) +{ + smb_ucs2_t *ptr; + push_ucs2(NULL, tmpbuf,s, sizeof(tmpbuf), STR_TERMINATE); + for(ptr=tmpbuf;*ptr;ptr++) + if(isupper_w(*ptr)) + return True; + return(False); +} + +/** + Does a string have any lowercase chars in it? +**/ + +BOOL strhaslower(const char *s) +{ + smb_ucs2_t *ptr; + push_ucs2(NULL, tmpbuf,s, sizeof(tmpbuf), STR_TERMINATE); + for(ptr=tmpbuf;*ptr;ptr++) + if(islower_w(*ptr)) + return True; + return(False); +} + +/** + Find the number of 'c' chars in a string +**/ + +size_t count_chars(const char *s,char c) +{ + smb_ucs2_t *ptr; + int count; + push_ucs2(NULL, tmpbuf,s, sizeof(tmpbuf), STR_TERMINATE); + for(count=0,ptr=tmpbuf;*ptr;ptr++) + if(*ptr==UCS2_CHAR(c)) + count++; + return(count); +} + +/** +Return True if a string consists only of one particular character. +**/ + +BOOL str_is_all(const char *s,char c) +{ + smb_ucs2_t *ptr; + + if(s == NULL) + return False; + if(!*s) + return False; + + push_ucs2(NULL, tmpbuf,s, sizeof(tmpbuf), STR_TERMINATE); + for(ptr=tmpbuf;*ptr;ptr++) + if(*ptr!=UCS2_CHAR(c)) + return False; + + return True; +} + +/** + Safe string copy into a known length string. maxlength does not + include the terminating zero. +**/ + +char *safe_strcpy(char *dest,const char *src, size_t maxlength) +{ + size_t len; + + if (!dest) { + DEBUG(0,("ERROR: NULL dest in safe_strcpy\n")); + return NULL; + } + +#ifdef DEVELOPER + /* We intentionally write out at the extremity of the destination + * string. If the destination is too short (e.g. pstrcpy into mallocd + * or fstring) then this should cause an error under a memory + * checker. */ + dest[maxlength] = '\0'; + if (PTR_DIFF(&len, dest) > 0) { /* check if destination is on the stack, ok if so */ + log_suspicious_usage("safe_strcpy", src); + } +#endif + + if (!src) { + *dest = 0; + return dest; + } + + len = strlen(src); + + if (len > maxlength) { + DEBUG(0,("ERROR: string overflow by %u (%u - %u) in safe_strcpy [%.50s]\n", + (unsigned int)(len-maxlength), len, maxlength, src)); + len = maxlength; + } + + memmove(dest, src, len); + dest[len] = 0; + return dest; +} + +/** + Safe string cat into a string. maxlength does not + include the terminating zero. +**/ + +char *safe_strcat(char *dest, const char *src, size_t maxlength) +{ + size_t src_len, dest_len; + + if (!dest) { + DEBUG(0,("ERROR: NULL dest in safe_strcat\n")); + return NULL; + } + + if (!src) + return dest; + +#ifdef DEVELOPER + if (PTR_DIFF(&src_len, dest) > 0) { /* check if destination is on the stack, ok if so */ + log_suspicious_usage("safe_strcat", src); + } +#endif + src_len = strlen(src); + dest_len = strlen(dest); + + if (src_len + dest_len > maxlength) { + DEBUG(0,("ERROR: string overflow by %d in safe_strcat [%.50s]\n", + (int)(src_len + dest_len - maxlength), src)); + if (maxlength > dest_len) { + memcpy(&dest[dest_len], src, maxlength - dest_len); + } + dest[maxlength] = 0; + return NULL; + } + + memcpy(&dest[dest_len], src, src_len); + dest[dest_len + src_len] = 0; + return dest; +} + +/** + Paranoid strcpy into a buffer of given length (includes terminating + zero. Strips out all but 'a-Z0-9' and the character in other_safe_chars + and replaces with '_'. Deliberately does *NOT* check for multibyte + characters. Don't change it ! +**/ + +char *alpha_strcpy(char *dest, const char *src, const char *other_safe_chars, size_t maxlength) +{ + size_t len, i; + + if (maxlength == 0) { + /* can't fit any bytes at all! */ + return NULL; + } + + if (!dest) { + DEBUG(0,("ERROR: NULL dest in alpha_strcpy\n")); + return NULL; + } + + if (!src) { + *dest = 0; + return dest; + } + + len = strlen(src); + if (len >= maxlength) + len = maxlength - 1; + + if (!other_safe_chars) + other_safe_chars = ""; + + for(i = 0; i < len; i++) { + int val = (src[i] & 0xff); + if (isupper(val) || islower(val) || isdigit(val) || strchr_m(other_safe_chars, val)) + dest[i] = src[i]; + else + dest[i] = '_'; + } + + dest[i] = '\0'; + + return dest; +} + +/** + Like strncpy but always null terminates. Make sure there is room! + The variable n should always be one less than the available size. +**/ + +char *StrnCpy(char *dest,const char *src,size_t n) +{ + char *d = dest; + if (!dest) + return(NULL); + if (!src) { + *dest = 0; + return(dest); + } + while (n-- && (*d++ = *src++)) + ; + *d = 0; + return(dest); +} + +/** + Like strncpy but copies up to the character marker. always null terminates. + returns a pointer to the character marker in the source string (src). +**/ + +char *strncpyn(char *dest, const char *src, size_t n, char c) +{ + char *p; + size_t str_len; + + p = strchr_m(src, c); + if (p == NULL) { + DEBUG(5, ("strncpyn: separator character (%c) not found\n", c)); + return NULL; + } + + str_len = PTR_DIFF(p, src); + strncpy(dest, src, MIN(n, str_len)); + dest[str_len] = '\0'; + + return p; +} + +/** + Routine to get hex characters and turn them into a 16 byte array. + the array can be variable length, and any non-hex-numeric + characters are skipped. "0xnn" or "0Xnn" is specially catered + for. + + valid examples: "0A5D15"; "0x15, 0x49, 0xa2"; "59\ta9\te3\n" + +**/ + +size_t strhex_to_str(char *p, size_t len, const char *strhex) +{ + size_t i; + size_t num_chars = 0; + unsigned char lonybble, hinybble; + const char *hexchars = "0123456789ABCDEF"; + char *p1 = NULL, *p2 = NULL; + + for (i = 0; i < len && strhex[i] != 0; i++) { + if (strnequal(hexchars, "0x", 2)) { + i++; /* skip two chars */ + continue; + } + + if (!(p1 = strchr_m(hexchars, toupper(strhex[i])))) + break; + + i++; /* next hex digit */ + + if (!(p2 = strchr_m(hexchars, toupper(strhex[i])))) + break; + + /* get the two nybbles */ + hinybble = PTR_DIFF(p1, hexchars); + lonybble = PTR_DIFF(p2, hexchars); + + p[num_chars] = (hinybble << 4) | lonybble; + num_chars++; + + p1 = NULL; + p2 = NULL; + } + return num_chars; +} + +/** + Check if a string is part of a list. +**/ + +BOOL in_list(const char *s, const char *list, BOOL casesensitive) +{ + pstring tok; + const char *p=list; + + if (!list) + return(False); + + while (next_token(&p,tok,LIST_SEP,sizeof(tok))) { + if (casesensitive) { + if (strcmp(tok,s) == 0) + return(True); + } else { + if (StrCaseCmp(tok,s) == 0) + return(True); + } + } + return(False); +} + +/** + Set a string value, allocing the space for the string +**/ +static BOOL string_init(char **dest,const char *src) +{ + size_t l; + if (!src) src = ""; + + l = strlen(src); + + (*dest) = strdup(src); + if ((*dest) == NULL) { + DEBUG(0,("Out of memory in string_init\n")); + return False; + } + return True; +} + +/** + Free a string value. +**/ +void string_free(char **s) +{ + if (s) SAFE_FREE(*s); +} + +/** + Set a string value, deallocating any existing space, and allocing the space + for the string +**/ +BOOL string_set(char **dest, const char *src) +{ + string_free(dest); + return string_init(dest,src); +} + +/** + Substitute a string for a pattern in another string. Make sure there is + enough room! + + This routine looks for pattern in s and replaces it with + insert. It may do multiple replacements. + + Any of " ; ' $ or ` in the insert string are replaced with _ + if len==0 then the string cannot be extended. This is different from the old + use of len==0 which was for no length checks to be done. +**/ + +void string_sub(char *s,const char *pattern, const char *insert, size_t len) +{ + char *p; + ssize_t ls,lp,li, i; + + if (!insert || !pattern || !*pattern || !s) + return; + + ls = (ssize_t)strlen(s); + lp = (ssize_t)strlen(pattern); + li = (ssize_t)strlen(insert); + + if (len == 0) + len = ls + 1; /* len is number of *bytes* */ + + while (lp <= ls && (p = strstr(s,pattern))) { + if (ls + (li-lp) >= len) { + DEBUG(0,("ERROR: string overflow by %d in string_sub(%.50s, %d)\n", + (int)(ls + (li-lp) - len), + pattern, (int)len)); + break; + } + if (li != lp) { + memmove(p+li,p+lp,strlen(p+lp)+1); + } + for (i=0;i<li;i++) { + switch (insert[i]) { + case '`': + case '"': + case '\'': + case ';': + case '$': + case '%': + case '\r': + case '\n': + p[i] = '_'; + break; + default: + p[i] = insert[i]; + } + } + s = p + li; + ls += (li-lp); + } +} + +void fstring_sub(char *s,const char *pattern,const char *insert) +{ + string_sub(s, pattern, insert, sizeof(fstring)); +} + +void pstring_sub(char *s,const char *pattern,const char *insert) +{ + string_sub(s, pattern, insert, sizeof(pstring)); +} + +/** + Similar to string_sub, but it will accept only allocated strings + and may realloc them so pay attention at what you pass on no + pointers inside strings, no pstrings or const may be passed + as string. +**/ + +char *realloc_string_sub(char *string, const char *pattern, const char *insert) +{ + char *p, *in; + char *s; + ssize_t ls,lp,li,ld, i; + + if (!insert || !pattern || !*pattern || !string || !*string) + return NULL; + + s = string; + + in = strdup(insert); + if (!in) { + DEBUG(0, ("realloc_string_sub: out of memory!\n")); + return NULL; + } + ls = (ssize_t)strlen(s); + lp = (ssize_t)strlen(pattern); + li = (ssize_t)strlen(insert); + ld = li - lp; + for (i=0;i<li;i++) { + switch (in[i]) { + case '`': + case '"': + case '\'': + case ';': + case '$': + case '%': + case '\r': + case '\n': + in[i] = '_'; + default: + /* ok */ + break; + } + } + + while ((p = strstr(s,pattern))) { + if (ld > 0) { + char *t = Realloc(string, ls + ld + 1); + if (!t) { + DEBUG(0, ("realloc_string_sub: out of memory!\n")); + SAFE_FREE(in); + return NULL; + } + string = t; + p = t + (p - s); + } + if (li != lp) { + memmove(p+li,p+lp,strlen(p+lp)+1); + } + memcpy(p, in, li); + s = p + li; + ls += ld; + } + SAFE_FREE(in); + return string; +} + +/** + Similar to string_sub() but allows for any character to be substituted. + Use with caution! + if len==0 then the string cannot be extended. This is different from the old + use of len==0 which was for no length checks to be done. +**/ + +void all_string_sub(char *s,const char *pattern,const char *insert, size_t len) +{ + char *p; + ssize_t ls,lp,li; + + if (!insert || !pattern || !s) + return; + + ls = (ssize_t)strlen(s); + lp = (ssize_t)strlen(pattern); + li = (ssize_t)strlen(insert); + + if (!*pattern) + return; + + if (len == 0) + len = ls + 1; /* len is number of *bytes* */ + + while (lp <= ls && (p = strstr(s,pattern))) { + if (ls + (li-lp) >= len) { + DEBUG(0,("ERROR: string overflow by %d in all_string_sub(%.50s, %d)\n", + (int)(ls + (li-lp) - len), + pattern, (int)len)); + break; + } + if (li != lp) { + memmove(p+li,p+lp,strlen(p+lp)+1); + } + memcpy(p, insert, li); + s = p + li; + ls += (li-lp); + } +} + +/** + Similar to all_string_sub but for unicode strings. + Return a new allocated unicode string. + similar to string_sub() but allows for any character to be substituted. + Use with caution! +**/ + +smb_ucs2_t *all_string_sub_w(const smb_ucs2_t *s, const smb_ucs2_t *pattern, + const smb_ucs2_t *insert) +{ + smb_ucs2_t *r, *rp; + const smb_ucs2_t *sp; + size_t lr, lp, li, lt; + + if (!insert || !pattern || !*pattern || !s) + return NULL; + + lt = (size_t)strlen_w(s); + lp = (size_t)strlen_w(pattern); + li = (size_t)strlen_w(insert); + + if (li > lp) { + const smb_ucs2_t *st = s; + int ld = li - lp; + while ((sp = strstr_w(st, pattern))) { + st = sp + lp; + lt += ld; + } + } + + r = rp = (smb_ucs2_t *)malloc((lt + 1)*(sizeof(smb_ucs2_t))); + if (!r) { + DEBUG(0, ("all_string_sub_w: out of memory!\n")); + return NULL; + } + + while ((sp = strstr_w(s, pattern))) { + memcpy(rp, s, (sp - s)); + rp += ((sp - s) / sizeof(smb_ucs2_t)); + memcpy(rp, insert, (li * sizeof(smb_ucs2_t))); + s = sp + lp; + rp += li; + } + lr = ((rp - r) / sizeof(smb_ucs2_t)); + if (lr < lt) { + memcpy(rp, s, ((lt - lr) * sizeof(smb_ucs2_t))); + rp += (lt - lr); + } + *rp = 0; + + return r; +} + +smb_ucs2_t *all_string_sub_wa(smb_ucs2_t *s, const char *pattern, + const char *insert) +{ + wpstring p, i; + + if (!insert || !pattern || !s) + return NULL; + push_ucs2(NULL, p, pattern, sizeof(wpstring) - 1, STR_TERMINATE); + push_ucs2(NULL, i, insert, sizeof(wpstring) - 1, STR_TERMINATE); + return all_string_sub_w(s, p, i); +} + +/** + Splits out the front and back at a separator. +**/ + +void split_at_last_component(char *path, char *front, char sep, char *back) +{ + char *p = strrchr_m(path, sep); + + if (p != NULL) + *p = 0; + + if (front != NULL) + pstrcpy(front, path); + + if (p != NULL) { + if (back != NULL) + pstrcpy(back, p+1); + *p = '\\'; + } else { + if (back != NULL) + back[0] = 0; + } +} + +/** + Write an octal as a string. +**/ + +const char *octal_string(int i) +{ + static char ret[64]; + if (i == -1) + return "-1"; + slprintf(ret, sizeof(ret)-1, "0%o", i); + return ret; +} + + +/** + Truncate a string at a specified length. +**/ + +char *string_truncate(char *s, int length) +{ + if (s && strlen(s) > length) + s[length] = 0; + return s; +} + +/** + Strchr and strrchr_m are very hard to do on general multi-byte strings. + We convert via ucs2 for now. +**/ + +char *strchr_m(const char *s, char c) +{ + wpstring ws; + pstring s2; + smb_ucs2_t *p; + + push_ucs2(NULL, ws, s, sizeof(ws), STR_TERMINATE); + p = strchr_w(ws, UCS2_CHAR(c)); + if (!p) + return NULL; + *p = 0; + pull_ucs2_pstring(s2, ws); + return (char *)(s+strlen(s2)); +} + +char *strrchr_m(const char *s, char c) +{ + wpstring ws; + pstring s2; + smb_ucs2_t *p; + + push_ucs2(NULL, ws, s, sizeof(ws), STR_TERMINATE); + p = strrchr_w(ws, UCS2_CHAR(c)); + if (!p) + return NULL; + *p = 0; + pull_ucs2_pstring(s2, ws); + return (char *)(s+strlen(s2)); +} + +/** + Convert a string to lower case. +**/ + +void strlower_m(char *s) +{ + /* this is quite a common operation, so we want it to be + fast. We optimise for the ascii case, knowing that all our + supported multi-byte character sets are ascii-compatible + (ie. they match for the first 128 chars) */ + + while (*s && !(((unsigned char)s[0]) & 0x7F)) { + *s = tolower((unsigned char)*s); + s++; + } + + if (!*s) + return; + + /* I assume that lowercased string takes the same number of bytes + * as source string even in UTF-8 encoding. (VIV) */ + unix_strlower(s,strlen(s)+1,s,strlen(s)+1); +} + +/** + Duplicate convert a string to lower case. +**/ + +char *strdup_lower(const char *s) +{ + char *t = strdup(s); + if (t == NULL) { + DEBUG(0, ("strdup_lower: Out of memory!\n")); + return NULL; + } + strlower_m(t); + return t; +} + +/** + Convert a string to upper case. +**/ + +void strupper_m(char *s) +{ + /* this is quite a common operation, so we want it to be + fast. We optimise for the ascii case, knowing that all our + supported multi-byte character sets are ascii-compatible + (ie. they match for the first 128 chars) */ + + while (*s && !(((unsigned char)s[0]) & 0x7F)) { + *s = toupper((unsigned char)*s); + s++; + } + + if (!*s) + return; + + /* I assume that lowercased string takes the same number of bytes + * as source string even in multibyte encoding. (VIV) */ + unix_strupper(s,strlen(s)+1,s,strlen(s)+1); +} + +/** + Convert a string to upper case. +**/ + +char *strdup_upper(const char *s) +{ + char *t = strdup(s); + if (t == NULL) { + DEBUG(0, ("strdup_upper: Out of memory!\n")); + return NULL; + } + strupper_m(t); + return t; +} + +/** + Return a RFC2254 binary string representation of a buffer. + Used in LDAP filters. + Caller must free. +**/ + +char *binary_string(char *buf, int len) +{ + char *s; + int i, j; + const char *hex = "0123456789ABCDEF"; + s = malloc(len * 3 + 1); + if (!s) + return NULL; + for (j=i=0;i<len;i++) { + s[j] = '\\'; + s[j+1] = hex[((unsigned char)buf[i]) >> 4]; + s[j+2] = hex[((unsigned char)buf[i]) & 0xF]; + j += 3; + } + s[j] = 0; + return s; +} + +/** + Just a typesafety wrapper for snprintf into a pstring. +**/ + + int pstr_sprintf(pstring s, const char *fmt, ...) +{ + va_list ap; + int ret; + + va_start(ap, fmt); + ret = vsnprintf(s, PSTRING_LEN, fmt, ap); + va_end(ap); + return ret; +} + +/** + Just a typesafety wrapper for snprintf into a fstring. +**/ + + int fstr_sprintf(fstring s, const char *fmt, ...) +{ + va_list ap; + int ret; + + va_start(ap, fmt); + ret = vsnprintf(s, FSTRING_LEN, fmt, ap); + va_end(ap); + return ret; +} + +#ifndef HAVE_STRNDUP +/** + Some platforms don't have strndup. +**/ + + char *strndup(const char *s, size_t n) +{ + char *ret; + + n = strnlen(s, n); + ret = malloc(n+1); + if (!ret) + return NULL; + memcpy(ret, s, n); + ret[n] = 0; + + return ret; +} +#endif + +#ifndef HAVE_STRNLEN +/** + Some platforms don't have strnlen +**/ + + size_t strnlen(const char *s, size_t n) +{ + int i; + for (i=0; s[i] && i<n; i++) + /* noop */ ; + return i; +} +#endif + +/** + List of Strings manipulation functions +**/ + +#define S_LIST_ABS 16 /* List Allocation Block Size */ + +char **str_list_make(const char *string, const char *sep) +{ + char **list, **rlist; + const char *str; + char *s; + int num, lsize; + pstring tok; + + if (!string || !*string) + return NULL; + s = strdup(string); + if (!s) { + DEBUG(0,("str_list_make: Unable to allocate memory")); + return NULL; + } + if (!sep) sep = LIST_SEP; + + num = lsize = 0; + list = NULL; + + str = s; + while (next_token(&str, tok, sep, sizeof(tok))) { + if (num == lsize) { + lsize += S_LIST_ABS; + rlist = (char **)Realloc(list, ((sizeof(char **)) * (lsize +1))); + if (!rlist) { + DEBUG(0,("str_list_make: Unable to allocate memory")); + str_list_free(&list); + SAFE_FREE(s); + return NULL; + } else + list = rlist; + memset (&list[num], 0, ((sizeof(char**)) * (S_LIST_ABS +1))); + } + + list[num] = strdup(tok); + if (!list[num]) { + DEBUG(0,("str_list_make: Unable to allocate memory")); + str_list_free(&list); + SAFE_FREE(s); + return NULL; + } + + num++; + } + + SAFE_FREE(s); + return list; +} + +BOOL str_list_copy(char ***dest, const char **src) +{ + char **list, **rlist; + int num, lsize; + + *dest = NULL; + if (!src) + return False; + + num = lsize = 0; + list = NULL; + + while (src[num]) { + if (num == lsize) { + lsize += S_LIST_ABS; + rlist = (char **)Realloc(list, ((sizeof(char **)) * (lsize +1))); + if (!rlist) { + DEBUG(0,("str_list_copy: Unable to re-allocate memory")); + str_list_free(&list); + return False; + } else + list = rlist; + memset (&list[num], 0, ((sizeof(char **)) * (S_LIST_ABS +1))); + } + + list[num] = strdup(src[num]); + if (!list[num]) { + DEBUG(0,("str_list_copy: Unable to allocate memory")); + str_list_free(&list); + return False; + } + + num++; + } + + *dest = list; + return True; +} + +/** + * Return true if all the elements of the list match exactly. + **/ +BOOL str_list_compare(char **list1, char **list2) +{ + int num; + + if (!list1 || !list2) + return (list1 == list2); + + for (num = 0; list1[num]; num++) { + if (!list2[num]) + return False; + if (!strcsequal(list1[num], list2[num])) + return False; + } + if (list2[num]) + return False; /* if list2 has more elements than list1 fail */ + + return True; +} + +void str_list_free(char ***list) +{ + char **tlist; + + if (!list || !*list) + return; + tlist = *list; + for(; *tlist; tlist++) + SAFE_FREE(*tlist); + SAFE_FREE(*list); +} + +BOOL str_list_substitute(char **list, const char *pattern, const char *insert) +{ + char *p, *s, *t; + ssize_t ls, lp, li, ld, i, d; + + if (!list) + return False; + if (!pattern) + return False; + if (!insert) + return False; + + lp = (ssize_t)strlen(pattern); + li = (ssize_t)strlen(insert); + ld = li -lp; + + while (*list) { + s = *list; + ls = (ssize_t)strlen(s); + + while ((p = strstr(s, pattern))) { + t = *list; + d = p -t; + if (ld) { + t = (char *) malloc(ls +ld +1); + if (!t) { + DEBUG(0,("str_list_substitute: Unable to allocate memory")); + return False; + } + memcpy(t, *list, d); + memcpy(t +d +li, p +lp, ls -d -lp +1); + SAFE_FREE(*list); + *list = t; + ls += ld; + s = t +d +li; + } + + for (i = 0; i < li; i++) { + switch (insert[i]) { + case '`': + case '"': + case '\'': + case ';': + case '$': + case '%': + case '\r': + case '\n': + t[d +i] = '_'; + break; + default: + t[d +i] = insert[i]; + } + } + } + + list++; + } + + return True; +} + + +#define IPSTR_LIST_SEP "," + +/** + * Add ip string representation to ipstr list. Used also + * as part of @function ipstr_list_make + * + * @param ipstr_list pointer to string containing ip list; + * MUST BE already allocated and IS reallocated if necessary + * @param ipstr_size pointer to current size of ipstr_list (might be changed + * as a result of reallocation) + * @param ip IP address which is to be added to list + * @return pointer to string appended with new ip and possibly + * reallocated to new length + **/ + +char* ipstr_list_add(char** ipstr_list, const struct in_addr *ip) +{ + char* new_ipstr = NULL; + + /* arguments checking */ + if (!ipstr_list || !ip) return NULL; + + /* attempt to convert ip to a string and append colon separator to it */ + if (*ipstr_list) { + asprintf(&new_ipstr, "%s%s%s", *ipstr_list, IPSTR_LIST_SEP,inet_ntoa(*ip)); + SAFE_FREE(*ipstr_list); + } else { + asprintf(&new_ipstr, "%s", inet_ntoa(*ip)); + } + *ipstr_list = new_ipstr; + return *ipstr_list; +} + + +/** + * Allocate and initialise an ipstr list using ip adresses + * passed as arguments. + * + * @param ipstr_list pointer to string meant to be allocated and set + * @param ip_list array of ip addresses to place in the list + * @param ip_count number of addresses stored in ip_list + * @return pointer to allocated ip string + **/ + +char* ipstr_list_make(char** ipstr_list, const struct in_addr* ip_list, int ip_count) +{ + int i; + + /* arguments checking */ + if (!ip_list && !ipstr_list) return 0; + + *ipstr_list = NULL; + + /* process ip addresses given as arguments */ + for (i = 0; i < ip_count; i++) + *ipstr_list = ipstr_list_add(ipstr_list, &ip_list[i]); + + return (*ipstr_list); +} + + +/** + * Parse given ip string list into array of ip addresses + * (as in_addr structures) + * + * @param ipstr ip string list to be parsed + * @param ip_list pointer to array of ip addresses which is + * allocated by this function and must be freed by caller + * @return number of succesfully parsed addresses + **/ + +int ipstr_list_parse(const char* ipstr_list, struct in_addr** ip_list) +{ + fstring token_str; + int count; + + if (!ipstr_list || !ip_list) return 0; + + for (*ip_list = NULL, count = 0; + next_token(&ipstr_list, token_str, IPSTR_LIST_SEP, FSTRING_LEN); + count++) { + + struct in_addr addr; + + /* convert single token to ip address */ + if ( (addr.s_addr = inet_addr(token_str)) == INADDR_NONE ) + break; + + /* prepare place for another in_addr structure */ + *ip_list = Realloc(*ip_list, (count + 1) * sizeof(struct in_addr)); + if (!*ip_list) return -1; + + (*ip_list)[count] = addr; + } + + return count; +} + + +/** + * Safely free ip string list + * + * @param ipstr_list ip string list to be freed + **/ + +void ipstr_list_free(char* ipstr_list) +{ + SAFE_FREE(ipstr_list); +} + + +/** + Unescape a URL encoded string, in place. +**/ + +void rfc1738_unescape(char *buf) +{ + char *p=buf; + + while ((p=strchr_m(p,'+'))) + *p = ' '; + + p = buf; + + while (p && *p && (p=strchr_m(p,'%'))) { + int c1 = p[1]; + int c2 = p[2]; + + if (c1 >= '0' && c1 <= '9') + c1 = c1 - '0'; + else if (c1 >= 'A' && c1 <= 'F') + c1 = 10 + c1 - 'A'; + else if (c1 >= 'a' && c1 <= 'f') + c1 = 10 + c1 - 'a'; + else {p++; continue;} + + if (c2 >= '0' && c2 <= '9') + c2 = c2 - '0'; + else if (c2 >= 'A' && c2 <= 'F') + c2 = 10 + c2 - 'A'; + else if (c2 >= 'a' && c2 <= 'f') + c2 = 10 + c2 - 'a'; + else {p++; continue;} + + *p = (c1<<4) | c2; + + memmove(p+1, p+3, strlen(p+3)+1); + p++; + } +} + +static const char *b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +/** + * Decode a base64 string into a DATA_BLOB - simple and slow algorithm + **/ +DATA_BLOB base64_decode_data_blob(const char *s) +{ + int bit_offset, byte_offset, idx, i, n; + DATA_BLOB decoded = data_blob(s, strlen(s)+1); + unsigned char *d = decoded.data; + char *p; + + n=i=0; + + while (*s && (p=strchr_m(b64,*s))) { + idx = (int)(p - b64); + byte_offset = (i*6)/8; + bit_offset = (i*6)%8; + d[byte_offset] &= ~((1<<(8-bit_offset))-1); + if (bit_offset < 3) { + d[byte_offset] |= (idx << (2-bit_offset)); + n = byte_offset+1; + } else { + d[byte_offset] |= (idx >> (bit_offset-2)); + d[byte_offset+1] = 0; + d[byte_offset+1] |= (idx << (8-(bit_offset-2))) & 0xFF; + n = byte_offset+2; + } + s++; i++; + } + + /* fix up length */ + decoded.length = n; + return decoded; +} + +/** + * Decode a base64 string in-place - wrapper for the above + **/ +void base64_decode_inplace(char *s) +{ + DATA_BLOB decoded = base64_decode_data_blob(s); + memcpy(s, decoded.data, decoded.length); + data_blob_free(&decoded); + + /* null terminate */ + s[decoded.length] = '\0'; +} + +/** + * Encode a base64 string into a malloc()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) +{ + int bits = 0; + int char_count = 0; + size_t out_cnt = 0; + size_t len = data.length; + size_t output_len = data.length * 2; + char *result = malloc(output_len); /* get us plenty of space */ + + while (len-- && out_cnt < (data.length * 2) - 5) { + int c = (unsigned char) *(data.data++); + bits += c; + char_count++; + if (char_count == 3) { + result[out_cnt++] = b64[bits >> 18]; + result[out_cnt++] = b64[(bits >> 12) & 0x3f]; + result[out_cnt++] = b64[(bits >> 6) & 0x3f]; + result[out_cnt++] = b64[bits & 0x3f]; + bits = 0; + char_count = 0; + } else { + bits <<= 8; + } + } + if (char_count != 0) { + bits <<= 16 - (8 * char_count); + result[out_cnt++] = b64[bits >> 18]; + result[out_cnt++] = b64[(bits >> 12) & 0x3f]; + if (char_count == 1) { + result[out_cnt++] = '='; + result[out_cnt++] = '='; + } else { + result[out_cnt++] = b64[(bits >> 6) & 0x3f]; + result[out_cnt++] = '='; + } + } + result[out_cnt] = '\0'; /* terminate */ + return result; +} + +#ifdef VALGRIND +size_t valgrind_strlen(const char *s) +{ + size_t count; + for(count = 0; *s++; count++) + ; + return count; +} +#endif diff --git a/source4/lib/util_unistr.c b/source4/lib/util_unistr.c new file mode 100644 index 0000000000..e5d2b8c3db --- /dev/null +++ b/source4/lib/util_unistr.c @@ -0,0 +1,838 @@ +/* + Unix SMB/CIFS implementation. + Samba utility functions + Copyright (C) Andrew Tridgell 1992-2001 + Copyright (C) Simo Sorce 2001 + + 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" + +#ifndef MAXUNI +#define MAXUNI 1024 +#endif + +/* these 3 tables define the unicode case handling. They are loaded + at startup either via mmap() or read() from the lib directory */ +static smb_ucs2_t *upcase_table; +static smb_ucs2_t *lowcase_table; +static uint8 *valid_table; + + +/******************************************************************* +load the case handling tables +********************************************************************/ +void load_case_tables(void) +{ + static int initialised; + int i; + TALLOC_CTX *mem_ctx; + + if (initialised) return; + initialised = 1; + + mem_ctx = talloc_init("load_case_tables"); + if (!mem_ctx) { + smb_panic("No memory for case_tables"); + } + upcase_table = map_file(lib_path(mem_ctx, "upcase.dat"), 0x20000); + lowcase_table = map_file(lib_path(mem_ctx, "lowcase.dat"), 0x20000); + talloc_destroy(mem_ctx); + + /* we would like Samba to limp along even if these tables are + not available */ + if (!upcase_table) { + DEBUG(1,("creating lame upcase table\n")); + upcase_table = malloc(0x20000); + if (!upcase_table) { + smb_panic("No memory for upcase tables"); + } + for (i=0;i<0x10000;i++) { + smb_ucs2_t v; + SSVAL(&v, 0, i); + upcase_table[v] = i; + } + for (i=0;i<256;i++) { + smb_ucs2_t v; + SSVAL(&v, 0, UCS2_CHAR(i)); + upcase_table[v] = UCS2_CHAR(islower(i)?toupper(i):i); + } + } + + if (!lowcase_table) { + DEBUG(1,("creating lame lowcase table\n")); + lowcase_table = malloc(0x20000); + if (!lowcase_table) { + smb_panic("No memory for lowcase tables"); + } + for (i=0;i<0x10000;i++) { + smb_ucs2_t v; + SSVAL(&v, 0, i); + lowcase_table[v] = i; + } + for (i=0;i<256;i++) { + smb_ucs2_t v; + SSVAL(&v, 0, UCS2_CHAR(i)); + lowcase_table[v] = UCS2_CHAR(isupper(i)?tolower(i):i); + } + } +} + +/* + see if a ucs2 character can be mapped correctly to a dos character + and mapped back to the same character in ucs2 +*/ +static int check_dos_char(smb_ucs2_t c) +{ + char buf[10]; + smb_ucs2_t c2 = 0; + int len1, len2; + len1 = convert_string(CH_UCS2, CH_DOS, &c, 2, buf, sizeof(buf)); + if (len1 == 0) return 0; + len2 = convert_string(CH_DOS, CH_UCS2, buf, len1, &c2, 2); + if (len2 != 2) return 0; + return (c == c2); +} + +/** + * Load the valid character map table from <tt>valid.dat</tt> or + * create from the configured codepage. + * + * This function is called whenever the configuration is reloaded. + * However, the valid character table is not changed if it's loaded + * from a file, because we can't unmap files. + **/ +void init_valid_table(void) +{ + static int mapped_file; + int i; + const char *allowed = ".!#$%&'()_-@^`~"; + uint8 *valid_file; + TALLOC_CTX *mem_ctx; + + if (mapped_file) { + /* Can't unmap files, so stick with what we have */ + return; + } + + mem_ctx = talloc_init("init_valid_table"); + if (!mem_ctx) { + smb_panic("No memory for valid_table"); + } + valid_file = map_file(lib_path(mem_ctx, "valid.dat"), 0x10000); + talloc_destroy(mem_ctx); + if (valid_file) { + valid_table = valid_file; + mapped_file = 1; + return; + } + + /* Otherwise, we're using a dynamically created valid_table. + * It might need to be regenerated if the code page changed. + * We know that we're not using a mapped file, so we can + * free() the old one. */ + if (valid_table) free(valid_table); + + DEBUG(2,("creating default valid table\n")); + valid_table = malloc(0x10000); + if (!valid_table) { + smb_panic("No memory for valid_table"); + } + for (i=0;i<128;i++) + valid_table[i] = isalnum(i) || strchr(allowed,i); + + for (;i<0x10000;i++) { + smb_ucs2_t c; + SSVAL(&c, 0, i); + valid_table[i] = check_dos_char(c); + } +} + + + +/******************************************************************* + Write a string in (little-endian) unicode format. src is in + the current DOS codepage. len is the length in bytes of the + string pointed to by dst. + + if null_terminate is True then null terminate the packet (adds 2 bytes) + + the return value is the length in bytes consumed by the string, including the + null termination if applied +********************************************************************/ + +size_t dos_PutUniCode(char *dst,const char *src, ssize_t len, BOOL null_terminate) +{ + return push_ucs2(NULL, dst, src, len, + STR_UNICODE|STR_NOALIGN | (null_terminate?STR_TERMINATE:0)); +} + + +/******************************************************************* + Skip past a unicode string, but not more than len. Always move + past a terminating zero if found. +********************************************************************/ + +char *skip_unibuf(char *src, size_t len) +{ + char *srcend = src + len; + + while (src < srcend && SVAL(src,0)) + src += 2; + + if(!SVAL(src,0)) + src += 2; + + return src; +} + +/* Copy a string from little-endian or big-endian unicode source (depending + * on flags) to internal samba format destination + */ +int rpcstr_pull(char* dest, void *src, int dest_len, int src_len, int flags) +{ + if (!src) return 0; + if(dest_len==-1) dest_len=MAXUNI-3; + return pull_ucs2(NULL, dest, src, dest_len, src_len, flags|STR_UNICODE|STR_NOALIGN); +} + +/* Copy a string from a unistr2 source to internal samba format + destination. Use this instead of direct calls to rpcstr_pull() to avoid + having to determine whether the source string is null terminated. */ + +int rpcstr_pull_unistr2_fstring(char *dest, UNISTR2 *src) +{ + return pull_ucs2(NULL, dest, src->buffer, sizeof(fstring), + src->uni_str_len * 2, 0); +} + +/* Converts a string from internal samba format to unicode + */ +int rpcstr_push(void* dest, const char *src, int dest_len, int flags) +{ + return push_ucs2(NULL, dest, src, dest_len, flags|STR_UNICODE|STR_NOALIGN); +} + +/******************************************************************* + Convert a (little-endian) UNISTR2 structure to an ASCII string +********************************************************************/ +void unistr2_to_ascii(char *dest, const UNISTR2 *str, size_t maxlen) +{ + if (str == NULL) { + *dest='\0'; + return; + } + pull_ucs2(NULL, dest, str->buffer, maxlen, str->uni_str_len*2, STR_NOALIGN); +} + +/******************************************************************* +give a static string for displaying a UNISTR2 +********************************************************************/ +const char *unistr2_static(TALLOC_CTX *mem_ctx, const UNISTR2 *str) +{ + pstring ret; + unistr2_to_ascii(ret, str, sizeof(ret)); + return talloc_strdup(mem_ctx, ret); +} + + +/******************************************************************* + duplicate a UNISTR2 string into a null terminated char* + using a talloc context +********************************************************************/ +char *unistr2_tdup(TALLOC_CTX *ctx, const UNISTR2 *str) +{ + char *s; + int maxlen = (str->uni_str_len+1)*4; + if (!str->buffer) return NULL; + s = (char *)talloc(ctx, maxlen); /* convervative */ + if (!s) return NULL; + pull_ucs2(NULL, s, str->buffer, maxlen, str->uni_str_len*2, + STR_NOALIGN); + return s; +} + + +/******************************************************************* +Return a number stored in a buffer +********************************************************************/ + +uint32 buffer2_to_uint32(BUFFER2 *str) +{ + if (str->buf_len == 4) + return IVAL(str->buffer, 0); + else + return 0; +} + +/******************************************************************* + Convert a wchar to upper case. +********************************************************************/ + +smb_ucs2_t toupper_w(smb_ucs2_t val) +{ + return upcase_table[SVAL(&val,0)]; +} + +/******************************************************************* + Convert a wchar to lower case. +********************************************************************/ + +smb_ucs2_t tolower_w( smb_ucs2_t val ) +{ + return lowcase_table[SVAL(&val,0)]; + +} + +/******************************************************************* +determine if a character is lowercase +********************************************************************/ +BOOL islower_w(smb_ucs2_t c) +{ + return upcase_table[SVAL(&c,0)] != c; +} + +/******************************************************************* +determine if a character is uppercase +********************************************************************/ +BOOL isupper_w(smb_ucs2_t c) +{ + return lowcase_table[SVAL(&c,0)] != c; +} + + +/******************************************************************* +determine if a character is valid in a 8.3 name +********************************************************************/ +BOOL isvalid83_w(smb_ucs2_t c) +{ + return valid_table[SVAL(&c,0)] != 0; +} + +/******************************************************************* + Count the number of characters in a smb_ucs2_t string. +********************************************************************/ +size_t strlen_w(const smb_ucs2_t *src) +{ + size_t len; + + for (len = 0; SVAL(src,0); len++, src++) ; + + return len; +} + +/******************************************************************* + Count up to max number of characters in a smb_ucs2_t string. +********************************************************************/ +size_t strnlen_w(const smb_ucs2_t *src, size_t max) +{ + size_t len; + + for (len = 0; (len < max) && SVAL(src, 0); len++, src++) ; + + return len; +} + +/******************************************************************* +wide strchr() +********************************************************************/ +const smb_ucs2_t *strchr_w(const smb_ucs2_t *s, smb_ucs2_t c) +{ + while (*s != 0) { + if (c == *s) return s; + s++; + } + if (c == *s) return s; + + return NULL; +} + +const smb_ucs2_t *strchr_wa(const smb_ucs2_t *s, char c) +{ + return strchr_w(s, UCS2_CHAR(c)); +} + +const smb_ucs2_t *strrchr_w(const smb_ucs2_t *s, smb_ucs2_t c) +{ + const smb_ucs2_t *p = s; + int len = strlen_w(s); + if (len == 0) return NULL; + p += (len - 1); + do { + if (c == *p) return p; + } while (p-- != s); + return NULL; +} + +/******************************************************************* +wide strstr() +********************************************************************/ +const smb_ucs2_t *strstr_w(const smb_ucs2_t *s, const smb_ucs2_t *ins) +{ + const smb_ucs2_t *r; + size_t slen, inslen; + + if (!s || !*s || !ins || !*ins) return NULL; + slen = strlen_w(s); + inslen = strlen_w(ins); + r = s; + while ((r = strchr_w(r, *ins))) { + if (strncmp_w(r, ins, inslen) == 0) return r; + r++; + } + return NULL; +} + +/******************************************************************* + Convert a string to lower case. + return True if any char is converted +********************************************************************/ +BOOL strlower_w(smb_ucs2_t *s) +{ + BOOL ret = False; + while (*s) { + smb_ucs2_t v = tolower_w(*s); + if (v != *s) { + *s = v; + ret = True; + } + s++; + } + return ret; +} + +/******************************************************************* + Convert a string to upper case. + return True if any char is converted +********************************************************************/ +BOOL strupper_w(smb_ucs2_t *s) +{ + BOOL ret = False; + while (*s) { + smb_ucs2_t v = toupper_w(*s); + if (v != *s) { + *s = v; + ret = True; + } + s++; + } + return ret; +} + +int strcmp_w(const smb_ucs2_t *a, const smb_ucs2_t *b) +{ + while (*b && *a == *b) { a++; b++; } + return (*a - *b); + /* warning: if *a != *b and both are not 0 we retrun a random + greater or lesser than 0 number not realted to which + string is longer */ +} + +int strncmp_w(const smb_ucs2_t *a, const smb_ucs2_t *b, size_t len) +{ + size_t n = 0; + while ((n < len) && *b && *a == *b) { a++; b++; n++;} + return (len - n)?(*a - *b):0; +} + +/******************************************************************* +case insensitive string comparison +********************************************************************/ +int strcasecmp_w(const smb_ucs2_t *a, const smb_ucs2_t *b) +{ + while (*b && toupper_w(*a) == toupper_w(*b)) { a++; b++; } + return (tolower_w(*a) - tolower_w(*b)); +} + +/******************************************************************* +case insensitive string comparison, lenght limited +********************************************************************/ +int strncasecmp_w(const smb_ucs2_t *a, const smb_ucs2_t *b, size_t len) +{ + size_t n = 0; + while ((n < len) && *b && (toupper_w(*a) == toupper_w(*b))) { a++; b++; n++; } + return (len - n)?(tolower_w(*a) - tolower_w(*b)):0; +} + +/******************************************************************* + compare 2 strings +********************************************************************/ +BOOL strequal_w(const smb_ucs2_t *s1, const smb_ucs2_t *s2) +{ + if (s1 == s2) return(True); + if (!s1 || !s2) return(False); + + return(strcasecmp_w(s1,s2)==0); +} + +/******************************************************************* + compare 2 strings up to and including the nth char. + ******************************************************************/ +BOOL strnequal_w(const smb_ucs2_t *s1,const smb_ucs2_t *s2,size_t n) +{ + if (s1 == s2) return(True); + if (!s1 || !s2 || !n) return(False); + + return(strncasecmp_w(s1,s2,n)==0); +} + +/******************************************************************* +duplicate string +********************************************************************/ +smb_ucs2_t *strdup_w(const smb_ucs2_t *src) +{ + return strndup_w(src, 0); +} + +/* if len == 0 then duplicate the whole string */ +smb_ucs2_t *strndup_w(const smb_ucs2_t *src, size_t len) +{ + smb_ucs2_t *dest; + + if (!len) len = strlen_w(src); + dest = (smb_ucs2_t *)malloc((len + 1) * sizeof(smb_ucs2_t)); + if (!dest) { + DEBUG(0,("strdup_w: out of memory!\n")); + return NULL; + } + + memcpy(dest, src, len * sizeof(smb_ucs2_t)); + dest[len] = 0; + + return dest; +} + +/******************************************************************* +copy a string with max len +********************************************************************/ + +smb_ucs2_t *strncpy_w(smb_ucs2_t *dest, const smb_ucs2_t *src, const size_t max) +{ + size_t len; + + if (!dest || !src) return NULL; + + for (len = 0; (src[len] != 0) && (len < max); len++) + dest[len] = src[len]; + while (len < max) + dest[len++] = 0; + + return dest; +} + + +/******************************************************************* +append a string of len bytes and add a terminator +********************************************************************/ + +smb_ucs2_t *strncat_w(smb_ucs2_t *dest, const smb_ucs2_t *src, const size_t max) +{ + size_t start; + size_t len; + + if (!dest || !src) return NULL; + + start = strlen_w(dest); + len = strnlen_w(src, max); + + memcpy(&dest[start], src, len*sizeof(smb_ucs2_t)); + dest[start+len] = 0; + + return dest; +} + +smb_ucs2_t *strcat_w(smb_ucs2_t *dest, const smb_ucs2_t *src) +{ + size_t start; + size_t len; + + if (!dest || !src) return NULL; + + start = strlen_w(dest); + len = strlen_w(src); + + memcpy(&dest[start], src, len*sizeof(smb_ucs2_t)); + dest[start+len] = 0; + + return dest; +} + + +/******************************************************************* +replace any occurence of oldc with newc in unicode string +********************************************************************/ + +void string_replace_w(smb_ucs2_t *s, smb_ucs2_t oldc, smb_ucs2_t newc) +{ + for(;*s;s++) { + if(*s==oldc) *s=newc; + } +} + +/******************************************************************* +trim unicode string +********************************************************************/ + +BOOL trim_string_w(smb_ucs2_t *s, const smb_ucs2_t *front, + const smb_ucs2_t *back) +{ + BOOL ret = False; + size_t len, front_len, back_len; + + if (!s || !*s) return False; + + len = strlen_w(s); + + if (front && *front) { + front_len = strlen_w(front); + while (len && strncmp_w(s, front, front_len) == 0) { + memmove(s, (s + front_len), (len - front_len + 1) * sizeof(smb_ucs2_t)); + len -= front_len; + ret = True; + } + } + + if (back && *back) { + back_len = strlen_w(back); + while (len && strncmp_w((s + (len - back_len)), back, back_len) == 0) { + s[len - back_len] = 0; + len -= back_len; + ret = True; + } + } + + return ret; +} + +/* + The *_wa() functions take a combination of 7 bit ascii + and wide characters They are used so that you can use string + functions combining C string constants with ucs2 strings + + The char* arguments must NOT be multibyte - to be completely sure + of this only pass string constants */ + + +void pstrcpy_wa(smb_ucs2_t *dest, const char *src) +{ + int i; + for (i=0;i<PSTRING_LEN;i++) { + dest[i] = UCS2_CHAR(src[i]); + if (src[i] == 0) return; + } +} + +int strcmp_wa(const smb_ucs2_t *a, const char *b) +{ + while (*b && *a == UCS2_CHAR(*b)) { a++; b++; } + return (*a - UCS2_CHAR(*b)); +} + +int strncmp_wa(const smb_ucs2_t *a, const char *b, size_t len) +{ + size_t n = 0; + while ((n < len) && *b && *a == UCS2_CHAR(*b)) { a++; b++; n++;} + return (len - n)?(*a - UCS2_CHAR(*b)):0; +} + +const smb_ucs2_t *strpbrk_wa(const smb_ucs2_t *s, const char *p) +{ + while (*s != 0) { + int i; + for (i=0; p[i] && *s != UCS2_CHAR(p[i]); i++) + ; + if (p[i]) return s; + s++; + } + return NULL; +} + +const smb_ucs2_t *strstr_wa(const smb_ucs2_t *s, const char *ins) +{ + const smb_ucs2_t *r; + size_t slen, inslen; + + if (!s || !*s || !ins || !*ins) return NULL; + slen = strlen_w(s); + inslen = strlen(ins); + r = s; + while ((r = strchr_w(r, UCS2_CHAR(*ins)))) { + if (strncmp_wa(r, ins, inslen) == 0) return r; + r++; + } + return NULL; +} + +/******************************************************************* +copy a string with max len +********************************************************************/ + +smb_ucs2_t *strncpy_wa(smb_ucs2_t *dest, const char *src, const size_t max) +{ + smb_ucs2_t *ucs2_src; + + if (!dest || !src) return NULL; + if (!(ucs2_src = acnv_uxu2(src))) + return NULL; + + strncpy_w(dest, ucs2_src, max); + SAFE_FREE(ucs2_src); + return dest; +} + +/******************************************************************* +convert and duplicate an ascii string +********************************************************************/ +smb_ucs2_t *strdup_wa(const char *src) +{ + return strndup_wa(src, 0); +} + +/* if len == 0 then duplicate the whole string */ +smb_ucs2_t *strndup_wa(const char *src, size_t len) +{ + smb_ucs2_t *dest, *s; + + s = acnv_dosu2(src); + if (!len) len = strlen_w(s); + dest = (smb_ucs2_t *)malloc((len + 1) * sizeof(smb_ucs2_t)); + if (!dest) { + DEBUG(0,("strdup_w: out of memory!\n")); + SAFE_FREE(s); + return NULL; + } + + memcpy(dest, src, len * sizeof(smb_ucs2_t)); + dest[len] = 0; + + SAFE_FREE(s); + return dest; +} + +/******************************************************************* +append a string of len bytes and add a terminator +********************************************************************/ + +smb_ucs2_t *strncat_wa(smb_ucs2_t *dest, const char *src, const size_t max) +{ + smb_ucs2_t *ucs2_src; + + if (!dest || !src) return NULL; + if (!(ucs2_src = acnv_uxu2(src))) + return NULL; + + strncat_w(dest, ucs2_src, max); + SAFE_FREE(ucs2_src); + return dest; +} + +smb_ucs2_t *strcat_wa(smb_ucs2_t *dest, const char *src) +{ + smb_ucs2_t *ucs2_src; + + if (!dest || !src) return NULL; + if (!(ucs2_src = acnv_uxu2(src))) + return NULL; + + strcat_w(dest, ucs2_src); + SAFE_FREE(ucs2_src); + return dest; +} + +BOOL trim_string_wa(smb_ucs2_t *s, const char *front, + const char *back) +{ + wpstring f, b; + + if (front) push_ucs2(NULL, f, front, sizeof(wpstring) - 1, STR_TERMINATE); + else *f = 0; + if (back) push_ucs2(NULL, b, back, sizeof(wpstring) - 1, STR_TERMINATE); + else *b = 0; + return trim_string_w(s, f, b); +} + +/******************************************************************* + returns the length in number of wide characters + ******************************************************************/ +int unistrlen(uint16 *s) +{ + int len; + + if (!s) + return -1; + + for (len=0; *s; s++,len++); + + return len; +} + +/******************************************************************* + Strcpy for unicode strings. returns length (in num of wide chars) +********************************************************************/ + +int unistrcpy(uint16 *dst, uint16 *src) +{ + int num_wchars = 0; + + while (*src) { + *dst++ = *src++; + num_wchars++; + } + *dst = 0; + + return num_wchars; +} + +/** + * Samba ucs2 type to UNISTR2 conversion + * + * @param ctx Talloc context to create the dst strcture (if null) and the + * contents of the unicode string. + * @param dst UNISTR2 destination. If equals null, then it's allocated. + * @param src smb_ucs2_t source. + * @param max_len maximum number of unicode characters to copy. If equals + * null, then null-termination of src is taken + * + * @return copied UNISTR2 destination + **/ +UNISTR2* ucs2_to_unistr2(TALLOC_CTX *ctx, UNISTR2* dst, smb_ucs2_t* src) +{ + size_t len; + + if (!src) return NULL; + len = strlen_w(src); + + /* allocate UNISTR2 destination if not given */ + if (!dst) { + dst = (UNISTR2*) talloc(ctx, sizeof(UNISTR2)); + if (!dst) return NULL; + } + if (!dst->buffer) { + dst->buffer = (uint16*) talloc(ctx, sizeof(uint16) * (len + 1)); + if (!dst->buffer) return NULL; + } + + /* set UNISTR2 parameters */ + dst->uni_max_len = len + 1; + dst->undoc = 0; + dst->uni_str_len = len; + + /* copy the actual unicode string */ + strncpy_w(dst->buffer, src, dst->uni_max_len); + + return dst; +}; + diff --git a/source4/lib/util_uuid.c b/source4/lib/util_uuid.c new file mode 100644 index 0000000000..76eb93a9b8 --- /dev/null +++ b/source4/lib/util_uuid.c @@ -0,0 +1,104 @@ +/* + * Unix SMB/CIFS implementation. + * UUID server routines + * Copyright (C) Theodore Ts'o 1996, 1997, + * Copyright (C) Jim McDonough 2002. + * + * 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" + +/* + * Offset between 15-Oct-1582 and 1-Jan-70 + */ +#define TIME_OFFSET_HIGH 0x01B21DD2 +#define TIME_OFFSET_LOW 0x13814000 + +struct uuid { + uint32 time_low; + uint16 time_mid; + uint16 time_hi_and_version; + uint8 clock_seq[2]; + uint8 node[6]; +}; + + +static void uuid_pack(const struct uuid *uu, GUID *ptr) +{ + uint8 *out = ptr->info; + + SIVAL(out, 0, uu->time_low); + SSVAL(out, 4, uu->time_mid); + SSVAL(out, 6, uu->time_hi_and_version); + memcpy(out+8, uu->clock_seq, 2); + memcpy(out+10, uu->node, 6); +} + +static void uuid_unpack(const GUID in, struct uuid *uu) +{ + const uint8 *ptr = in.info; + + uu->time_low = IVAL(ptr, 0); + uu->time_mid = SVAL(ptr, 4); + uu->time_hi_and_version = SVAL(ptr, 6); + memcpy(uu->clock_seq, ptr+8, 2); + memcpy(uu->node, ptr+10, 6); +} + +void uuid_generate_random(GUID *out) +{ + GUID tmp; + struct uuid uu; + + generate_random_buffer(tmp.info, sizeof(tmp.info), True); + uuid_unpack(tmp, &uu); + + uu.clock_seq[0] = (uu.clock_seq[0] & 0x3F) | 0x80; + uu.time_hi_and_version = (uu.time_hi_and_version & 0x0FFF) | 0x4000; + uuid_pack(&uu, out); +} + +char *guid_to_string(const GUID in) +{ + struct uuid uu; + char *out; + + uuid_unpack(in, &uu); + + asprintf(&out, "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", + uu.time_low, uu.time_mid, uu.time_hi_and_version, + uu.clock_seq[0], uu.clock_seq[1], + uu.node[0], uu.node[1], uu.node[2], + uu.node[3], uu.node[4], uu.node[5]); + + return out; +} + +const char *uuid_string(TALLOC_CTX *mem_ctx, const GUID in) +{ + struct uuid uu; + char *out; + + uuid_unpack(in, &uu); + if (!out) return NULL; + out = talloc_asprintf(mem_ctx, + "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", + uu.time_low, uu.time_mid, uu.time_hi_and_version, + uu.clock_seq[0], uu.clock_seq[1], + uu.node[0], uu.node[1], uu.node[2], + uu.node[3], uu.node[4], uu.node[5]); + return out; +} diff --git a/source4/lib/wins_srv.c b/source4/lib/wins_srv.c new file mode 100644 index 0000000000..30e81b08ab --- /dev/null +++ b/source4/lib/wins_srv.c @@ -0,0 +1,361 @@ +/* + Unix SMB/CIFS implementation. + Samba wins server helper functions + Copyright (C) Andrew Tridgell 1992-2002 + Copyright (C) Christopher R. Hertel 2000 + Copyright (C) Tim Potter 2003 + + 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" + +/* + This is pretty much a complete rewrite of the earlier code. The main + aim of the rewrite is to add support for having multiple wins server + lists, so Samba can register with multiple groups of wins servers + and each group has a failover list of wins servers. + + Central to the way it all works is the idea of a wins server + 'tag'. A wins tag is a label for a group of wins servers. For + example if you use + + wins server = fred:192.168.2.10 mary:192.168.3.199 fred:192.168.2.61 + + then you would have two groups of wins servers, one tagged with the + name 'fred' and the other with the name 'mary'. I would usually + recommend using interface names instead of 'fred' and 'mary' but + they can be any alpha string. + + Now, how does it all work. Well, nmbd needs to register each of its + IPs with each of its names once with each group of wins servers. So + it tries registering with the first one mentioned in the list, then + if that fails it marks that WINS server dead and moves onto the next + one. + + In the client code things are a bit different. As each of the groups + of wins servers is a separate name space we need to try each of the + groups until we either succeed or we run out of wins servers to + try. If we get a negative response from a wins server then that + means the name doesn't exist in that group, so we give up on that + group and move to the next group. If we don't get a response at all + then maybe the wins server is down, in which case we need to + failover to the next one for that group. + + confused yet? (tridge) +*/ + +/* how long a server is marked dead for */ +#define DEATH_TIME 600 + +/* The list of dead wins servers is stored in gencache.tdb. Each server is + marked dead from the point of view of a given source address. We keep a + separate dead list for each src address to cope with multiple interfaces + that are not routable to each other. + */ + +#define WINS_SRV_FMT "WINS_SRV_DEAD/%s,%s" /* wins_ip,src_ip */ + +static char *wins_srv_keystr(struct in_addr wins_ip, struct in_addr src_ip) +{ + char *keystr; + + if (asprintf(&keystr, WINS_SRV_FMT, inet_ntoa(wins_ip), + inet_ntoa(src_ip)) == -1) { + DEBUG(0, ("wins_srv_is_dead: malloc error\n")); + return NULL; + } + + return keystr; +} + +/* + see if an ip is on the dead list +*/ + +BOOL wins_srv_is_dead(struct in_addr wins_ip, struct in_addr src_ip) +{ + char *keystr = wins_srv_keystr(wins_ip, src_ip); + BOOL result; + + /* If the key exists then the WINS server has been marked as dead */ + + result = gencache_get(keystr, NULL, NULL); + SAFE_FREE(keystr); + + DEBUG(4, ("wins_srv_is_dead: %s is %s\n", inet_ntoa(wins_ip), + result ? "dead" : "alive")); + + return result; +} + + +/* + mark a wins server as being alive (for the moment) +*/ +void wins_srv_alive(struct in_addr wins_ip, struct in_addr src_ip) +{ + char *keystr = wins_srv_keystr(wins_ip, src_ip); + + gencache_del(keystr); + SAFE_FREE(keystr); + + DEBUG(4, ("wins_srv_alive: marking wins server %s alive\n", + inet_ntoa(wins_ip))); +} + +/* + mark a wins server as temporarily dead +*/ +void wins_srv_died(struct in_addr wins_ip, struct in_addr src_ip) +{ + char *keystr; + + if (is_zero_ip(wins_ip) || wins_srv_is_dead(wins_ip, src_ip)) + return; + + keystr = wins_srv_keystr(wins_ip, src_ip); + + gencache_set(keystr, "DOWN", time(NULL) + DEATH_TIME); + + SAFE_FREE(keystr); + + DEBUG(4,("Marking wins server %s dead for %u seconds from source %s\n", + inet_ntoa(wins_ip), DEATH_TIME, inet_ntoa(src_ip))); +} + +/* + return the total number of wins servers, dead or not +*/ +unsigned wins_srv_count(void) +{ + const char **list; + int count = 0; + + if (lp_wins_support()) { + /* simple - just talk to ourselves */ + return 1; + } + + list = lp_wins_server_list(); + for (count=0; list && list[count]; count++) + /* nop */ ; + + return count; +} + +/* an internal convenience structure for an IP with a short string tag + attached */ +struct tagged_ip { + fstring tag; + struct in_addr ip; +}; + +/* + parse an IP string that might be in tagged format + the result is a tagged_ip structure containing the tag + and the ip in in_addr format. If there is no tag then + use the tag '*' +*/ +static void parse_ip(TALLOC_CTX *mem_ctx, struct tagged_ip *ip, const char *str) +{ + char *s = strchr(str, ':'); + if (!s) { + fstrcpy(ip->tag, "*"); + ip->ip = *interpret_addr2(mem_ctx, str); + return; + } + + ip->ip = *interpret_addr2(mem_ctx, s+1); + fstrcpy(ip->tag, str); + s = strchr(ip->tag, ':'); + if (s) *s = 0; +} + + + +/* + return the list of wins server tags. A 'tag' is used to distinguish + wins server as either belonging to the same name space or a separate + name space. Usually you would setup your 'wins server' option to + list one or more wins server per interface and use the interface + name as your tag, but you are free to use any tag you like. +*/ +char **wins_srv_tags(void) +{ + char **ret = NULL; + int count=0, i, j; + const char **list; + TALLOC_CTX *mem_ctx; + + if (lp_wins_support()) { + /* give the caller something to chew on. This makes + the rest of the logic simpler (ie. less special cases) */ + ret = (char **)malloc(sizeof(char *)*2); + if (!ret) return NULL; + ret[0] = strdup("*"); + ret[1] = NULL; + return ret; + } + + list = lp_wins_server_list(); + if (!list) + return NULL; + + mem_ctx = talloc_init("wins_ssrv_tags"); + if (!mem_ctx) { + return NULL; + } + /* yes, this is O(n^2) but n is very small */ + for (i=0;list[i];i++) { + struct tagged_ip t_ip; + + parse_ip(mem_ctx, &t_ip, list[i]); + + /* see if we already have it */ + for (j=0;j<count;j++) { + if (strcmp(ret[j], t_ip.tag) == 0) { + break; + } + } + + if (j != count) { + /* we already have it. Move along */ + continue; + } + + /* add it to the list */ + ret = (char **)Realloc(ret, (count+2) * sizeof(char *)); + ret[count] = strdup(t_ip.tag); + if (!ret[count]) break; + count++; + } + + if (count) { + /* make sure we null terminate */ + ret[count] = NULL; + } + + return ret; +} + +/* free a list of wins server tags given by wins_srv_tags */ +void wins_srv_tags_free(char **list) +{ + int i; + if (!list) return; + for (i=0; list[i]; i++) { + free(list[i]); + } + free(list); +} + + +/* + return the IP of the currently active wins server for the given tag, + or the zero IP otherwise +*/ +struct in_addr wins_srv_ip_tag(const char *tag, struct in_addr src_ip) +{ + const char **list; + int i; + struct tagged_ip t_ip; + TALLOC_CTX *mem_ctx; + + /* if we are a wins server then we always just talk to ourselves */ + if (lp_wins_support()) { + extern struct in_addr loopback_ip; + return loopback_ip; + } + + list = lp_wins_server_list(); + if (!list || !list[0]) { + struct in_addr ip; + zero_ip(&ip); + return ip; + } + + mem_ctx = talloc_init("wins_srv_ip_tag"); + /* find the first live one for this tag */ + for (i=0; list[i]; i++) { + parse_ip(mem_ctx, &t_ip, list[i]); + if (strcmp(tag, t_ip.tag) != 0) { + /* not for the right tag. Move along */ + continue; + } + if (!wins_srv_is_dead(t_ip.ip, src_ip)) { + char *src_name; + src_name = talloc_strdup(mem_ctx, inet_ntoa(src_ip)); + DEBUG(6,("Current wins server for tag '%s' with source %s is %s\n", + tag, + src_name, + inet_ntoa(t_ip.ip))); + goto exit; + } + } + + /* they're all dead - try the first one until they revive */ + for (i=0; list[i]; i++) { + parse_ip(mem_ctx, &t_ip, list[i]); + if (strcmp(tag, t_ip.tag) != 0) { + continue; + } + goto exit; + } + + /* this can't happen?? */ + zero_ip(&t_ip.ip); +exit: + talloc_destroy(mem_ctx); + return t_ip.ip; +} + + +/* + return a count of the number of IPs for a particular tag, including + dead ones +*/ +unsigned wins_srv_count_tag(const char *tag) +{ + const char **list; + int i, count=0; + TALLOC_CTX *mem_ctx; + + /* if we are a wins server then we always just talk to ourselves */ + if (lp_wins_support()) { + return 1; + } + + list = lp_wins_server_list(); + if (!list || !list[0]) { + return 0; + } + + /* find the first live one for this tag */ + mem_ctx = talloc_init("wins_srv_count_tag"); + if (!mem_ctx) { + return 0; + } + for (i=0; list[i]; i++) { + struct tagged_ip t_ip; + parse_ip(mem_ctx, &t_ip, list[i]); + if (strcmp(tag, t_ip.tag) == 0) { + count++; + } + } + talloc_destroy(mem_ctx); + + return count; +} diff --git a/source4/lib/xfile.c b/source4/lib/xfile.c new file mode 100644 index 0000000000..1534dd855e --- /dev/null +++ b/source4/lib/xfile.c @@ -0,0 +1,378 @@ +/* + Unix SMB/CIFS implementation. + stdio replacement + Copyright (C) Andrew Tridgell 2001 + + 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. +*/ + +/* + stdio is very convenient, but on some systems the file descriptor + in FILE* is 8 bits, so it fails when more than 255 files are open. + + XFILE replaces stdio. It is less efficient, but at least it works + when you have lots of files open + + The main restriction on XFILE is that it doesn't support seeking, + and doesn't support O_RDWR. That keeps the code simple. +*/ + +#include "includes.h" + +#define XBUFSIZE BUFSIZ + +static XFILE _x_stdin = { 0, NULL, NULL, XBUFSIZE, 0, O_RDONLY, X_IOFBF, 0 }; +static XFILE _x_stdout = { 1, NULL, NULL, XBUFSIZE, 0, O_WRONLY, X_IOLBF, 0 }; +static XFILE _x_stderr = { 2, NULL, NULL, 0, 0, O_WRONLY, X_IONBF, 0 }; + +XFILE *x_stdin = &_x_stdin; +XFILE *x_stdout = &_x_stdout; +XFILE *x_stderr = &_x_stderr; + +#define X_FLAG_EOF 1 +#define X_FLAG_ERROR 2 +#define X_FLAG_EINVAL 3 + +/* simulate setvbuf() */ +int x_setvbuf(XFILE *f, char *buf, int mode, size_t size) +{ + x_fflush(f); + if (f->bufused) return -1; + + /* on files being read full buffering is the only option */ + if ((f->open_flags & O_ACCMODE) == O_RDONLY) { + mode = X_IOFBF; + } + + /* destroy any earlier buffer */ + SAFE_FREE(f->buf); + f->buf = 0; + f->bufsize = 0; + f->next = NULL; + f->bufused = 0; + f->buftype = mode; + + if (f->buftype == X_IONBF) return 0; + + /* if buffering then we need some size */ + if (size == 0) size = XBUFSIZE; + + f->bufsize = size; + f->bufused = 0; + + return 0; +} + +/* allocate the buffer */ +static int x_allocate_buffer(XFILE *f) +{ + if (f->buf) return 1; + if (f->bufsize == 0) return 0; + f->buf = malloc(f->bufsize); + if (!f->buf) return 0; + f->next = f->buf; + return 1; +} + + +/* this looks more like open() than fopen(), but that is quite deliberate. + I want programmers to *think* about O_EXCL, O_CREAT etc not just + get them magically added +*/ +XFILE *x_fopen(const char *fname, int flags, mode_t mode) +{ + XFILE *ret; + + ret = (XFILE *)malloc(sizeof(XFILE)); + if (!ret) return NULL; + + memset(ret, 0, sizeof(XFILE)); + + if ((flags & O_ACCMODE) == O_RDWR) { + /* we don't support RDWR in XFILE - use file + descriptors instead */ + errno = EINVAL; + return NULL; + } + + ret->open_flags = flags; + + ret->fd = sys_open(fname, flags, mode); + if (ret->fd == -1) { + SAFE_FREE(ret); + return NULL; + } + + x_setvbuf(ret, NULL, X_IOFBF, XBUFSIZE); + + return ret; +} + +/* simulate fclose() */ +int x_fclose(XFILE *f) +{ + int ret; + + /* make sure we flush any buffered data */ + x_fflush(f); + + ret = close(f->fd); + f->fd = -1; + if (f->buf) { + /* make sure data can't leak into a later malloc */ + memset(f->buf, 0, f->bufsize); + SAFE_FREE(f->buf); + } + SAFE_FREE(f); + return ret; +} + +/* simulate fwrite() */ +size_t x_fwrite(const void *p, size_t size, size_t nmemb, XFILE *f) +{ + ssize_t ret; + size_t total=0; + + /* we might be writing unbuffered */ + if (f->buftype == X_IONBF || + (!f->buf && !x_allocate_buffer(f))) { + ret = write(f->fd, p, size*nmemb); + if (ret == -1) return -1; + return ret/size; + } + + + while (total < size*nmemb) { + size_t n = f->bufsize - f->bufused; + n = MIN(n, (size*nmemb)-total); + + if (n == 0) { + /* it's full, flush it */ + x_fflush(f); + continue; + } + + memcpy(f->buf + f->bufused, total+(const char *)p, n); + f->bufused += n; + total += n; + } + + /* when line buffered we need to flush at the last linefeed. This can + flush a bit more than necessary, but that is harmless */ + if (f->buftype == X_IOLBF && f->bufused) { + int i; + for (i=(size*nmemb)-1; i>=0; i--) { + if (*(i+(const char *)p) == '\n') { + x_fflush(f); + break; + } + } + } + + return total/size; +} + +/* thank goodness for asprintf() */ + int x_vfprintf(XFILE *f, const char *format, va_list ap) +{ + char *p; + int len, ret; + va_list ap2; + + VA_COPY(ap2, ap); + + len = vasprintf(&p, format, ap2); + if (len <= 0) return len; + ret = x_fwrite(p, 1, len, f); + SAFE_FREE(p); + return ret; +} + + int x_fprintf(XFILE *f, const char *format, ...) +{ + va_list ap; + int ret; + + va_start(ap, format); + ret = x_vfprintf(f, format, ap); + va_end(ap); + return ret; +} + +/* at least fileno() is simple! */ +int x_fileno(XFILE *f) +{ + return f->fd; +} + +/* simulate fflush() */ +int x_fflush(XFILE *f) +{ + int ret; + + if (f->flags & X_FLAG_ERROR) return -1; + + if ((f->open_flags & O_ACCMODE) != O_WRONLY) { + errno = EINVAL; + return -1; + } + + if (f->bufused == 0) return 0; + + ret = write(f->fd, f->buf, f->bufused); + if (ret == -1) return -1; + + f->bufused -= ret; + if (f->bufused > 0) { + f->flags |= X_FLAG_ERROR; + memmove(f->buf, ret + (char *)f->buf, f->bufused); + return -1; + } + + return 0; +} + +/* simulate setbuffer() */ +void x_setbuffer(XFILE *f, char *buf, size_t size) +{ + x_setvbuf(f, buf, buf?X_IOFBF:X_IONBF, size); +} + +/* simulate setbuf() */ +void x_setbuf(XFILE *f, char *buf) +{ + x_setvbuf(f, buf, buf?X_IOFBF:X_IONBF, XBUFSIZE); +} + +/* simulate setlinebuf() */ +void x_setlinebuf(XFILE *f) +{ + x_setvbuf(f, NULL, X_IOLBF, 0); +} + + +/* simulate feof() */ +int x_feof(XFILE *f) +{ + if (f->flags & X_FLAG_EOF) return 1; + return 0; +} + +/* simulate ferror() */ +int x_ferror(XFILE *f) +{ + if (f->flags & X_FLAG_ERROR) return 1; + return 0; +} + +/* fill the read buffer */ +static void x_fillbuf(XFILE *f) +{ + int n; + + if (f->bufused) return; + + if (!f->buf && !x_allocate_buffer(f)) return; + + n = read(f->fd, f->buf, f->bufsize); + if (n <= 0) return; + f->bufused = n; + f->next = f->buf; +} + +/* simulate fgetc() */ +int x_fgetc(XFILE *f) +{ + int ret; + + if (f->flags & (X_FLAG_EOF | X_FLAG_ERROR)) return EOF; + + if (f->bufused == 0) x_fillbuf(f); + + if (f->bufused == 0) { + f->flags |= X_FLAG_EOF; + return EOF; + } + + ret = *(unsigned char *)(f->next); + f->next++; + f->bufused--; + return ret; +} + +/* simulate fread */ +size_t x_fread(void *p, size_t size, size_t nmemb, XFILE *f) +{ + size_t total = 0; + while (total < size*nmemb) { + int c = x_fgetc(f); + if (c == EOF) break; + (total+(char *)p)[0] = (char)c; + total++; + } + return total/size; +} + +/* simulate fgets() */ +char *x_fgets(char *s, int size, XFILE *stream) +{ + char *s0 = s; + int l = size; + while (l>1) { + int c = x_fgetc(stream); + if (c == EOF) break; + *s++ = (char)c; + l--; + if (c == '\n') break; + } + if (l==size || x_ferror(stream)) { + return 0; + } + *s = 0; + return s0; +} + +/* trivial seek, works only for SEEK_SET and SEEK_END if SEEK_CUR is + * set then an error is returned */ +off_t x_tseek(XFILE *f, off_t offset, int whence) +{ + if (f->flags & X_FLAG_ERROR) + return -1; + + /* only SEEK_SET and SEEK_END are supported */ + /* SEEK_CUR needs internal offset counter */ + if (whence != SEEK_SET && whence != SEEK_END) { + f->flags |= X_FLAG_EINVAL; + errno = EINVAL; + return -1; + } + + /* empty the buffer */ + switch (f->open_flags & O_ACCMODE) { + case O_RDONLY: + f->bufused = 0; + break; + case O_WRONLY: + if (x_fflush(f) != 0) + return -1; + break; + default: + errno = EINVAL; + return -1; + } + + f->flags &= ~X_FLAG_EOF; + return (off_t)sys_lseek(f->fd, offset, whence); +} |