diff options
Diffstat (limited to 'source3/lib')
56 files changed, 22962 insertions, 5911 deletions
diff --git a/source3/lib/.cvsignore b/source3/lib/.cvsignore new file mode 100644 index 0000000000..07da2225c7 --- /dev/null +++ b/source3/lib/.cvsignore @@ -0,0 +1,3 @@ +*.po +*.po32 + diff --git a/source3/lib/access.c b/source3/lib/access.c index 14a84b2fb4..f12ee92799 100644 --- a/source3/lib/access.c +++ b/source3/lib/access.c @@ -1,147 +1,162 @@ /* -This module is an adaption of code from the tcpd-1.4 package written -by Wietse Venema, Eindhoven University of Technology, The Netherlands. + This module is an adaption of code from the tcpd-1.4 package written + by Wietse Venema, Eindhoven University of Technology, The Netherlands. -The code is used here with permission. + The code is used here with permission. -The code has been considerably changed from the original. Bug reports -should be sent to Andrew.Tridgell@anu.edu.au + The code has been considerably changed from the original. Bug reports + should be sent to samba@samba.org */ #include "includes.h" -#include "loadparm.h" -#define ALLOW_PURE_ADDRESSES - -extern int DEBUGLEVEL; - -#ifndef INADDR_NONE -#define INADDR_NONE ((unsigned long)~0) -#endif - - -#define FROM_ADDRLEN (4*3+3+1) -#define Good True -#define Bad False - -#define CLIENT_MATCH client_match - -/* Delimiters for lists of daemons or clients. */ - -static char sep[] = ", \t"; - -/* Constants to be used in assignments only, not in comparisons... */ - -#define YES 1 -#define NO 0 #define FAIL (-1) -/* Forward declarations. */ -BOOL allow_access(char *deny_list,char *allow_list,struct from_host *client); -static int list_match(char *list,char *item, int (*match_fn)()); -static int client_match(char *tok,char *item); -static int string_match(char *tok,char *s); -static int masked_match(char *tok, char *slash, char *s); -static int matchname(char *remotehost,struct in_addr addr); -BOOL fromhost(int sock,struct from_host *f); - - -/* Size of logical line buffer. */ -#define BUFLEN 2048 +#define ALLONES ((uint32)0xFFFFFFFF) +/* masked_match - match address against netnumber/netmask */ +static int masked_match(char *tok, char *slash, char *s) +{ + uint32 net; + uint32 mask; + uint32 addr; + + if ((addr = interpret_addr(s)) == INADDR_NONE) + return (False); + *slash = 0; + net = interpret_addr(tok); + *slash = '/'; + + if (strlen(slash + 1) > 2) { + mask = interpret_addr(slash + 1); + } else { + mask = (uint32)((ALLONES >> atoi(slash + 1)) ^ ALLONES); + } + + if (net == INADDR_NONE || mask == INADDR_NONE) { + DEBUG(0,("access: bad net/mask access control: %s\n", tok)); + return (False); + } + return ((addr & mask) == net); +} -/* return true if access should be allowed to a service*/ -BOOL check_access(int snum) +/* string_match - match string against token */ +static int string_match(char *tok,char *s, char *invalid_char) { - extern int Client; - extern struct from_host Client_info; - char *denyl,*allowl; - BOOL ret = False; + size_t tok_len; + size_t str_len; + char *cut; + + *invalid_char = '\0'; + + /* Return True if a token has the magic value "ALL". Return + * FAIL if the token is "FAIL". If the token starts with a "." + * (domain name), return True if it matches the last fields of + * the string. If the token has the magic value "LOCAL", + * return True if the string does not contain a "." + * character. If the token ends on a "." (network number), + * return True if it matches the first fields of the + * string. If the token begins with a "@" (netgroup name), + * return True if the string is a (host) member of the + * netgroup. Return True if the token fully matches the + * string. If the token is a netnumber/netmask pair, return + * True if the address is a member of the specified subnet. + */ + + if (tok[0] == '.') { /* domain: match last fields */ + if ((str_len = strlen(s)) > (tok_len = strlen(tok)) + && strcasecmp(tok, s + str_len - tok_len) == 0) + return (True); + } else if (tok[0] == '@') { /* netgroup: look it up */ +#ifdef HAVE_NETGROUP + static char *mydomain = NULL; + char *hostname = NULL; + BOOL netgroup_ok = False; + + if (!mydomain) yp_get_default_domain(&mydomain); + + if (!mydomain) { + DEBUG(0,("Unable to get default yp domain.\n")); + return False; + } + if (!(hostname = strdup(s))) { + DEBUG(1,("out of memory for strdup!\n")); + return False; + } + + netgroup_ok = innetgr(tok + 1, hostname, (char *) 0, mydomain); + + DEBUG(5,("looking for %s of domain %s in netgroup %s gave %s\n", + hostname, + mydomain, + tok+1, + BOOLSTR(netgroup_ok))); + + SAFE_FREE(hostname); + + if (netgroup_ok) return(True); +#else + DEBUG(0,("access: netgroup support is not configured\n")); + return (False); +#endif + } else if (strcasecmp(tok, "ALL") == 0) { /* all: match any */ + return (True); + } else if (strcasecmp(tok, "FAIL") == 0) { /* fail: match any */ + return (FAIL); + } else if (strcasecmp(tok, "LOCAL") == 0) { /* local: no dots */ + if (strchr_m(s, '.') == 0 && strcasecmp(s, "unknown") != 0) + return (True); + } else if (!strcasecmp(tok, s)) { /* match host name or address */ + return (True); + } else if (tok[(tok_len = strlen(tok)) - 1] == '.') { /* network */ + if (strncmp(tok, s, tok_len) == 0) + return (True); + } else if ((cut = strchr_m(tok, '/')) != 0) { /* netnumber/netmask */ + if (isdigit((int)s[0]) && masked_match(tok, cut, s)) + return (True); + } else if (strchr_m(tok, '*') != 0) { + *invalid_char = '*'; + } else if (strchr_m(tok, '?') != 0) { + *invalid_char = '?'; + } + return (False); +} - denyl = lp_hostsdeny(snum); - if (denyl) denyl = strdup(denyl); - allowl = lp_hostsallow(snum); - if (allowl) allowl = strdup(allowl); +/* client_match - match host name and address against token */ +static int client_match(char *tok,char *item) +{ + char **client = (char **)item; + int match; + char invalid_char = '\0'; + /* + * Try to match the address first. If that fails, try to match the host + * name if available. + */ - fromhost(Client,&Client_info); + if ((match = string_match(tok, client[1], &invalid_char)) == 0) { + if(invalid_char) + DEBUG(0,("client_match: address match failing due to invalid character '%c' found in \ +token '%s' in an allow/deny hosts line.\n", invalid_char, tok )); - if ((!denyl || *denyl==0) && (!allowl || *allowl==0)) - ret = True; + if (client[0][0] != 0) + match = string_match(tok, client[0], &invalid_char); - if (!ret) - { - if (!fromhost(Client,&Client_info)) - DEBUG(0,("ERROR: Can't get from_host info\n")); - else - { - if (allow_access(denyl,allowl,&Client_info)) - { - if (snum >= 0) - DEBUG(2,("Allowed connection from %s (%s) to %s\n", - Client_info.name,Client_info.addr, - lp_servicename(snum))); - ret = True; - } - else - if (snum >= 0) - DEBUG(0,("Denied connection from %s (%s) to %s\n", - Client_info.name,Client_info.addr, - lp_servicename(snum))); + if(invalid_char) + DEBUG(0,("client_match: address match failing due to invalid character '%c' found in \ +token '%s' in an allow/deny hosts line.\n", invalid_char, tok )); } - } - - if (denyl) free(denyl); - if (allowl) free(allowl); - return(ret); -} - -/* return true if access should be allowed */ -BOOL allow_access(char *deny_list,char *allow_list,struct from_host *client) -{ - /* if theres no deny list and no allow list then allow access */ - if ((!deny_list || *deny_list == 0) && (!allow_list || *allow_list == 0)) - return(True); - - /* if there is an allow list but no deny list then allow only hosts - on the allow list */ - if (!deny_list || *deny_list == 0) - return(list_match(allow_list,(char *)client,CLIENT_MATCH)); - - /* if theres a deny list but no allow list then allow - all hosts not on the deny list */ - if (!allow_list || *allow_list == 0) - return(!list_match(deny_list,(char *)client,CLIENT_MATCH)); - - /* if there are both type of list then allow all hosts on the allow list */ - if (list_match(allow_list,(char *)client,CLIENT_MATCH)) - return (True); - - /* if there are both type of list and it's not on the allow then - allow it if its not on the deny */ - if (list_match(deny_list,(char *)client,CLIENT_MATCH)) - return (False); - - return (True); + return (match); } /* list_match - match an item against a list of tokens with exceptions */ -/* (All modifications are marked with the initials "jkf") */ -static int list_match(char *list,char *item, int (*match_fn)()) +static int list_match(char **list,char *item, int (*match_fn)(char *, char *)) { - char *tok; - char *listcopy; /* jkf */ - int match = NO; - - /* - * jkf@soton.ac.uk -- 31 August 1994 -- Stop list_match() - * overwriting the list given as its first parameter. - */ + int match = False; - /* jkf -- can get called recursively with NULL list */ - listcopy = (list == 0) ? (char *)0 : strdup(list); + if (!list) return False; /* * Process tokens one at a time. We have exhausted all possible matches @@ -150,240 +165,158 @@ static int list_match(char *list,char *item, int (*match_fn)()) * the match is affected by any exceptions. */ - for (tok = strtok(listcopy, sep); tok ; tok = strtok(NULL, sep)) { - if (strcasecmp(tok, "EXCEPT") == 0) /* EXCEPT: give up */ + for (; *list ; list++) { + if (strcasecmp(*list, "EXCEPT") == 0) /* EXCEPT: give up */ break; - if ((match = (*match_fn) (tok, item))) /* YES or FAIL */ + if ((match = (*match_fn) (*list, item))) /* True or FAIL */ break; } - /* Process exceptions to YES or FAIL matches. */ - - if (match != NO) { - while ((tok = strtok((char *) 0, sep)) && strcasecmp(tok, "EXCEPT")) - /* VOID */ ; - if (tok == 0 || list_match((char *) 0, item, match_fn) == NO) { - if (listcopy != 0) free(listcopy); /* jkf */ - return (match); - } - } - - if (listcopy != 0) free(listcopy); /* jkf */ - return (NO); -} + /* Process exceptions to True or FAIL matches. */ + if (match != False) { + while (*list && strcasecmp(*list, "EXCEPT")) + list++; -/* client_match - match host name and address against token */ -static int client_match(char *tok,char *item) -{ - struct from_host *client = (struct from_host *) item; - int match; - - /* - * Try to match the address first. If that fails, try to match the host - * name if available. - */ + for (; *list; list++) { + if ((*match_fn) (*list, item)) /* Exception Found */ + return False; + } + } - if ((match = string_match(tok, client->addr)) == 0) - if (client->name[0] != 0) - match = string_match(tok, client->name); return (match); } -/* string_match - match string against token */ -static int string_match(char *tok,char *s) -{ - int tok_len; - int str_len; - char *cut; - - /* - * Return YES if a token has the magic value "ALL". Return FAIL if the - * token is "FAIL". If the token starts with a "." (domain name), return - * YES if it matches the last fields of the string. If the token has the - * magic value "LOCAL", return YES if the string does not contain a "." - * character. If the token ends on a "." (network number), return YES if - * it matches the first fields of the string. If the token begins with a - * "@" (netgroup name), return YES if the string is a (host) member of - * the netgroup. Return YES if the token fully matches the string. If the - * token is a netnumber/netmask pair, return YES if the address is a - * member of the specified subnet. - */ - if (tok[0] == '.') { /* domain: match last fields */ - if ((str_len = strlen(s)) > (tok_len = strlen(tok)) - && strcasecmp(tok, s + str_len - tok_len) == 0) - return (YES); - } else if (tok[0] == '@') { /* netgroup: look it up */ -#ifdef NETGROUP - static char *mydomain = NULL; - char *hostname = NULL; - BOOL netgroup_ok = False; - - if (!mydomain) yp_get_default_domain(&mydomain); - - if (!(hostname = strdup(s))) { - DEBUG(1,("out of memory for strdup!\n")); - return NO; - } - - netgroup_ok = innetgr(tok + 1, hostname, (char *) 0, mydomain); - - DEBUG(5,("looking for %s of domain %s in netgroup %s gave %s\n", - hostname, - mydomain, - tok+1, - BOOLSTR(netgroup_ok))); - -#ifdef NETGROUP_INSECURE - /* if you really want netgroups that match non qualified names - then define NETGROUP_INSECURE. It can, however, be a big - security hole */ - { - char *clnt_domain; - if (!netgroup_ok && (clnt_domain=strchr(hostname,'.'))) { - *clnt_domain++ = '\0'; - netgroup_ok = innetgr(tok + 1, hostname, (char *) 0, mydomain); +/* return true if access should be allowed */ +BOOL allow_access(char **deny_list,char **allow_list, + char *cname,char *caddr) +{ + char *client[2]; + + client[0] = cname; + client[1] = caddr; + + /* if it is loopback then always allow unless specifically denied */ + if (strcmp(caddr, "127.0.0.1") == 0) { + /* + * If 127.0.0.1 matches both allow and deny then allow. + * Patch from Steve Langasek vorlon@netexpress.net. + */ + if (deny_list && + list_match(deny_list,(char *)client,client_match) && + (!allow_list || + !list_match(allow_list,(char *)client, client_match))) { + return False; + } + return True; } - } -#endif - free(hostname); - - if (netgroup_ok) return(YES); -#else - DEBUG(0,("access: netgroup support is not configured")); - return (NO); -#endif - } else if (strcasecmp(tok, "ALL") == 0) { /* all: match any */ - return (YES); - } else if (strcasecmp(tok, "FAIL") == 0) { /* fail: match any */ - return (FAIL); - } else if (strcasecmp(tok, "LOCAL") == 0) { /* local: no dots */ - if (strchr(s, '.') == 0 && strcasecmp(s, "unknown") != 0) - return (YES); - } else if (!strcasecmp(tok, s)) { /* match host name or address */ - return (YES); - } else if (tok[(tok_len = strlen(tok)) - 1] == '.') { /* network */ - if (strncmp(tok, s, tok_len) == 0) - return (YES); - } else if ((cut = strchr(tok, '/')) != 0) { /* netnumber/netmask */ - if (isdigit(s[0]) && masked_match(tok, cut, s)) - return (YES); - } - return (NO); -} + /* if theres no deny list and no allow list then allow access */ + if ((!deny_list || *deny_list == 0) && + (!allow_list || *allow_list == 0)) { + return(True); + } -/* masked_match - match address against netnumber/netmask */ -static int masked_match(char *tok, char *slash, char *s) -{ - unsigned long net; - unsigned long mask; - unsigned long addr; - - if ((addr = interpret_addr(s)) == INADDR_NONE) - return (NO); - *slash = 0; - net = interpret_addr(tok); - *slash = '/'; - if (net == INADDR_NONE || (mask = interpret_addr(slash + 1)) == INADDR_NONE) { - DEBUG(0,("access: bad net/mask access control: %s", tok)); - return (NO); - } - return ((addr & mask) == net); + /* if there is an allow list but no deny list then allow only hosts + on the allow list */ + if (!deny_list || *deny_list == 0) + return(list_match(allow_list,(char *)client,client_match)); + + /* if theres a deny list but no allow list then allow + all hosts not on the deny list */ + if (!allow_list || *allow_list == 0) + return(!list_match(deny_list,(char *)client,client_match)); + + /* if there are both types of list then allow all hosts on the + allow list */ + if (list_match(allow_list,(char *)client,client_match)) + return (True); + + /* if there are both types of list and it's not on the allow then + allow it if its not on the deny */ + if (list_match(deny_list,(char *)client,client_match)) + return (False); + + return (True); } - -/* fromhost - find out what is at the other end of a socket */ -BOOL fromhost(int sock,struct from_host *f) +/* return true if the char* contains ip addrs only. Used to avoid +gethostbyaddr() calls */ +static BOOL only_ipaddrs_in_list(char** list) { - static struct sockaddr sa; - struct sockaddr_in *sockin = (struct sockaddr_in *) (&sa); - struct hostent *hp; - int length = sizeof(sa); - static char addr_buf[FROM_ADDRLEN]; - static char name_buf[MAXHOSTNAMELEN]; - BOOL takeAddressAsHostname = False; - - if (getpeername(sock, &sa, &length) < 0) - { - DEBUG(0,("getpeername failed\n")); - return(False); - } - - f->sin = sockin; - f->addr = strcpy(addr_buf,(char *)inet_ntoa(sockin->sin_addr)); - - /* Look up the remote host name. */ - if ((hp = gethostbyaddr((char *) &sockin->sin_addr, - sizeof(sockin->sin_addr), - AF_INET)) == 0) { - DEBUG(1,("Gethostbyaddr failed for %s\n",addr_buf)); -#ifdef ALLOW_PURE_ADDRESSES - takeAddressAsHostname = True; -#else - return(False); -#endif - } - - /* Save the host name. A later gethostbyxxx() call may clobber it. */ - f->name = StrnCpy(name_buf, - takeAddressAsHostname? f->addr : hp->h_name, - sizeof(name_buf) - 1); - - /* - * Verify that the host name does not belong to someone else. If host - * name verification fails, pretend that the host name lookup failed. - */ - if (!takeAddressAsHostname && !matchname(f->name, sockin->sin_addr)) - { - DEBUG(0,("Matchname failed\n")); - return(False); - } - - return(True); + BOOL only_ip = True; + + if (!list) return True; + + for (; *list ; list++) + { + /* factor out the special strings */ + if (!strcasecmp(*list, "ALL") || !strcasecmp(*list, "FAIL") || + !strcasecmp(*list, "EXCEPT")) + { + continue; + } + + if (!is_ipaddress(*list)) + { + char *p; + /* + * if we failed, make sure that it was not because the token + * was a network/netmask pair. Only network/netmask pairs + * have a '/' in them + */ + if ((p=strchr_m(*list, '/')) == NULL) + { + only_ip = False; + DEBUG(3,("only_ipaddrs_in_list: list has non-ip address (%s)\n", *list)); + break; + } + } + } + + return only_ip; } -/* matchname - determine if host name matches IP address */ -static int matchname(char *remotehost,struct in_addr addr) +/* return true if access should be allowed to a service for a socket */ +BOOL check_access(int sock, char **allow_list, char **deny_list) { - struct hostent *hp; - int i; - - if ((hp = Get_Hostbyname(remotehost)) == 0) { - DEBUG(0,("Get_Hostbyname(%s): lookup failure", remotehost)); - return (Bad); - } - - /* - * 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", - remotehost, hp->h_name)); - return (Bad); - } + BOOL ret = False; + BOOL only_ip = 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], (caddr_t) & addr, sizeof(addr)) == 0) - return (Good); - } - - /* - * 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", - inet_ntoa(addr), hp->h_name)); - return (Bad); -} + if ((!deny_list || *deny_list==0) && (!allow_list || *allow_list==0)) + { + ret = True; + } + if (!ret) + { + /* bypass gethostbyaddr() calls if the lists only contain IP addrs */ + if (only_ipaddrs_in_list(allow_list) && only_ipaddrs_in_list(deny_list)) + { + only_ip = True; + DEBUG (3, ("check_access: no hostnames in host allow/deny list.\n")); + ret = allow_access(deny_list,allow_list, "", get_socket_addr(sock)); + } + else + { + DEBUG (3, ("check_access: hostnames in host allow/deny list.\n")); + ret = allow_access(deny_list,allow_list, get_socket_name(sock), + get_socket_addr(sock)); + } + + if (ret) + { + DEBUG(2,("Allowed connection from %s (%s)\n", + only_ip ? "" : get_socket_name(sock), + get_socket_addr(sock))); + } + else + { + DEBUG(0,("Denied connection from %s (%s)\n", + only_ip ? "" : get_socket_name(sock), + get_socket_addr(sock))); + } + } + return(ret); +} diff --git a/source3/lib/account_pol.c b/source3/lib/account_pol.c new file mode 100644 index 0000000000..07676e2202 --- /dev/null +++ b/source3/lib/account_pol.c @@ -0,0 +1,126 @@ +/* + * Unix SMB/CIFS implementation. + * account policy storage + * Copyright (C) Jean François Micouleau 1998-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 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; + char *vstring = "INFO/version"; + + if (tdb && local_pid == sys_getpid()) + return True; + tdb = tdb_open_log(lock_path("account_policy.tdb"), 0, TDB_DEFAULT, O_RDWR|O_CREAT, 0600); + if (!tdb) { + DEBUG(0,("Failed to open account policy database\n")); + return False; + } + + local_pid = sys_getpid(); + + /* handle a Samba upgrade */ + tdb_lock_bystring(tdb, vstring); + if (tdb_fetch_int32(tdb, vstring) != DATABASE_VERSION) { + tdb_traverse(tdb, tdb_traverse_delete_fn, NULL); + tdb_store_int32(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 char *decode_account_policy_name(int field) +{ + switch (field) { + case AP_MIN_PASSWORD_LEN: + return "min password length"; + case AP_PASSWORD_HISTORY: + return "password history"; + case AP_USER_MUST_LOGON_TO_CHG_PASS: + return "user must logon to change password"; + case AP_MAX_PASSWORD_AGE: + return "maximum password age"; + case AP_MIN_PASSWORD_AGE: + return "minimum password age"; + case AP_LOCK_ACCOUNT_DURATION: + return "lockout duration"; + case AP_RESET_COUNT_TIME: + return "reset count minutes"; + case AP_BAD_ATTEMPT_LOCKOUT: + return "bad lockout attempt"; + case AP_TIME_TO_LOGOUT: + return "disconnect time"; + default: + return "undefined value"; + } +} + + +/**************************************************************************** +****************************************************************************/ +BOOL account_policy_get(int field, uint32 *value) +{ + fstring name; + + init_account_policy(); + + fstrcpy(name, decode_account_policy_name(field)); + *value=tdb_fetch_int32(tdb, name); + 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 ( tdb_store_int32(tdb, name, value)== -1) + return False; + DEBUG(10,("account_policy_set: %s:%d\n", name, value)); + + return True; +} + diff --git a/source3/lib/bitmap.c b/source3/lib/bitmap.c new file mode 100644 index 0000000000..8121c38bd5 --- /dev/null +++ b/source3/lib/bitmap.c @@ -0,0 +1,139 @@ +/* + 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); +} + +/**************************************************************************** +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) +{ + 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/source3/lib/charcnv.c b/source3/lib/charcnv.c index 049390f2a4..cdfca8eb97 100644 --- a/source3/lib/charcnv.c +++ b/source3/lib/charcnv.c @@ -1,8 +1,9 @@ /* - Unix SMB/Netbios implementation. - Version 1.9. + Unix SMB/CIFS implementation. Character set conversion Extensions - Copyright (C) Andrew Tridgell 1992-1994 + 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 @@ -20,107 +21,768 @@ */ #include "includes.h" -extern int DEBUGLEVEL; -static char cvtbuf[1024]; +static pstring cvtbuf; -static mapsinited = 0; +static smb_iconv_t conv_handles[NUM_CHARSETS][NUM_CHARSETS]; -static char unix2dos[256]; -static char dos2unix[256]; +/**************************************************************************** +return the name of a charset to give to iconv() +****************************************************************************/ +static char *charset_name(charset_t ch) +{ + 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++) { + char *n1 = charset_name((charset_t)c1); + 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 descriptor conversion descriptor, created in init_iconv() + * @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 + * @retval the number of bytes occupied in the destination + **/ +size_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 == -1) srclen = strlen(src)+1; -static void initmaps() { - int k; + lazy_initialize_conv(); - for (k = 0; k < 256; k++) unix2dos[k] = k; - for (k = 0; k < 256; k++) dos2unix[k] = k; + descriptor = conv_handles[from][to]; - mapsinited = 1; + if (descriptor == (smb_iconv_t)-1 || descriptor == (smb_iconv_t)0) { + /* conversion not supported, use as is */ + int 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==-1) + { + char *reason="unknown error"; + switch(errno) + { case EINVAL: reason="Incomplete multibyte sequence"; break; + case E2BIG: reason="No more room"; + DEBUG(0, ("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; } -static void update_map(char * str) { - char *p; +/** + * 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. + * + * @retval Size in bytes of the converted string; or -1 in case of error. + **/ +size_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 == -1) return -1; + + lazy_initialize_conv(); - for (p = str; *p; p++) { - if (p[1]) { - unix2dos[(unsigned char)*p] = p[1]; - dos2unix[(unsigned char)p[1]] = *p; - p++; - } - } + 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 -1; + } + else outbuf = ob; + i_len = srclen; + o_len = destlen; + retval = smb_iconv(descriptor, + &inbuf, &i_len, + &outbuf, &o_len); + if(retval == -1) + { + 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 -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 -1; + } + + return destlen; } -static void initiso() { +/** + * 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. + * + * @retval Size in bytes of the converted string; or -1 in case of error. + **/ +size_t convert_string_talloc(TALLOC_CTX *ctx, charset_t from, charset_t to, + void const *src, size_t srclen, void **dest) +{ + void *ob; + size_t dest_len; - if (!mapsinited) initmaps(); + *dest = NULL; + dest_len=convert_string_allocate(from, to, src, srclen, (void **)&ob); + if (dest_len == -1) + return -1; + *dest = talloc_strdup(ctx, (char *)ob); + SAFE_FREE(ob); + if (*dest == NULL) + return -1; + return dest_len; +} - update_map("\241\255\242\233\243\234\244\236\245\235\246\272\247\025\250\251"); - update_map("\251\273\252\246\253\256\254\252\255\274\256\310\257\257\260\370"); - update_map("\261\361\262\375\263\264\264\265\265\266\266\024\267\371\270\267"); - update_map("\271\270\272\247\273\275\274\254\275\253\276\276\277\250\200\277"); - update_map("\301\300\302\301\303\302\304\216\305\217\306\222\307\200\310\303"); - update_map("\311\220\312\305\313\306\314\307\315\315\316\317\317\320\320\311"); - update_map("\321\245\322\321\323\322\324\323\325\324\326\231\327\312\330\325"); - update_map("\331\326\332\327\333\330\334\232\335\313\336\314\337\341\340\205"); - update_map("\341\240\342\203\343\331\344\204\345\206\346\221\347\207\350\212"); - update_map("\351\202\352\210\353\211\354\215\355\241\356\214\357\213\360\316"); - update_map("\361\244\362\225\363\242\364\223\365\332\366\224\367\366\370\362"); - update_map("\371\227\372\243\373\226\374\201\375\304\376\263\377\230"); +int unix_strupper(const char *src, size_t srclen, char *dest, size_t destlen) +{ + int size; + smb_ucs2_t *buffer=(smb_ucs2_t*)cvtbuf; + size=convert_string(CH_UNIX, CH_UCS2, src, srclen, buffer, sizeof(cvtbuf)); + if (!strupper_w(buffer) && (dest == src)) return srclen; + return convert_string(CH_UCS2, CH_UNIX, buffer, size, dest, destlen); } -/* - * Convert unix to dos - */ -char * -unix2dos_format(char *str,BOOL overwrite) +int unix_strlower(const char *src, size_t srclen, char *dest, size_t destlen) { - char *p; - char *dp; + int size; + smb_ucs2_t *buffer=(smb_ucs2_t*)cvtbuf; + size=convert_string(CH_UNIX, CH_UCS2, src, srclen, buffer, sizeof(cvtbuf)); + if (!strlower_w(buffer) && (dest == src)) return srclen; + return convert_string(CH_UCS2, CH_UNIX, buffer, size, dest, destlen); +} + - if (!mapsinited) initmaps(); - if (overwrite) { - for (p = str; *p; p++) *p = unix2dos[(unsigned char)*p]; - return str; - } else { - for (p = str, dp = cvtbuf; *p; p++,dp++) *dp = unix2dos[(unsigned char)*p]; - *dp = 0; - return cvtbuf; - } +int 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; } -/* - * Convert dos to unix - */ -char * -dos2unix_format (char *str, BOOL overwrite) + +/**************************************************************************** +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 +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 +****************************************************************************/ +int push_ascii(void *dest, const char *src, int dest_len, int flags) { - char *p; - char *dp; + int src_len = strlen(src); + pstring tmpbuf; - if (!mapsinited) initmaps(); - if (overwrite) { - for (p = str; *p; p++) *p = dos2unix[(unsigned char)*p]; - return str; - } else { - for (p = str, dp = cvtbuf; *p; p++,dp++) *dp = dos2unix[(unsigned char)*p]; - *dp = 0; - return cvtbuf; - } + /* treat a pstring as "unlimited" length */ + if (dest_len == -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_DOS, src, src_len, dest, dest_len); } +int push_ascii_fstring(void *dest, const char *src) +{ + return push_ascii(dest, src, sizeof(fstring), STR_TERMINATE); +} -/* - * Interpret character set. - */ -int -interpret_character_set (char *str, int def) +int push_ascii_pstring(void *dest, const char *src) { + return push_ascii(dest, src, sizeof(pstring), STR_TERMINATE); +} + +int 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 +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 +****************************************************************************/ +int pull_ascii(char *dest, const void *src, int dest_len, int src_len, int flags) +{ + int ret; + + if (dest_len == -1) { + dest_len = sizeof(pstring); + } + + if (flags & STR_TERMINATE) src_len = strlen(src)+1; + + 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; +} + +int pull_ascii_pstring(char *dest, const void *src) +{ + return pull_ascii(dest, src, sizeof(pstring), -1, STR_TERMINATE); +} + +int 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 +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_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 +****************************************************************************/ +int push_ucs2(const void *base_ptr, void *dest, const char *src, int dest_len, int flags) +{ + int len=0; + int src_len = strlen(src); + pstring tmpbuf; + + /* treat a pstring as "unlimited" length */ + if (dest_len == -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 + * + * @retval The number of bytes occupied by the string in the destination + **/ +int push_ucs2_talloc(TALLOC_CTX *ctx, void **dest, const char *src) +{ + int src_len = strlen(src)+1; + + *dest = NULL; + return convert_string_talloc(ctx, CH_UNIX, CH_UCS2, src, src_len, dest); +} + +/** + * Copy a string from a unix char* src to a UCS2 destination, allocating a buffer + * + * @param dest always set at least to NULL + * + * @retval The number of bytes occupied by the string in the destination + **/ +int push_ucs2_allocate(void **dest, const char *src) +{ + int src_len = strlen(src)+1; + + *dest = NULL; + return convert_string_allocate(CH_UNIX, CH_UCS2, src, src_len, 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 +****************************************************************************/ +int push_utf8(void *dest, const char *src, int dest_len, int flags) +{ + int src_len = strlen(src); + pstring tmpbuf; + + /* treat a pstring as "unlimited" length */ + if (dest_len == -1) { + dest_len = sizeof(pstring); + } - if (strequal (str, "iso8859-1")) { - initiso(); - return def; - } else { - DEBUG(0,("unrecognized character set\n")); - } - return def; + 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); +} + +int push_utf8_fstring(void *dest, const char *src) +{ + return push_utf8(dest, src, sizeof(fstring), STR_TERMINATE); +} + +int 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 + * + * @retval The number of bytes occupied by the string in the destination + **/ +int push_utf8_talloc(TALLOC_CTX *ctx, void **dest, const char *src) +{ + int src_len = strlen(src)+1; + + *dest = NULL; + return convert_string_talloc(ctx, CH_UNIX, CH_UTF8, src, src_len, 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 + * + * @retval The number of bytes occupied by the string in the destination + **/ +int push_utf8_allocate(void **dest, const char *src) +{ + int src_len = strlen(src)+1; + + *dest = NULL; + return convert_string_allocate(CH_UNIX, CH_UTF8, src, src_len, 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 +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 +****************************************************************************/ +int pull_ucs2(const void *base_ptr, char *dest, const void *src, int dest_len, int src_len, int flags) +{ + int ret; + + if (dest_len == -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) src_len = strlen_w(src)*2+2; + + /* ucs2 is always a multiple of 2 bytes */ + 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; +} + +int pull_ucs2_pstring(char *dest, const void *src) +{ + return pull_ucs2(NULL, dest, src, sizeof(pstring), -1, STR_TERMINATE); +} + +int 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 + * + * @retval The number of bytes occupied by the string in the destination + **/ +int pull_ucs2_talloc(TALLOC_CTX *ctx, void **dest, const char *src) +{ + int src_len = strlen(src)+1; + *dest = NULL; + return convert_string_talloc(ctx, CH_UCS2, CH_UNIX, src, src_len, dest); +} + +/** + * Copy a string from a UCS2 src to a unix char * destination, allocating a buffer + * + * @param dest always set at least to NULL + * + * @retval The number of bytes occupied by the string in the destination + **/ +int pull_ucs2_allocate(void **dest, const char *src) +{ + int src_len = strlen(src)+1; + *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 +****************************************************************************/ +int pull_utf8(char *dest, const void *src, int dest_len, int src_len, int flags) +{ + int ret; + + if (dest_len == -1) { + dest_len = sizeof(pstring); + } + + if (flags & STR_TERMINATE) src_len = strlen(src)+1; + + 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; +} + +int pull_utf8_pstring(char *dest, const void *src) +{ + return pull_utf8(dest, src, sizeof(pstring), -1, STR_TERMINATE); +} + +int 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 + * + * @retval The number of bytes occupied by the string in the destination + **/ +int pull_utf8_talloc(TALLOC_CTX *ctx, void **dest, const char *src) +{ + int src_len = strlen(src)+1; + *dest = NULL; + return convert_string_talloc(ctx, CH_UTF8, CH_UNIX, src, src_len, 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 + * + * @retval The number of bytes occupied by the string in the destination + **/ +int pull_utf8_allocate(void **dest, const char *src) +{ + int 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 +****************************************************************************/ +int push_string(const void *base_ptr, void *dest, const char *src, int dest_len, int flags) +{ + if (!(flags & STR_ASCII) && \ + ((flags & STR_UNICODE || \ + (SVAL(base_ptr, smb_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 +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 +****************************************************************************/ +int pull_string(const void *base_ptr, char *dest, const void *src, int dest_len, int src_len, + int flags) +{ + if (!(flags & STR_ASCII) && \ + ((flags & STR_UNICODE || \ + (SVAL(base_ptr, smb_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); +} + +int align_string(const void *base_ptr, const char *p, int flags) +{ + if (!(flags & STR_ASCII) && \ + ((flags & STR_UNICODE || \ + (SVAL(base_ptr, smb_flg2) & FLAGS2_UNICODE_STRINGS)))) { + return ucs2_align(base_ptr, p, flags); + } + return 0; +} + + + +/**************************************************************************** +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 == -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 == -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 == -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 == -1) return NULL; + else return dest; } diff --git a/source3/lib/charset.c b/source3/lib/charset.c deleted file mode 100644 index ada3ef790a..0000000000 --- a/source3/lib/charset.c +++ /dev/null @@ -1,111 +0,0 @@ -/* - Unix SMB/Netbios implementation. - Version 1.9. - Character set handling - Copyright (C) Andrew Tridgell 1992-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. -*/ - -#define CHARSET_C -#include "includes.h" - -extern int DEBUGLEVEL; - -char xx_dos_char_map[256]; -char xx_upper_char_map[256]; -char xx_lower_char_map[256]; - -char *dos_char_map = NULL; -char *upper_char_map = NULL; -char *lower_char_map = NULL; - -static void add_dos_char(int lower, int upper) -{ - DEBUG(6,("Adding chars 0%o 0%o\n",lower,upper)); - if (lower) dos_char_map[(char)lower] = 1; - if (upper) dos_char_map[(char)upper] = 1; - if (lower && upper) { - lower_char_map[(char)upper] = (char)lower; - upper_char_map[(char)lower] = (char)upper; - } -} - -/**************************************************************************** -initialise the charset arrays -****************************************************************************/ -void charset_initialise(void) -{ - int i; - - dos_char_map = &xx_dos_char_map[128]; - upper_char_map = &xx_upper_char_map[128]; - lower_char_map = &xx_lower_char_map[128]; - - for (i= -128;i<=127;i++) { - dos_char_map[(char)i] = 0; - } - - for (i=0;i<=127;i++) { - if (isalnum((char)i) || strchr("._^$~!#%&-{}()@'`",(char)i)) - add_dos_char(i,0); - } - - for (i= -128;i<=127;i++) { - char c = (char)i; - upper_char_map[i] = lower_char_map[i] = c; - if (isupper(c)) lower_char_map[c] = tolower(c); - if (islower(c)) upper_char_map[c] = toupper(c); - } - - /* valid for all DOS PC */ - add_dos_char(142,0); /* A trema */ - add_dos_char(143,0); /* A o */ - add_dos_char(144,0); /* E ' */ - add_dos_char(146,0); /* AE */ - add_dos_char(153,0); /* O trema */ - add_dos_char(154,0); /* U trema */ - add_dos_char(165,0); /* N tilda */ - add_dos_char(128,0); /* C cedille */ - add_dos_char(156,0); /* Pound */ - add_dos_char(183,0); /* A ` (WIN)*/ - add_dos_char(157,0); /* Phi (WIN)*/ - add_dos_char(212,0); /* E` (WIN)*/ -} - - -/******************************************************************* -add characters depending on a string passed by the user -********************************************************************/ -void add_char_string(char *s) -{ - char *extra_chars = (char *)strdup(s); - char *t; - if (!extra_chars) return; - - for (t=strtok(extra_chars," \t\r\n"); t; t=strtok(NULL," \t\r\n")) { - char c1=0,c2=0; - int i1=0,i2=0; - if (isdigit(*t) || (*t)=='-') { - sscanf(t,"%i:%i",&i1,&i2); - add_dos_char(i1,i2); - } else { - sscanf(t,"%c:%c",&c1,&c2); - add_dos_char(c1,c2); - } - } - - free(extra_chars); -} diff --git a/source3/lib/crc32.c b/source3/lib/crc32.c new file mode 100644 index 0000000000..bd4f1633e5 --- /dev/null +++ b/source3/lib/crc32.c @@ -0,0 +1,67 @@ +/* + * Copyright Francesco Ferrara, 1998 <francesco@aerra.it> + * + * Used by kind permission, 14th October 1998. http://www.aerre.it/francesco + * + * + */ + +#include "includes.h" + +static unsigned long CRCTable[256] = +{ + 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 +}; + +uint32 crc32_calc_buffer( char *buffer, uint32 count) +{ + uint32 crc=0xffffffff, i; + for(i=0;i<count;i++) + crc = (crc>>8) ^ CRCTable[(buffer[i] ^ crc) & 0xff]; + crc^=0xffffffff; + DEBUG(10,("crc32_calc_buffer: %x\n", crc)); + dump_data(100, buffer, count); + return crc; +} diff --git a/source3/lib/debug.c b/source3/lib/debug.c new file mode 100644 index 0000000000..7960c32b58 --- /dev/null +++ b/source3/lib/debug.c @@ -0,0 +1,771 @@ +/* + Unix SMB/CIFS implementation. + Samba 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" + +/* -------------------------------------------------------------------------- ** + * Defines... + * + * FORMAT_BUFR_MAX - Index of the last byte of the format buffer; + * format_bufr[FORMAT_BUFR_MAX] should always be reserved + * for a terminating null byte. + */ + +#define FORMAT_BUFR_MAX ( sizeof( format_bufr ) - 1 ) + +/* -------------------------------------------------------------------------- ** + * This module implements Samba's debugging utility. + * + * The syntax of a debugging log file is represented as: + * + * <debugfile> :== { <debugmsg> } + * + * <debugmsg> :== <debughdr> '\n' <debugtext> + * + * <debughdr> :== '[' TIME ',' LEVEL ']' [ [FILENAME ':'] [FUNCTION '()'] ] + * + * <debugtext> :== { <debugline> } + * + * <debugline> :== TEXT '\n' + * + * TEXT is a string of characters excluding the newline character. + * LEVEL is the DEBUG level of the message (an integer in the range 0..10). + * TIME is a timestamp. + * FILENAME is the name of the file from which the debug message was generated. + * FUNCTION is the function from which the debug message was generated. + * + * Basically, what that all means is: + * + * - A debugging log file is made up of debug messages. + * + * - Each debug message is made up of a header and text. The header is + * separated from the text by a newline. + * + * - The header begins with the timestamp and debug level of the message + * enclosed in brackets. The filename and function from which the + * message was generated may follow. The filename is terminated by a + * colon, and the function name is terminated by parenthesis. + * + * - The message text is made up of zero or more lines, each terminated by + * a newline. + */ + +/* -------------------------------------------------------------------------- ** + * External variables. + * + * dbf - Global debug file handle. + * debugf - Debug file name. + * append_log - If True, then the output file will be opened in append + * mode. + * DEBUGLEVEL - System-wide debug message limit. Messages with message- + * levels higher than DEBUGLEVEL will not be processed. + */ + +XFILE *dbf = NULL; +pstring debugf = ""; +BOOL append_log = False; + +int DEBUGLEVEL_CLASS[DBGC_LAST]; +BOOL DEBUGLEVEL_CLASS_ISSET[DBGC_LAST]; +int DEBUGLEVEL = DEBUGLEVEL_CLASS; +BOOL AllowDebugChange = True; + + +/* -------------------------------------------------------------------------- ** + * Internal variables. + * + * stdout_logging - Default False, if set to True then dbf will be set to + * stdout and debug output will go to dbf only, and not + * to syslog. Set in setup_logging() and read in Debug1(). + * + * debug_count - Number of debug messages that have been output. + * Used to check log size. + * + * syslog_level - Internal copy of the message debug level. Written by + * dbghdr() and read by Debug1(). + * + * format_bufr - Used to format debug messages. The dbgtext() function + * prints debug messages to a string, and then passes the + * string to format_debug_text(), which uses format_bufr + * to build the formatted output. + * + * format_pos - Marks the first free byte of the format_bufr. + * + * + * log_overflow - When this variable is True, never attempt to check the + * size of the log. This is a hack, so that we can write + * a message using DEBUG, from open_logs() when we + * are unable to open a new log file for some reason. + */ + +static BOOL stdout_logging = False; +static int debug_count = 0; +#ifdef WITH_SYSLOG +static int syslog_level = 0; +#endif +static pstring format_bufr = { '\0' }; +static size_t format_pos = 0; +static BOOL log_overflow = False; + +/* + * Define all the debug class selection names here. Names *MUST NOT* contain + * white space. There must be one name for each DBGC_<class name>, and they + * must be in the table in the order of DBGC_<class name>.. + */ +char *classname_table[] = { + "all", /* DBGC_ALL; index refs traditional DEBUGLEVEL */ + "tdb", /* DBGC_TDB */ + "printdrivers", /* DBGC_PRINTDRIVERS */ + "lanman", /* DBGC_LANMAN */ + "smb", /* DBGC_SMB */ + "rpc", /* DBGC_RPC */ + "rpc_hdr", /* DBGC_RPC_HDR */ + "bdc", /* DBGC_BDC */ +}; + + +/* -------------------------------------------------------------------------- ** + * Functions... + */ + +/**************************************************************************** +utility access to debug class names's +****************************************************************************/ +char* debug_classname_from_index(int ndx) +{ + return classname_table[ndx]; +} + +/**************************************************************************** +utility to translate names to debug class index's +****************************************************************************/ +int debug_lookup_classname(char* classname) +{ + int i; + + if (!classname) return -1; + + for (i=0; i<DBGC_LAST; i++) { + if (strcmp(classname, classname_table[i])==0) + return i; + } + return -1; +} + +/**************************************************************************** +parse the debug levels from smbcontrol. Example debug level parameter: + printdrivers:7 +****************************************************************************/ +BOOL debug_parse_params(char **params, int *debuglevel_class, + BOOL *debuglevel_class_isset) +{ + int i, ndx; + char *class_name; + char *class_level; + + /* Set the new debug level array to the current DEBUGLEVEL array */ + memcpy(debuglevel_class, DEBUGLEVEL_CLASS, sizeof(DEBUGLEVEL_CLASS)); + + /* Allow DBGC_ALL to be specified w/o requiring its class name e.g."10" + * v.s. "all:10", this is the traditional way to set DEBUGLEVEL + */ + if (isdigit((int)params[0][0])) { + debuglevel_class[DBGC_ALL] = atoi(params[0]); + debuglevel_class_isset[DBGC_ALL] = True; + i = 1; /* start processing at the next params */ + } + else + i = 0; /* DBGC_ALL not specified OR class name was included */ + + /* Fill in new debug class levels */ + for (; i < DBGC_LAST && params[i]; i++) { + if ((class_name=strtok(params[i],":")) && + (class_level=strtok(NULL, "\0")) && + ((ndx = debug_lookup_classname(class_name)) != -1)) { + debuglevel_class[ndx] = atoi(class_level); + debuglevel_class_isset[ndx] = True; + } else { + DEBUG(0,("debug_parse_params: unrecognized debug class name or format [%s]\n", params[i])); + return False; + } + } + + return True; +} + +/**************************************************************************** +parse the debug levels from smb.conf. Example debug level string: + 3 tdb:5 printdrivers:7 +Note: the 1st param has no "name:" preceeding it. +****************************************************************************/ +BOOL debug_parse_levels(char *params_str) +{ + int i; + char *params[DBGC_LAST]; + int debuglevel_class[DBGC_LAST]; + BOOL debuglevel_class_isset[DBGC_LAST]; + + if (AllowDebugChange == False) + return True; + ZERO_ARRAY(params); + ZERO_ARRAY(debuglevel_class); + ZERO_ARRAY(debuglevel_class_isset); + + if ((params[0]=strtok(params_str," ,"))) { + for (i=1; i<DBGC_LAST;i++) { + if ((params[i]=strtok(NULL," ,"))==NULL) + break; + } + } + else + return False; + + if (debug_parse_params(params, debuglevel_class, + debuglevel_class_isset)) { + debug_message(0, sys_getpid(), (void*)debuglevel_class, sizeof(debuglevel_class)); + + memcpy(DEBUGLEVEL_CLASS, debuglevel_class, + sizeof(debuglevel_class)); + + memcpy(DEBUGLEVEL_CLASS_ISSET, debuglevel_class_isset, + sizeof(debuglevel_class_isset)); + + { + int q; + + for (q = 0; q < DBGC_LAST; q++) + DEBUG(5, ("%s: %d/%d\n", + classname_table[q], + DEBUGLEVEL_CLASS[q], + DEBUGLEVEL_CLASS_ISSET[q])); + } + + return True; + } else + return False; +} + +/**************************************************************************** +receive a "set debug level" message +****************************************************************************/ +void debug_message(int msg_type, pid_t src, void *buf, size_t len) +{ + struct debuglevel_message *dm = (struct debuglevel_message *)buf; + int i; + + /* Set the new DEBUGLEVEL_CLASS array from the passed message */ + memcpy(DEBUGLEVEL_CLASS, dm->debuglevel_class, sizeof(dm->debuglevel_class)); + memcpy(DEBUGLEVEL_CLASS_ISSET, dm->debuglevel_class_isset, sizeof(dm->debuglevel_class_isset)); + + DEBUG(3,("INFO: Debug class %s level = %d (pid %u from pid %u)\n", + classname_table[DBGC_ALL], + DEBUGLEVEL_CLASS[DBGC_ALL], (unsigned int)sys_getpid(), (unsigned int)src)); + + for (i=1; i<DBGC_LAST; i++) { + if (DEBUGLEVEL_CLASS[i]) + DEBUGADD(3,("INFO: Debug class %s level = %d\n", + classname_table[i], DEBUGLEVEL_CLASS[i])); + } +} + + +/**************************************************************************** +send a "set debug level" message +****************************************************************************/ +void debug_message_send(pid_t pid, int level) +{ + message_send_pid(pid, MSG_DEBUG, &level, sizeof(int), False); +} + + +/* ************************************************************************** ** + * get ready for syslog stuff + * ************************************************************************** ** + */ +void setup_logging(char *pname, BOOL interactive) +{ + message_register(MSG_DEBUG, debug_message); + + /* reset to allow multiple setup calls, going from interactive to + non-interactive */ + stdout_logging = False; + dbf = NULL; + + if (interactive) { + stdout_logging = True; + dbf = x_stdout; + } +#ifdef WITH_SYSLOG + else { + char *p = strrchr_m( pname,'/' ); + if (p) + pname = p + 1; +#ifdef LOG_DAEMON + openlog( pname, LOG_PID, SYSLOG_FACILITY ); +#else + /* for old systems that have no facility codes. */ + openlog( pname, LOG_PID ); +#endif + } +#endif +} /* setup_logging */ + +/* ************************************************************************** ** + * reopen the log files + * note that we now do this unconditionally + * We attempt to open the new debug fp before closing the old. This means + * if we run out of fd's we just keep using the old fd rather than aborting. + * Fix from dgibson@linuxcare.com. + * ************************************************************************** ** + */ + +BOOL reopen_logs( void ) +{ + pstring fname; + mode_t oldumask; + XFILE *new_dbf = NULL; + BOOL ret = True; + + if (stdout_logging) + return True; + + oldumask = umask( 022 ); + + pstrcpy(fname, debugf ); + + if (lp_loaded()) { + char *logfname; + + logfname = lp_logfile(); + if (*logfname) + pstrcpy(fname, logfname); + } + + pstrcpy( debugf, fname ); + if (append_log) + new_dbf = x_fopen( debugf, O_WRONLY|O_APPEND|O_CREAT, 0644); + else + new_dbf = x_fopen( debugf, O_WRONLY|O_CREAT|O_TRUNC, 0644 ); + + if (!new_dbf) { + log_overflow = True; + DEBUG(0, ("Unable to open new log file %s: %s\n", debugf, strerror(errno))); + log_overflow = False; + if (dbf) + x_fflush(dbf); + ret = False; + } else { + x_setbuf(new_dbf, NULL); + if (dbf) + (void) x_fclose(dbf); + dbf = new_dbf; + } + + /* Fix from klausr@ITAP.Physik.Uni-Stuttgart.De + * to fix problem where smbd's that generate less + * than 100 messages keep growing the log. + */ + force_check_log_size(); + (void)umask(oldumask); + + return ret; +} + +/* ************************************************************************** ** + * Force a check of the log size. + * ************************************************************************** ** + */ +void force_check_log_size( void ) +{ + debug_count = 100; +} + +/*************************************************************************** + Check to see if there is any need to check if the logfile has grown too big. +**************************************************************************/ + +BOOL need_to_check_log_size( void ) +{ + int maxlog; + + if( debug_count++ < 100 ) + return( False ); + + maxlog = lp_max_log_size() * 1024; + if( !dbf || maxlog <= 0 ) { + debug_count = 0; + return(False); + } + return( True ); +} + +/* ************************************************************************** ** + * Check to see if the log has grown to be too big. + * ************************************************************************** ** + */ + +void check_log_size( void ) +{ + int maxlog; + SMB_STRUCT_STAT st; + + /* + * We need to be root to check/change log-file, skip this and let the main + * loop check do a new check as root. + */ + + if( geteuid() != 0 ) + return; + + if(log_overflow || !need_to_check_log_size() ) + return; + + maxlog = lp_max_log_size() * 1024; + + if( sys_fstat( x_fileno( dbf ), &st ) == 0 && st.st_size > maxlog ) { + (void)reopen_logs(); + if( dbf && get_file_size( debugf ) > maxlog ) { + pstring name; + + slprintf( name, sizeof(name)-1, "%s.old", debugf ); + (void)rename( debugf, name ); + + if (!reopen_logs()) { + /* We failed to reopen a log - continue using the old name. */ + (void)rename(name, debugf); + } + } + } + + /* + * Here's where we need to panic if dbf == NULL.. + */ + + if(dbf == NULL) { + /* This code should only be reached in very strange + * circumstances. If we merely fail to open the new log we + * should stick with the old one. ergo this should only be + * reached when opening the logs for the first time: at + * startup or when the log level is increased from zero. + * -dwg 6 June 2000 + */ + dbf = x_fopen( "/dev/console", O_WRONLY, 0); + if(dbf) { + DEBUG(0,("check_log_size: open of debug file %s failed - using console.\n", + debugf )); + } else { + /* + * We cannot continue without a debug file handle. + */ + abort(); + } + } + debug_count = 0; +} /* check_log_size */ + +/* ************************************************************************** ** + * Write an debug message on the debugfile. + * This is called by dbghdr() and format_debug_text(). + * ************************************************************************** ** + */ + int Debug1( char *format_str, ... ) +{ + va_list ap; + int old_errno = errno; + + if( stdout_logging ) + { + va_start( ap, format_str ); + if(dbf) + (void)x_vfprintf( dbf, format_str, ap ); + va_end( ap ); + errno = old_errno; + return( 0 ); + } + +#ifdef WITH_SYSLOG + if( !lp_syslog_only() ) +#endif + { + if( !dbf ) + { + mode_t oldumask = umask( 022 ); + + if( append_log ) + dbf = x_fopen( debugf, O_WRONLY|O_APPEND|O_CREAT, 0644 ); + else + dbf = x_fopen( debugf, O_WRONLY|O_CREAT|O_TRUNC, 0644 ); + (void)umask( oldumask ); + if( dbf ) + { + x_setbuf( dbf, NULL ); + } + else + { + errno = old_errno; + return(0); + } + } + } + +#ifdef WITH_SYSLOG + if( syslog_level < lp_syslog() ) + { + /* map debug levels to syslog() priorities + * note that not all DEBUG(0, ...) calls are + * necessarily errors + */ + static int priority_map[] = { + LOG_ERR, /* 0 */ + LOG_WARNING, /* 1 */ + LOG_NOTICE, /* 2 */ + LOG_INFO, /* 3 */ + }; + int priority; + pstring msgbuf; + + if( syslog_level >= ( sizeof(priority_map) / sizeof(priority_map[0]) ) + || syslog_level < 0) + priority = LOG_DEBUG; + else + priority = priority_map[syslog_level]; + + va_start( ap, format_str ); + vslprintf( msgbuf, sizeof(msgbuf)-1, format_str, ap ); + va_end( ap ); + + msgbuf[255] = '\0'; + syslog( priority, "%s", msgbuf ); + } +#endif + + check_log_size(); + +#ifdef WITH_SYSLOG + if( !lp_syslog_only() ) +#endif + { + va_start( ap, format_str ); + if(dbf) + (void)x_vfprintf( dbf, format_str, ap ); + va_end( ap ); + if(dbf) + (void)x_fflush( dbf ); + } + + errno = old_errno; + + return( 0 ); + } /* Debug1 */ + + +/* ************************************************************************** ** + * Print the buffer content via Debug1(), then reset the buffer. + * + * Input: none + * Output: none + * + * ************************************************************************** ** + */ +static void bufr_print( void ) + { + format_bufr[format_pos] = '\0'; + (void)Debug1( "%s", format_bufr ); + format_pos = 0; + } /* bufr_print */ + +/* ************************************************************************** ** + * Format the debug message text. + * + * Input: msg - Text to be added to the "current" debug message text. + * + * Output: none. + * + * Notes: The purpose of this is two-fold. First, each call to syslog() + * (used by Debug1(), see above) generates a new line of syslog + * output. This is fixed by storing the partial lines until the + * newline character is encountered. Second, printing the debug + * message lines when a newline is encountered allows us to add + * spaces, thus indenting the body of the message and making it + * more readable. + * + * ************************************************************************** ** + */ +static void format_debug_text( char *msg ) + { + size_t i; + BOOL timestamp = (!stdout_logging && (lp_timestamp_logs() || + !(lp_loaded()))); + + for( i = 0; msg[i]; i++ ) + { + /* Indent two spaces at each new line. */ + if(timestamp && 0 == format_pos) + { + format_bufr[0] = format_bufr[1] = ' '; + format_pos = 2; + } + + /* If there's room, copy the character to the format buffer. */ + if( format_pos < FORMAT_BUFR_MAX ) + format_bufr[format_pos++] = msg[i]; + + /* If a newline is encountered, print & restart. */ + if( '\n' == msg[i] ) + bufr_print(); + + /* If the buffer is full dump it out, reset it, and put out a line + * continuation indicator. + */ + if( format_pos >= FORMAT_BUFR_MAX ) + { + bufr_print(); + (void)Debug1( " +>\n" ); + } + } + + /* Just to be safe... */ + format_bufr[format_pos] = '\0'; + } /* format_debug_text */ + +/* ************************************************************************** ** + * Flush debug output, including the format buffer content. + * + * Input: none + * Output: none + * + * ************************************************************************** ** + */ +void dbgflush( void ) + { + bufr_print(); + if(dbf) + (void)x_fflush( dbf ); + } /* dbgflush */ + +/* ************************************************************************** ** + * Print a Debug Header. + * + * Input: level - Debug level of the message (not the system-wide debug + * level. ) + * file - Pointer to a string containing the name of the file + * from which this function was called, or an empty string + * if the __FILE__ macro is not implemented. + * func - Pointer to a string containing the name of the function + * from which this function was called, or an empty string + * if the __FUNCTION__ macro is not implemented. + * line - line number of the call to dbghdr, assuming __LINE__ + * works. + * + * Output: Always True. This makes it easy to fudge a call to dbghdr() + * in a macro, since the function can be called as part of a test. + * Eg: ( (level <= DEBUGLEVEL) && (dbghdr(level,"",line)) ) + * + * Notes: This function takes care of setting syslog_level. + * + * ************************************************************************** ** + */ + +BOOL dbghdr( int level, char *file, char *func, int line ) +{ + /* Ensure we don't lose any real errno value. */ + int old_errno = errno; + + if( format_pos ) { + /* This is a fudge. If there is stuff sitting in the format_bufr, then + * the *right* thing to do is to call + * format_debug_text( "\n" ); + * to write the remainder, and then proceed with the new header. + * Unfortunately, there are several places in the code at which + * the DEBUG() macro is used to build partial lines. That in mind, + * we'll work under the assumption that an incomplete line indicates + * that a new header is *not* desired. + */ + return( True ); + } + +#ifdef WITH_SYSLOG + /* Set syslog_level. */ + syslog_level = level; +#endif + + /* Don't print a header if we're logging to stdout. */ + if( stdout_logging ) + return( True ); + + /* Print the header if timestamps are turned on. If parameters are + * not yet loaded, then default to timestamps on. + */ + if( lp_timestamp_logs() || !(lp_loaded()) ) { + char header_str[200]; + + header_str[0] = '\0'; + + if( lp_debug_pid()) + slprintf(header_str,sizeof(header_str)-1,", pid=%u",(unsigned int)sys_getpid()); + + if( lp_debug_uid()) { + size_t hs_len = strlen(header_str); + slprintf(header_str + hs_len, + sizeof(header_str) - 1 - hs_len, + ", effective(%u, %u), real(%u, %u)", + (unsigned int)geteuid(), (unsigned int)getegid(), + (unsigned int)getuid(), (unsigned int)getgid()); + } + + /* Print it all out at once to prevent split syslog output. */ + (void)Debug1( "[%s, %d%s] %s:%s(%d)\n", + timestring(lp_debug_hires_timestamp()), level, + header_str, file, func, line ); + } + + errno = old_errno; + return( True ); +} + +/* ************************************************************************** ** + * Add text to the body of the "current" debug message via the format buffer. + * + * Input: format_str - Format string, as used in printf(), et. al. + * ... - Variable argument list. + * + * ..or.. va_alist - Old style variable parameter list starting point. + * + * Output: Always True. See dbghdr() for more info, though this is not + * likely to be used in the same way. + * + * ************************************************************************** ** + */ + BOOL dbgtext( char *format_str, ... ) + { + va_list ap; + pstring msgbuf; + + va_start( ap, format_str ); + vslprintf( msgbuf, sizeof(msgbuf)-1, format_str, ap ); + va_end( ap ); + + format_debug_text( msgbuf ); + + return( True ); + } /* dbgtext */ + + +/* ************************************************************************** */ diff --git a/source3/lib/dmallocmsg.c b/source3/lib/dmallocmsg.c new file mode 100644 index 0000000000..a83ed518d7 --- /dev/null +++ b/source3/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/source3/lib/dprintf.c b/source3/lib/dprintf.c new file mode 100644 index 0000000000..dadebdb3b4 --- /dev/null +++ b/source3/lib/dprintf.c @@ -0,0 +1,110 @@ +/* + 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; + + /* do any message translations */ + msgstr = lang_msg(format); + if (!msgstr) return -1; + + ret = vasprintf(&p, msgstr, ap); + + 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/source3/lib/error.c b/source3/lib/error.c new file mode 100644 index 0000000000..22ac1cb2d7 --- /dev/null +++ b/source3/lib/error.c @@ -0,0 +1,72 @@ +/* + * Unix SMB/CIFS implementation. + * Unix/DOS/NT error code conversions + * Copyright (C) Tim Potter 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" + +/* Mapping between Unix, DOS and NT error numbers */ + +const struct unix_error_map unix_dos_nt_errmap[] = { + { EPERM, ERRDOS, ERRnoaccess, NT_STATUS_ACCESS_DENIED }, + { EACCES, ERRDOS, ERRnoaccess, NT_STATUS_ACCESS_DENIED }, + { ENOENT, ERRDOS, ERRbadfile, NT_STATUS_NO_SUCH_FILE }, + { ENOTDIR, ERRDOS, ERRbadpath, NT_STATUS_NOT_A_DIRECTORY }, + { EIO, ERRHRD, ERRgeneral, NT_STATUS_IO_DEVICE_ERROR }, + { EBADF, ERRSRV, ERRsrverror, NT_STATUS_INVALID_HANDLE }, + { EINVAL, ERRSRV, ERRsrverror, NT_STATUS_INVALID_HANDLE }, + { EEXIST, ERRDOS, ERRfilexists, NT_STATUS_OBJECT_NAME_COLLISION}, + { ENFILE, ERRDOS, ERRnofids, NT_STATUS_TOO_MANY_OPENED_FILES }, + { EMFILE, ERRDOS, ERRnofids, NT_STATUS_TOO_MANY_OPENED_FILES }, + { ENOSPC, ERRHRD, ERRdiskfull, NT_STATUS_DISK_FULL }, +#ifdef EDQUOT + { EDQUOT, ERRHRD, ERRdiskfull, NT_STATUS_DISK_FULL }, +#endif +#ifdef ENOTEMPTY + { ENOTEMPTY, ERRDOS, ERRnoaccess, NT_STATUS_DIRECTORY_NOT_EMPTY }, +#endif +#ifdef EXDEV + { EXDEV, ERRDOS, ERRdiffdevice, NT_STATUS_NOT_SAME_DEVICE }, +#endif +#ifdef EROFS + { EROFS, ERRHRD, ERRnowrite, NT_STATUS_ACCESS_DENIED }, +#endif + { 0, 0, 0, NT_STATUS_OK } +}; + +/********************************************************************* + Map an NT error code from a Unix error code. +*********************************************************************/ + +NTSTATUS map_nt_error_from_unix(int unix_error) +{ + int i = 0; + + if (unix_error == 0) + return NT_STATUS_OK; + + /* Look through list */ + while(unix_dos_nt_errmap[i].unix_error != 0) { + if (unix_dos_nt_errmap[i].unix_error == unix_error) + return unix_dos_nt_errmap[i].nt_error; + i++; + } + + /* Default return */ + return NT_STATUS_ACCESS_DENIED; +} diff --git a/source3/lib/fault.c b/source3/lib/fault.c index 20c75f7876..e132ed8f67 100644 --- a/source3/lib/fault.c +++ b/source3/lib/fault.c @@ -1,8 +1,7 @@ /* - Unix SMB/Netbios implementation. - Version 1.9. + Unix SMB/CIFS implementation. Critical Fault handling - Copyright (C) Andrew Tridgell 1992-1995 + 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 @@ -19,44 +18,39 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#ifdef LINUX -#define __KERNEL__ -#endif - #include "includes.h" -extern int DEBUGLEVEL; - - -static void (*cont_fn)(); +static void (*cont_fn)(void *); /******************************************************************* report a fault ********************************************************************/ static void fault_report(int sig) { - DEBUG(0,("===============================================================\n")); - DEBUG(0,("INTERNAL ERROR: Signal %d in pid %d (%s)",sig,(int)getpid(),VERSION)); - DEBUG(0,("\nPlease read the file BUGS.txt in the distribution\n")); - DEBUG(0,("===============================================================\n")); + static int counter; + + if (counter) _exit(1); + + counter++; + + DEBUG(0,("===============================================================\n")); + DEBUG(0,("INTERNAL ERROR: Signal %d in pid %d (%s)",sig,(int)sys_getpid(),VERSION)); + DEBUG(0,("\nPlease read the file BUGS.txt in the distribution\n")); + DEBUG(0,("===============================================================\n")); -#if AJT - ajt_panic(); -#endif + smb_panic("internal error"); - if (cont_fn) - { - fault_setup(cont_fn); - cont_fn(NULL); + if (cont_fn) { + cont_fn(NULL); #ifdef SIGSEGV - signal(SIGSEGV,SIGNAL_CAST SIG_DFL); + CatchSignal(SIGSEGV,SIGNAL_CAST SIG_DFL); #endif #ifdef SIGBUS - signal(SIGBUS,SIGNAL_CAST SIG_DFL); + CatchSignal(SIGBUS,SIGNAL_CAST SIG_DFL); #endif - return; /* this should cause a core dump */ - } - exit(1); + return; /* this should cause a core dump */ + } + exit(1); } /**************************************************************************** @@ -64,21 +58,21 @@ catch serious errors ****************************************************************************/ static void sig_fault(int sig) { - fault_report(sig); + fault_report(sig); } /******************************************************************* setup our fault handlers ********************************************************************/ -void fault_setup(void (*fn)()) +void fault_setup(void (*fn)(void *)) { - cont_fn = fn; + cont_fn = fn; #ifdef SIGSEGV - signal(SIGSEGV,SIGNAL_CAST sig_fault); + CatchSignal(SIGSEGV,SIGNAL_CAST sig_fault); #endif #ifdef SIGBUS - signal(SIGBUS,SIGNAL_CAST sig_fault); + CatchSignal(SIGBUS,SIGNAL_CAST sig_fault); #endif } diff --git a/source3/lib/fsusage.c b/source3/lib/fsusage.c new file mode 100644 index 0000000000..bb7cff0645 --- /dev/null +++ b/source3/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/source3/lib/genrand.c b/source3/lib/genrand.c new file mode 100644 index 0000000000..6296ead726 --- /dev/null +++ b/source3/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; +unsigned char *reseed_data; +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(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 = sys_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) ]; + + retstr[i] = '\0'; + + return (char *)retstr; +} diff --git a/source3/lib/getsmbpass.c b/source3/lib/getsmbpass.c index 07a7dbfd9b..0874529d32 100644 --- a/source3/lib/getsmbpass.c +++ b/source3/lib/getsmbpass.c @@ -1,4 +1,4 @@ -/* Copyright (C) 1992, 1993, 1994 Free Software Foundation, Inc. +/* 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 @@ -40,20 +40,19 @@ static struct termio t; #define TCSANOW 0 #endif -int tcgetattr(int fd, struct termio *t) +static int tcgetattr(int fd, struct termio *t) { return ioctl(fd, TCGETA, t); } -int tcsetattr(int fd, int flags, const struct termio *t) +static int tcsetattr(int fd, int flags, struct termio *t) { if(flags & TCSAFLUSH) ioctl(fd, TCFLSH, TCIOFLUSH); return ioctl(fd, TCSETS, t); } -#else /* SYSV_TERMIO */ -#ifdef BSD_TERMIO +#elif !defined(TCSAFLUSH) /* BSD TERMIO HANDLING */ @@ -63,37 +62,28 @@ static struct sgttyb t; #define TURN_ECHO_OFF(t) ((t).sg_flags &= ~ECHO) #define TURN_ECHO_ON(t) ((t).sg_flags |= ECHO) -#ifndef TCSAFLUSH #define TCSAFLUSH 1 -#endif - -#ifndef TCSANOW #define TCSANOW 0 -#endif -int tcgetattr(int fd, struct sgttyb *t) +static int tcgetattr(int fd, struct sgttyb *t) { return ioctl(fd, TIOCGETP, (char *)t); } -int tcsetattr(int fd, int flags, const struct sgttyb *t) +static int tcsetattr(int fd, int flags, struct sgttyb *t) { return ioctl(fd, TIOCSETP, (char *)t); } -#else /* BSD_TERMIO */ - -/* POSIX TERMIO HANDLING */ +#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 /* BSD_TERMIO */ #endif /* SYSV_TERMIO */ -char * -getsmbpass(char *prompt) +char *getsmbpass(char *prompt) { FILE *in, *out; int echo_off; @@ -102,7 +92,7 @@ getsmbpass(char *prompt) size_t nread; /* Catch problematic signals */ - signal(SIGINT, SIGNAL_CAST SIG_IGN); + 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. */ @@ -154,13 +144,13 @@ getsmbpass(char *prompt) fclose (in); /* Catch problematic signals */ - signal(SIGINT, SIGNAL_CAST SIG_DFL); + CatchSignal(SIGINT, SIGNAL_CAST SIG_DFL); printf("\n"); return buf; } #else - -void getsmbpasswd_dummy() {;} + void getsmbpasswd_dummy(void); + void getsmbpasswd_dummy(void) {;} #endif diff --git a/source3/lib/hash.c b/source3/lib/hash.c new file mode 100644 index 0000000000..6b7a8476b1 --- /dev/null +++ b/source3/lib/hash.c @@ -0,0 +1,315 @@ +/* + Unix SMB/CIFS implementation. + + Copyright (C) Ying Chen 2000. + Copyright (C) Jeremy Allison 2000. + - added some defensive programming. + + 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. +*/ + +/* + * NB. We may end up replacing this functionality in a future 2.x + * release to reduce the number of hashing/lookup methods we support. JRA. + */ + +#include "includes.h" + +static BOOL enlarge_hash_table(hash_table *table); +static int primes[] = + {17, 37, 67, 131, 257, 521, 1031, 2053, 4099, 8209, 16411}; + +/**************************************************************************** + * This function initializes the hash table. + * This hash function hashes on string keys. + * This number of hash buckets is always rounded up to a power of + * 2 first, then to a prime number that is large than the power of two. + * Input: + * table -- the hash table pointer. + * num_buckets -- the number of buckets to be allocated. This + * hash function can dynamically increase its size when the + * the hash table size becomes small. There is a MAX hash table + * size defined in hash.h. + * compare_func -- the function pointer to a comparison function + * used by the hash key comparison. + **************************************************************************** + */ + +BOOL hash_table_init(hash_table *table, int num_buckets, compare_function compare_func) +{ + int i; + ubi_dlList *bucket; + + table->num_elements = 0; + table->size = 2; + table->comp_func = compare_func; + while (table->size < num_buckets) + table->size <<= 1; + for (i = 0; i < ARRAY_SIZE(primes); i++) { + if (primes[i] > table->size) { + table->size = primes[i]; + break; + } + } + + DEBUG(5, ("Hash size = %d.\n", table->size)); + + if(!(table->buckets = (ubi_dlList *) malloc(sizeof(ubi_dlList) * table->size))) { + DEBUG(0,("hash_table_init: malloc fail !\n")); + return False; + } + ubi_dlInitList(&(table->lru_chain)); + for (i=0, bucket = table->buckets; i < table->size; i++, bucket++) + ubi_dlInitList(bucket); + + return True; +} + +/* + ************************************************************** + * Compute a hash value based on a string key value. + * Make the string key into an array of int's if possible. + * For the last few chars that cannot be int'ed, use char instead. + * The function returns the bucket index number for the hashed + * key. + ************************************************************** + */ + +static int string_hash(int hash_size, const char *key) +{ + u32 value; /* Used to compute the hash value. */ + u32 i; /* Used to cycle through random values. */ + + for (value = 0x238F13AF, i=0; key[i]; i++) + value = (value + (key[i] << (i*5 % 24))); + + return (1103515243 * value + 12345) % hash_size; +} + + +/* ************************************************************************* + * Search the hash table for the entry in the hash chain. + * The function returns the pointer to the + * element found in the chain or NULL if none is found. + * If the element is found, the element is also moved to + * the head of the LRU list. + * + * Input: + * table -- The hash table where the element is stored in. + * hash_chain -- The pointer to the bucket that stores the + * element to be found. + * key -- The hash key to be found. + *************************************************************************** + */ + +static hash_element *hash_chain_find(hash_table *table, ubi_dlList *hash_chain, char *key) +{ + hash_element *hash_elem; + ubi_dlNodePtr lru_item; + int i = 0; + + for (hash_elem = (hash_element *)(ubi_dlFirst(hash_chain)); i < hash_chain->count; + i++, hash_elem = (hash_element *)(ubi_dlNext(hash_elem))) { + if ((table->comp_func)(hash_elem->key, key) == 0) { + /* Move to the head of the lru List. */ + lru_item = ubi_dlRemove(&(table->lru_chain), &(hash_elem->lru_link.lru_link)); + ubi_dlAddHead(&(table->lru_chain), lru_item); + return(hash_elem); + } + } + return ((hash_element *) NULL); +} + +/* *************************************************************************** + * + * Lookup a hash table for an element with key. + * The function returns a pointer to the hash element. + * If no element is found, the function returns NULL. + * + * Input: + * table -- The hash table to be searched on. + * key -- The key to be found. + ***************************************************************************** + */ + +hash_element *hash_lookup(hash_table *table, char *key) +{ + return (hash_chain_find(table, &table->buckets[string_hash(table->size, key)], key)); +} + +/* *************************************************************** + * + * This function first checks if an element with key "key" + * exists in the hash table. If so, the function moves the + * element to the front of the LRU list. Otherwise, a new + * hash element corresponding to "value" and "key" is allocated + * and inserted into the hash table. The new elements are + * always inserted in the LRU order to the LRU list as well. + * + * Input: + * table -- The hash table to be inserted in. + * value -- The content of the element to be inserted. + * key -- The key of the new element to be inserted. + * + **************************************************************** + */ + +hash_element *hash_insert(hash_table *table, char *value, char *key) +{ + hash_element *hash_elem; + ubi_dlNodePtr lru_item; + ubi_dlList *bucket; + + /* + * If the hash table size has not reached the MAX_HASH_TABLE_SIZE, + * the hash table may be enlarged if the current hash table is full. + * If the hash table size has reached the MAX_HASH_TABLE_SIZE, + * use LRU to remove the oldest element from the hash table. + */ + + if ((table->num_elements >= table->size) && + (table->num_elements < MAX_HASH_TABLE_SIZE)) { + if(!enlarge_hash_table(table)) + return (hash_element *)NULL; + table->num_elements += 1; + } else if (table->num_elements >= MAX_HASH_TABLE_SIZE) { + /* Do an LRU replacement. */ + lru_item = ubi_dlLast(&(table->lru_chain)); + hash_elem = (hash_element *)(((lru_node *)lru_item)->hash_elem); + bucket = hash_elem->bucket; + ubi_dlRemThis(&(table->lru_chain), &(hash_elem->lru_link.lru_link)); + ubi_dlRemThis(bucket, (ubi_dlNodePtr)hash_elem); + SAFE_FREE(hash_elem->value); + SAFE_FREE(hash_elem); + } else { + table->num_elements += 1; + } + + bucket = &table->buckets[string_hash(table->size, key)]; + + /* Since we only have 1-byte for the key string, we need to + * allocate extra space in the hash_element to store the entire key + * string. + */ + + if(!(hash_elem = (hash_element *) malloc(sizeof(hash_element) + strlen(key)))) { + DEBUG(0,("hash_insert: malloc fail !\n")); + return (hash_element *)NULL; + } + + safe_strcpy((char *) hash_elem->key, key, strlen(key)+1); + + hash_elem->value = (char *)value; + hash_elem->bucket = bucket; + /* Insert in front of the lru list and the bucket list. */ + ubi_dlAddHead(bucket, hash_elem); + hash_elem->lru_link.hash_elem = hash_elem; + ubi_dlAddHead(&(table->lru_chain), &(hash_elem->lru_link.lru_link)); + + return(hash_elem); +} + +/* ************************************************************************** + * + * Remove a hash element from the hash table. The hash element is + * removed from both the LRU list and the hash bucket chain. + * + * Input: + * table -- the hash table to be manipulated on. + * hash_elem -- the element to be removed. + ************************************************************************** + */ + +void hash_remove(hash_table *table, hash_element *hash_elem) +{ + if (hash_elem) { + ubi_dlRemove(&(table->lru_chain), &(hash_elem->lru_link.lru_link)); + ubi_dlRemove(hash_elem->bucket, (ubi_dlNodePtr) hash_elem); + SAFE_FREE(hash_elem->value); + SAFE_FREE(hash_elem); + table->num_elements--; + } +} + +/* ****************************************************************** + * Increase the hash table size if it is too small. + * The hash table size is increased by the HASH_TABLE_INCREMENT + * ratio. + * Input: + * table -- the hash table to be enlarged. + ****************************************************************** + */ + +static BOOL enlarge_hash_table(hash_table *table) +{ + hash_element *hash_elem; + int size, hash_value; + ubi_dlList *buckets; + ubi_dlList *old_bucket; + ubi_dlList *bucket; + ubi_dlList lru_chain; + + buckets = table->buckets; + lru_chain = table->lru_chain; + size = table->size; + + /* Reinitialize the hash table. */ + if(!hash_table_init(table, table->size * HASH_TABLE_INCREMENT, table->comp_func)) + return False; + + for (old_bucket = buckets; size > 0; size--, old_bucket++) { + while (old_bucket->count != 0) { + hash_elem = (hash_element *) ubi_dlRemHead(old_bucket); + ubi_dlRemove(&lru_chain, &(hash_elem->lru_link.lru_link)); + hash_value = string_hash(table->size, (char *) hash_elem->key); + bucket = &(table->buckets[hash_value]); + ubi_dlAddHead(bucket, hash_elem); + ubi_dlAddHead(&(table->lru_chain), &(hash_elem->lru_link.lru_link)); + hash_elem->bucket = bucket; + hash_elem->lru_link.hash_elem = hash_elem; + table->num_elements++; + } + } + SAFE_FREE(buckets); + + return True; +} + +/* ********************************************************************** + * + * Remove everything from a hash table and free up the memory it + * occupies. + * Input: + * table -- the hash table to be cleared. + * + ************************************************************************* + */ + +void hash_clear(hash_table *table) +{ + int i; + ubi_dlList *bucket = table->buckets; + hash_element *hash_elem; + for (i = 0; i < table->size; bucket++, i++) { + while (bucket->count != 0) { + hash_elem = (hash_element *) ubi_dlRemHead(bucket); + SAFE_FREE(hash_elem->value); + SAFE_FREE(hash_elem); + } + } + table->size = 0; + SAFE_FREE(table->buckets); + table->buckets = NULL; +} diff --git a/source3/lib/hmacmd5.c b/source3/lib/hmacmd5.c new file mode 100644 index 0000000000..f436fd30c0 --- /dev/null +++ b/source3/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/source3/lib/iconv.c b/source3/lib/iconv.c new file mode 100644 index 0000000000..43350d9349 --- /dev/null +++ b/source3/lib/iconv.c @@ -0,0 +1,581 @@ +/* + Unix SMB/CIFS implementation. + minimal iconv implementation + 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" + +static size_t ascii_pull(void *,char **, size_t *, char **, size_t *); +static size_t ascii_push(void *,char **, size_t *, char **, size_t *); +static size_t utf8_pull(void *,char **, size_t *, char **, size_t *); +static size_t utf8_push(void *,char **, size_t *, char **, size_t *); +static size_t weird_pull(void *,char **, size_t *, char **, size_t *); +static size_t weird_push(void *,char **, size_t *, char **, size_t *); +static size_t ucs2hex_pull(void *,char **, size_t *, char **, size_t *); +static size_t ucs2hex_push(void *,char **, size_t *, char **, size_t *); +static size_t iconv_copy(void *,char **, size_t *, char **, size_t *); + +/* + for each charset we have a function that pulls from that charset to + a ucs2 buffer, and a function that pushes to a ucs2 buffer +*/ +static struct { + char *name; + size_t (*pull)(void *, char **inbuf, size_t *inbytesleft, + char **outbuf, size_t *outbytesleft); + size_t (*push)(void *, char **inbuf, size_t *inbytesleft, + char **outbuf, size_t *outbytesleft); +} charsets[] = { + {"UCS-2LE", iconv_copy, iconv_copy}, + {"UTF8", utf8_pull, utf8_push}, + {"ASCII", ascii_pull, ascii_push}, + {"WEIRD", weird_pull, weird_push}, + {"UCS2-HEX", ucs2hex_pull, ucs2hex_push}, + {NULL, NULL, NULL} +}; + + +/* 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, + char **inbuf, size_t *inbytesleft, + char **outbuf, size_t *outbytesleft) +{ +#ifdef HAVE_NATIVE_ICONV + size_t ret = iconv((iconv_t)cd, + inbuf, inbytesleft, + outbuf, outbytesleft); + if (ret == (size_t)-1) iconv(cd, NULL, NULL, NULL, NULL); + return ret; +#else + errno = EINVAL; + return -1; +#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, + (char **)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, + (char **)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; + int from, to; + + 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; + } + + for (from=0; charsets[from].name; from++) { + if (strcasecmp(charsets[from].name, fromcode) == 0) break; + } + for (to=0; charsets[to].name; to++) { + if (strcasecmp(charsets[to].name, tocode) == 0) break; + } + +#ifdef HAVE_NATIVE_ICONV + if (!charsets[from].name) { + ret->pull = sys_iconv; + ret->cd_pull = iconv_open("UCS-2LE", fromcode); + if (ret->cd_pull == (iconv_t)-1) goto failed; + } + if (!charsets[to].name) { + ret->push = sys_iconv; + ret->cd_push = iconv_open(tocode, "UCS-2LE"); + if (ret->cd_push == (iconv_t)-1) goto failed; + } +#else + if (!charsets[from].name || !charsets[to].name) { + goto failed; + } +#endif + + /* check for conversion to/from ucs2 */ + if (from == 0 && charsets[to].name) { + ret->direct = charsets[to].push; + return ret; + } + if (to == 0 && charsets[from].name) { + ret->direct = charsets[from].pull; + return ret; + } + +#ifdef HAVE_NATIVE_ICONV + if (from == 0) { + ret->direct = sys_iconv; + ret->cd_direct = ret->cd_push; + ret->cd_push = NULL; + return ret; + } + if (to == 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 = charsets[from].pull; + if (!ret->push) ret->push = charsets[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, 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, 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, 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, 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; +} + + +/* the "weird" character set is very useful for testing multi-byte + support and finding bugs. Don't use on a production system! +*/ +static struct { + char from; + char *to; + int len; +} weird_table[] = { + {'q', "^q^", 3}, + {'Q', "^Q^", 3}, + {0, NULL} +}; + +static size_t weird_pull(void *cd, char **inbuf, size_t *inbytesleft, + char **outbuf, size_t *outbytesleft) +{ + while (*inbytesleft >= 1 && *outbytesleft >= 2) { + int i; + int done = 0; + for (i=0;weird_table[i].from;i++) { + if (strncmp((*inbuf), + weird_table[i].to, + weird_table[i].len) == 0) { + if (*inbytesleft < weird_table[i].len) { + DEBUG(0,("ERROR: truncated weird string\n")); + /* smb_panic("weird_pull"); */ + + } else { + (*outbuf)[0] = weird_table[i].from; + (*outbuf)[1] = 0; + (*inbytesleft) -= weird_table[i].len; + (*outbytesleft) -= 2; + (*inbuf) += weird_table[i].len; + (*outbuf) += 2; + done = 1; + break; + } + } + } + if (done) continue; + (*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 weird_push(void *cd, char **inbuf, size_t *inbytesleft, + char **outbuf, size_t *outbytesleft) +{ + int ir_count=0; + + while (*inbytesleft >= 2 && *outbytesleft >= 1) { + int i; + int done=0; + for (i=0;weird_table[i].from;i++) { + if ((*inbuf)[0] == weird_table[i].from && + (*inbuf)[1] == 0) { + if (*outbytesleft < weird_table[i].len) { + DEBUG(0,("No room for weird character\n")); + /* smb_panic("weird_push"); */ + } else { + memcpy(*outbuf, weird_table[i].to, + weird_table[i].len); + (*inbytesleft) -= 2; + (*outbytesleft) -= weird_table[i].len; + (*inbuf) += 2; + (*outbuf) += weird_table[i].len; + done = 1; + break; + } + } + } + if (done) continue; + + (*outbuf)[0] = (*inbuf)[0]; + 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 iconv_copy(void *cd, 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, char **inbuf, size_t *inbytesleft, + char **outbuf, size_t *outbytesleft) +{ + while (*inbytesleft >= 1 && *outbytesleft >= 2) { + unsigned char *c = (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, char **inbuf, size_t *inbytesleft, + char **outbuf, size_t *outbytesleft) +{ + while (*inbytesleft >= 2 && *outbytesleft >= 1) { + unsigned char *c = (unsigned char *)*outbuf; + unsigned char *uc = (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/source3/lib/interface.c b/source3/lib/interface.c new file mode 100644 index 0000000000..d43001342e --- /dev/null +++ b/source3/lib/interface.c @@ -0,0 +1,370 @@ +/* + 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; + +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(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(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(token); + + if (strlen(p) > 2) { + nmask = *interpret_addr2(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) +{ + char **ptr; + int i; + struct iface_struct ifaces[MAX_INTERFACES]; + + ptr = lp_interfaces(); + + allones_ip = *interpret_addr2("255.255.255.255"); + loopback_ip = *interpret_addr2("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); + } + } + return; + } + + if (ptr) { + while (*ptr) { + interpret_interface(*ptr); + ptr++; + } + } + + if (!local_interfaces) { + DEBUG(0,("WARNING: no network interfaces found\n")); + } +} + + +/**************************************************************************** +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; +} + +/**************************************************************************** + True if we have two or more interfaces. + **************************************************************************/ +BOOL we_are_multihomed(void) +{ + static int multi = -1; + + if(multi == -1) + multi = (iface_count() > 1 ? True : False); + + return multi; +} + +/**************************************************************************** + return the Nth interface + **************************************************************************/ +struct interface *get_interface(int n) +{ + struct interface *i; + + for (i=local_interfaces;i && n;i=i->next) + n--; + + if (i) return i; + return NULL; +} + +/**************************************************************************** + 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; +} + + +/**************************************************************************** +this function provides a simple hash of the configured interfaces. It is +used to detect a change in interfaces to tell us whether to discard +the current wins.dat file. +Note that the result is independent of the order of the interfaces + **************************************************************************/ +unsigned iface_hash(void) +{ + unsigned ret = 0; + struct interface *i; + + for (i=local_interfaces;i;i=i->next) { + unsigned x1 = (unsigned)str_checksum(inet_ntoa(i->ip)); + unsigned x2 = (unsigned)str_checksum(inet_ntoa(i->nmask)); + ret ^= (x1 ^ x2); + } + + return ret; +} + + +/* 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_bcast(struct in_addr ip) +{ + struct interface *i = iface_find(ip, True); + return(i ? &i->bcast : &local_interfaces->bcast); +} + +struct in_addr *iface_ip(struct in_addr ip) +{ + struct interface *i = iface_find(ip, True); + return(i ? &i->ip : &local_interfaces->ip); +} diff --git a/source3/lib/interfaces.c b/source3/lib/interfaces.c new file mode 100644 index 0000000000..7b8ef0d0c1 --- /dev/null +++ b/source3/lib/interfaces.c @@ -0,0 +1,401 @@ +/* + 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> + +#ifndef SIOCGIFCONF +#include <sys/sockio.h> +#endif + +#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_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/source3/lib/kanji.c b/source3/lib/kanji.c deleted file mode 100644 index 0af476eb15..0000000000 --- a/source3/lib/kanji.c +++ /dev/null @@ -1,895 +0,0 @@ -/* - Unix SMB/Netbios implementation. - Version 1.9. - Kanji Extensions - Copyright (C) Andrew Tridgell 1992-1994 - - 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. - - Adding for Japanese language by <fujita@ainix.isac.co.jp> 1994.9.5 - and extend coding system to EUC/SJIS/JIS/HEX at 1994.10.11 - and add all jis codes sequence type at 1995.8.16 - Notes: Hexadecimal code by <ohki@gssm.otuka.tsukuba.ac.jp> -*/ -#ifdef KANJI - -#define _KANJI_C_ -#include "includes.h" - -/* coding system keep in */ -int coding_system = SJIS_CODE; - -/* jis si/so sequence */ -char jis_kso = JIS_KSO; -char jis_ksi = JIS_KSI; -char hex_tag = HEXTAG; - -/******************************************************************* - SHIFT JIS functions -********************************************************************/ -/******************************************************************* - search token from S1 separated any char of S2 - S1 contain SHIFT JIS chars. -********************************************************************/ -char * -sj_strtok (char *s1, const char *s2) -{ - static char *s = NULL; - char *q; - if (!s1) { - if (!s) { - return NULL; - } - s1 = s; - } - for (q = s1; *s1; ) { - if (is_shift_jis (*s1)) { - s1 += 2; - } else if (is_kana (*s1)) { - s1++; - } else { - char *p = strchr (s2, *s1); - if (p) { - if (s1 != q) { - s = s1 + 1; - *s1 = '\0'; - return q; - } - q = s1 + 1; - } - s1++; - } - } - s = NULL; - if (*q) { - return q; - } - return NULL; -} - -/******************************************************************* - search string S2 from S1 - S1 contain SHIFT JIS chars. -********************************************************************/ -char * -sj_strstr (const char *s1, const char *s2) -{ - register int len = strlen ((char *) s2); - if (!*s2) - return (char *) s1; - for (;*s1;) { - if (*s1 == *s2) { - if (strncmp (s1, s2, len) == 0) - return (char *) s1; - } - if (is_shift_jis (*s1)) { - s1 += 2; - } else { - s1++; - } - } - return 0; -} - -/******************************************************************* - Search char C from beginning of S. - S contain SHIFT JIS chars. -********************************************************************/ -char * -sj_strchr (const char *s, int c) -{ - for (; *s; ) { - if (*s == c) - return (char *) s; - if (is_shift_jis (*s)) { - s += 2; - } else { - s++; - } - } - return 0; -} - -/******************************************************************* - Search char C end of S. - S contain SHIFT JIS chars. -********************************************************************/ -char * -sj_strrchr (const char *s, int c) -{ - register char *q; - - for (q = 0; *s; ) { - if (*s == c) { - q = (char *) s; - } - if (is_shift_jis (*s)) { - s += 2; - } else { - s++; - } - } - return q; -} - -/******************************************************************* - Code conversion -********************************************************************/ -/* convesion buffer */ -static char cvtbuf[1024]; - -/******************************************************************* - EUC <-> SJIS -********************************************************************/ -static int -euc2sjis (register int hi, register int lo) -{ - if (hi & 1) - return ((hi / 2 + (hi < 0xdf ? 0x31 : 0x71)) << 8) | - (lo - (lo >= 0xe0 ? 0x60 : 0x61)); - else - return ((hi / 2 + (hi < 0xdf ? 0x30 : 0x70)) << 8) | (lo - 2); -} - -static int -sjis2euc (register int hi, register int lo) -{ - if (lo >= 0x9f) - return ((hi * 2 - (hi >= 0xe0 ? 0xe0 : 0x60)) << 8) | (lo + 2); - else - return ((hi * 2 - (hi >= 0xe0 ? 0xe1 : 0x61)) << 8) | - (lo + (lo >= 0x7f ? 0x60 : 0x61)); -} - -/******************************************************************* - Convert FROM contain SHIFT JIS codes to EUC codes - return converted buffer -********************************************************************/ -static char * -sj_to_euc (const char *from, BOOL overwrite) -{ - register char *out; - char *save; - - save = (char *) from; - for (out = cvtbuf; *from;) { - if (is_shift_jis (*from)) { - int code = sjis2euc ((int) from[0] & 0xff, (int) from[1] & 0xff); - *out++ = (code >> 8) & 0xff; - *out++ = code; - from += 2; - } else if (is_kana (*from)) { - *out++ = euc_kana; - *out++ = *from++; - } else { - *out++ = *from++; - } - } - *out = 0; - if (overwrite) { - strcpy((char *) save, (char *) cvtbuf); - return (char *) save; - } else { - return cvtbuf; - } -} - -/******************************************************************* - Convert FROM contain EUC codes to SHIFT JIS codes - return converted buffer -********************************************************************/ -static char * -euc_to_sj (const char *from, BOOL overwrite) -{ - register char *out; - char *save; - - save = (char *) from; - for (out = cvtbuf; *from; ) { - if (is_euc (*from)) { - int code = euc2sjis ((int) from[0] & 0xff, (int) from[1] & 0xff); - *out++ = (code >> 8) & 0xff; - *out++ = code; - from += 2; - } else if (is_euc_kana (*from)) { - *out++ = from[1]; - from += 2; - } else { - *out++ = *from++; - } - } - *out = 0; - if (overwrite) { - strcpy(save, (char *) cvtbuf); - return save; - } else { - return cvtbuf; - } -} - -/******************************************************************* - JIS7,JIS8,JUNET <-> SJIS -********************************************************************/ -static int -sjis2jis (register int hi, register int lo) -{ - if (lo >= 0x9f) - return ((hi * 2 - (hi >= 0xe0 ? 0x160 : 0xe0)) << 8) | (lo - 0x7e); - else - return ((hi * 2 - (hi >= 0xe0 ? 0x161 : 0xe1)) << 8) | - (lo - (lo >= 0x7f ? 0x20 : 0x1f)); -} - -static int -jis2sjis (register int hi, register int lo) -{ - if (hi & 1) - return ((hi / 2 + (hi < 0x5f ? 0x71 : 0xb1)) << 8) | - (lo + (lo >= 0x60 ? 0x20 : 0x1f)); - else - return ((hi / 2 + (hi < 0x5f ? 0x70 : 0xb0)) << 8) | (lo + 0x7e); -} - -/******************************************************************* - Convert FROM contain JIS codes to SHIFT JIS codes - return converted buffer -********************************************************************/ -static char * -jis8_to_sj (const char *from, BOOL overwrite) -{ - register char *out; - register int shifted; - char *save; - - shifted = _KJ_ROMAN; - save = (char *) from; - for (out = cvtbuf; *from;) { - if (is_esc (*from)) { - if (is_so1 (from[1]) && is_so2 (from[2])) { - shifted = _KJ_KANJI; - from += 3; - } else if (is_si1 (from[1]) && is_si2 (from[2])) { - shifted = _KJ_ROMAN; - from += 3; - } else { /* sequence error */ - goto normal; - } - } else { - normal: - switch (shifted) { - default: - case _KJ_ROMAN: - *out++ = *from++; - break; - case _KJ_KANJI: - { - int code = jis2sjis ((int) from[0] & 0xff, (int) from[1] & 0xff); - *out++ = (code >> 8) & 0xff; - *out++ = code; - from += 2; - } - break; - } - } - } - *out = 0; - if (overwrite) { - strcpy (save, (char *) cvtbuf); - return save; - } else { - return cvtbuf; - } -} - -/******************************************************************* - Convert FROM contain SHIFT JIS codes to JIS codes - return converted buffer -********************************************************************/ -static char * -sj_to_jis8 (const char *from, BOOL overwrite) -{ - register char *out; - register int shifted; - char *save; - - shifted = _KJ_ROMAN; - save = (char *) from; - for (out = cvtbuf; *from; ) { - if (is_shift_jis (*from)) { - int code; - switch (shifted) { - case _KJ_ROMAN: /* to KANJI */ - *out++ = jis_esc; - *out++ = jis_so1; - *out++ = jis_kso; - shifted = _KJ_KANJI; - break; - } - code = sjis2jis ((int) from[0] & 0xff, (int) from[1] & 0xff); - *out++ = (code >> 8) & 0xff; - *out++ = code; - from += 2; - } else { - switch (shifted) { - case _KJ_KANJI: /* to ROMAN/KANA */ - *out++ = jis_esc; - *out++ = jis_si1; - *out++ = jis_ksi; - shifted = _KJ_ROMAN; - break; - } - *out++ = *from++; - } - } - switch (shifted) { - case _KJ_KANJI: /* to ROMAN/KANA */ - *out++ = jis_esc; - *out++ = jis_si1; - *out++ = jis_ksi; - shifted = _KJ_ROMAN; - break; - } - *out = 0; - if (overwrite) { - strcpy (save, (char *) cvtbuf); - return save; - } else { - return cvtbuf; - } -} - -/******************************************************************* - Convert FROM contain 7 bits JIS codes to SHIFT JIS codes - return converted buffer -********************************************************************/ -static char * -jis7_to_sj (const char *from, BOOL overwrite) -{ - register char *out; - register int shifted; - char *save; - - shifted = _KJ_ROMAN; - save = (char *) from; - for (out = cvtbuf; *from;) { - if (is_esc (*from)) { - if (is_so1 (from[1]) && is_so2 (from[2])) { - shifted = _KJ_KANJI; - from += 3; - } else if (is_si1 (from[1]) && is_si2 (from[2])) { - shifted = _KJ_ROMAN; - from += 3; - } else { /* sequence error */ - goto normal; - } - } else if (is_so (*from)) { - shifted = _KJ_KANA; /* to KANA */ - from++; - } else if (is_si (*from)) { - shifted = _KJ_ROMAN; /* to ROMAN */ - from++; - } else { - normal: - switch (shifted) { - default: - case _KJ_ROMAN: - *out++ = *from++; - break; - case _KJ_KANJI: - { - int code = jis2sjis ((int) from[0] & 0xff, (int) from[1] & 0xff); - *out++ = (code >> 8) & 0xff; - *out++ = code; - from += 2; - } - break; - case _KJ_KANA: - *out++ = ((int) from[0]) + 0x80; - break; - } - } - } - *out = 0; - if (overwrite) { - strcpy (save, (char *) cvtbuf); - return save; - } else { - return cvtbuf; - } -} - -/******************************************************************* - Convert FROM contain SHIFT JIS codes to 7 bits JIS codes - return converted buffer -********************************************************************/ -static char * -sj_to_jis7 (const char *from, BOOL overwrite) -{ - register char *out; - register int shifted; - char *save; - - shifted = _KJ_ROMAN; - save = (char *) from; - for (out = cvtbuf; *from; ) { - if (is_shift_jis (*from)) { - int code; - switch (shifted) { - case _KJ_KANA: - *out++ = jis_si; /* to ROMAN and through down */ - case _KJ_ROMAN: /* to KANJI */ - *out++ = jis_esc; - *out++ = jis_so1; - *out++ = jis_kso; - shifted = _KJ_KANJI; - break; - } - code = sjis2jis ((int) from[0] & 0xff, (int) from[1] & 0xff); - *out++ = (code >> 8) & 0xff; - *out++ = code; - from += 2; - } else if (is_kana (from[0])) { - switch (shifted) { - case _KJ_KANJI: /* to ROMAN */ - *out++ = jis_esc; - *out++ = jis_si1; - *out++ = jis_ksi; - case _KJ_ROMAN: /* to KANA */ - *out++ = jis_so; - shifted = _KJ_KANA; - break; - } - *out++ = ((int) *from++) - 0x80; - } else { - switch (shifted) { - case _KJ_KANA: - *out++ = jis_si; /* to ROMAN */ - shifted = _KJ_ROMAN; - break; - case _KJ_KANJI: /* to ROMAN */ - *out++ = jis_esc; - *out++ = jis_si1; - *out++ = jis_ksi; - shifted = _KJ_ROMAN; - break; - } - *out++ = *from++; - } - } - switch (shifted) { - case _KJ_KANA: - *out++ = jis_si; /* to ROMAN */ - break; - case _KJ_KANJI: /* to ROMAN */ - *out++ = jis_esc; - *out++ = jis_si1; - *out++ = jis_ksi; - break; - } - *out = 0; - if (overwrite) { - strcpy (save, (char *) cvtbuf); - return save; - } else { - return cvtbuf; - } -} - -/******************************************************************* - Convert FROM contain 7 bits JIS(junet) codes to SHIFT JIS codes - return converted buffer -********************************************************************/ -static char * -junet_to_sj (const char *from, BOOL overwrite) -{ - register char *out; - register int shifted; - char *save; - - shifted = _KJ_ROMAN; - save = (char *) from; - for (out = cvtbuf; *from;) { - if (is_esc (*from)) { - if (is_so1 (from[1]) && is_so2 (from[2])) { - shifted = _KJ_KANJI; - from += 3; - } else if (is_si1 (from[1]) && is_si2 (from[2])) { - shifted = _KJ_ROMAN; - from += 3; - } else if (is_juk1(from[1]) && is_juk2 (from[2])) { - shifted = _KJ_KANA; - from += 3; - } else { /* sequence error */ - goto normal; - } - } else { - normal: - switch (shifted) { - default: - case _KJ_ROMAN: - *out++ = *from++; - break; - case _KJ_KANJI: - { - int code = jis2sjis ((int) from[0] & 0xff, (int) from[1] & 0xff); - *out++ = (code >> 8) & 0xff; - *out++ = code; - from += 2; - } - break; - case _KJ_KANA: - *out++ = ((int) from[0]) + 0x80; - break; - } - } - } - *out = 0; - if (overwrite) { - strcpy (save, (char *) cvtbuf); - return save; - } else { - return cvtbuf; - } -} - -/******************************************************************* - Convert FROM contain SHIFT JIS codes to 7 bits JIS(junet) codes - return converted buffer -********************************************************************/ -static char * -sj_to_junet (const char *from, BOOL overwrite) -{ - register char *out; - register int shifted; - char *save; - - shifted = _KJ_ROMAN; - save = (char *) from; - for (out = cvtbuf; *from; ) { - if (is_shift_jis (*from)) { - int code; - switch (shifted) { - case _KJ_KANA: - case _KJ_ROMAN: /* to KANJI */ - *out++ = jis_esc; - *out++ = jis_so1; - *out++ = jis_so2; - shifted = _KJ_KANJI; - break; - } - code = sjis2jis ((int) from[0] & 0xff, (int) from[1] & 0xff); - *out++ = (code >> 8) & 0xff; - *out++ = code; - from += 2; - } else if (is_kana (from[0])) { - switch (shifted) { - case _KJ_KANJI: /* to ROMAN */ - case _KJ_ROMAN: /* to KANA */ - *out++ = jis_esc; - *out++ = junet_kana1; - *out++ = junet_kana2; - shifted = _KJ_KANA; - break; - } - *out++ = ((int) *from++) - 0x80; - } else { - switch (shifted) { - case _KJ_KANA: - case _KJ_KANJI: /* to ROMAN */ - *out++ = jis_esc; - *out++ = jis_si1; - *out++ = jis_si2; - shifted = _KJ_ROMAN; - break; - } - *out++ = *from++; - } - } - switch (shifted) { - case _KJ_KANA: - case _KJ_KANJI: /* to ROMAN */ - *out++ = jis_esc; - *out++ = jis_si1; - *out++ = jis_si2; - break; - } - *out = 0; - if (overwrite) { - strcpy (save, (char *) cvtbuf); - return save; - } else { - return cvtbuf; - } -} - -/******************************************************************* - HEX <-> SJIS -********************************************************************/ -/* ":xx" -> a byte */ -static char * -hex_to_sj (const char *from, BOOL overwrite) -{ - char *sp, *dp; - - sp = (char *) from; - dp = cvtbuf; - while (*sp) { - if (*sp == hex_tag && isxdigit (sp[1]) && isxdigit (sp[2])) { - *dp++ = (hex2bin (sp[1])<<4) | (hex2bin (sp[2])); - sp += 3; - } else - *dp++ = *sp++; - } - *dp = '\0'; - if (overwrite) { - strcpy ((char *) from, (char *) cvtbuf); - return (char *) from; - } else { - return cvtbuf; - } -} - -/******************************************************************* - kanji/kana -> ":xx" -********************************************************************/ -static char * -sj_to_hex (const char *from, BOOL overwrite) -{ - unsigned char *sp, *dp; - - sp = (unsigned char*) from; - dp = (unsigned char*) cvtbuf; - while (*sp) { - if (is_kana(*sp)) { - *dp++ = hex_tag; - *dp++ = bin2hex (((*sp)>>4)&0x0f); - *dp++ = bin2hex ((*sp)&0x0f); - sp++; - } else if (is_shift_jis (*sp) && is_shift_jis2 (sp[1])) { - *dp++ = hex_tag; - *dp++ = bin2hex (((*sp)>>4)&0x0f); - *dp++ = bin2hex ((*sp)&0x0f); - sp++; - *dp++ = hex_tag; - *dp++ = bin2hex (((*sp)>>4)&0x0f); - *dp++ = bin2hex ((*sp)&0x0f); - sp++; - } else - *dp++ = *sp++; - } - *dp = '\0'; - if (overwrite) { - strcpy ((char *) from, (char *) cvtbuf); - return (char *) from; - } else { - return cvtbuf; - } -} - -/******************************************************************* - kanji/kana -> ":xx" -********************************************************************/ -static char * -sj_to_cap (const char *from, BOOL overwrite) -{ - unsigned char *sp, *dp; - - sp = (unsigned char*) from; - dp = (unsigned char*) cvtbuf; - while (*sp) { - if (*sp >= 0x80) { - *dp++ = hex_tag; - *dp++ = bin2hex (((*sp)>>4)&0x0f); - *dp++ = bin2hex ((*sp)&0x0f); - sp++; - } else { - *dp++ = *sp++; - } - } - *dp = '\0'; - if (overwrite) { - strcpy ((char *) from, (char *) cvtbuf); - return (char *) from; - } else { - return cvtbuf; - } -} - -/******************************************************************* - sj to sj -********************************************************************/ -static char * -sj_to_sj (const char *from, BOOL overwrite) -{ - if (!overwrite) { - strcpy (cvtbuf, (char *) from); - return cvtbuf; - } else { - return (char *) from; - } -} - -/************************************************************************ - conversion: - _dos_to_unix _unix_to_dos -************************************************************************/ - -char* (*_dos_to_unix) (const char *str, BOOL overwrite) = sj_to_sj; -char* (*_unix_to_dos) (const char *str, BOOL overwrite) = sj_to_sj; - -static int -setup_string_function (int codes) -{ - switch (codes) { - default: - case SJIS_CODE: - _dos_to_unix = sj_to_sj; - _unix_to_dos = sj_to_sj; - - break; - - case EUC_CODE: - _dos_to_unix = sj_to_euc; - _unix_to_dos = euc_to_sj; - break; - - case JIS7_CODE: - _dos_to_unix = sj_to_jis7; - _unix_to_dos = jis7_to_sj; - break; - - case JIS8_CODE: - _dos_to_unix = sj_to_jis8; - _unix_to_dos = jis8_to_sj; - break; - - case JUNET_CODE: - _dos_to_unix = sj_to_junet; - _unix_to_dos = junet_to_sj; - break; - - case HEX_CODE: - _dos_to_unix = sj_to_hex; - _unix_to_dos = hex_to_sj; - break; - - case CAP_CODE: - _dos_to_unix = sj_to_cap; - _unix_to_dos = hex_to_sj; - break; - } - return codes; -} - -/* - * Interpret coding system. - */ -int -interpret_coding_system (char *str, int def) -{ - int codes = def; - - if (strequal (str, "sjis")) { - codes = SJIS_CODE; - } else if (strequal (str, "euc")) { - codes = EUC_CODE; - } else if (strequal (str, "cap")) { - codes = CAP_CODE; - hex_tag = HEXTAG; - } else if (strequal (str, "hex")) { - codes = HEX_CODE; - hex_tag = HEXTAG; - } else if (strncasecmp (str, "hex", 3)) { - codes = HEX_CODE; - hex_tag = (str[3] ? str[3] : HEXTAG); - } else if (strequal (str, "j8bb")) { - codes = JIS8_CODE; - jis_kso = 'B'; - jis_ksi = 'B'; - } else if (strequal (str, "j8bj") || strequal (str, "jis8")) { - codes = JIS8_CODE; - jis_kso = 'B'; - jis_ksi = 'J'; - } else if (strequal (str, "j8bh")) { - codes = JIS8_CODE; - jis_kso = 'B'; - jis_ksi = 'H'; - } else if (strequal (str, "j8@b")) { - codes = JIS8_CODE; - jis_kso = '@'; - jis_ksi = 'B'; - } else if (strequal (str, "j8@j")) { - codes = JIS8_CODE; - jis_kso = '@'; - jis_ksi = 'J'; - } else if (strequal (str, "j8@h")) { - codes = JIS8_CODE; - jis_kso = '@'; - jis_ksi = 'H'; - } else if (strequal (str, "j7bb")) { - codes = JIS7_CODE; - jis_kso = 'B'; - jis_ksi = 'B'; - } else if (strequal (str, "j7bj") || strequal (str, "jis7")) { - codes = JIS7_CODE; - jis_kso = 'B'; - jis_ksi = 'J'; - } else if (strequal (str, "j7bh")) { - codes = JIS7_CODE; - jis_kso = 'B'; - jis_ksi = 'H'; - } else if (strequal (str, "j7@b")) { - codes = JIS7_CODE; - jis_kso = '@'; - jis_ksi = 'B'; - } else if (strequal (str, "j7@j")) { - codes = JIS7_CODE; - jis_kso = '@'; - jis_ksi = 'J'; - } else if (strequal (str, "j7@h")) { - codes = JIS7_CODE; - jis_kso = '@'; - jis_ksi = 'H'; - } else if (strequal (str, "jubb")) { - codes = JUNET_CODE; - jis_kso = 'B'; - jis_ksi = 'B'; - } else if (strequal (str, "jubj") || strequal (str, "junet")) { - codes = JUNET_CODE; - jis_kso = 'B'; - jis_ksi = 'J'; - } else if (strequal (str, "jubh")) { - codes = JUNET_CODE; - jis_kso = 'B'; - jis_ksi = 'H'; - } else if (strequal (str, "ju@b")) { - codes = JUNET_CODE; - jis_kso = '@'; - jis_ksi = 'B'; - } else if (strequal (str, "ju@j")) { - codes = JUNET_CODE; - jis_kso = '@'; - jis_ksi = 'J'; - } else if (strequal (str, "ju@h")) { - codes = JUNET_CODE; - jis_kso = '@'; - jis_ksi = 'H'; - } - return setup_string_function (codes); -} -#else -int kanji_dummy_procedure(void) -{return 0;} -#endif /* KANJI */ diff --git a/source3/lib/md4.c b/source3/lib/md4.c index 485e231a78..6803b7e883 100644 --- a/source3/lib/md4.c +++ b/source3/lib/md4.c @@ -1,299 +1,169 @@ -#ifdef SMB_PASSWD -/* - This code is from rfc1186. +/* + 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. */ - /* - ** ******************************************************************** - ** md4.c -- Implementation of MD4 Message Digest Algorithm ** - ** Updated: 2/16/90 by Ronald L. Rivest ** - ** (C) 1990 RSA Data Security, Inc. ** - ** ******************************************************************** - */ +#include "includes.h" - /* - ** To use MD4: - ** -- Include md4.h in your program - ** -- Declare an MDstruct MD to hold the state of the digest - ** computation. - ** -- Initialize MD using MDbegin(&MD) - ** -- For each full block (64 bytes) X you wish to process, call - ** MDupdate(&MD,X,512) - ** (512 is the number of bits in a full block.) - ** -- For the last block (less than 64 bytes) you wish to process, - ** MDupdate(&MD,X,n) - ** where n is the number of bits in the partial block. A partial - ** block terminates the computation, so every MD computation - ** should terminate by processing a partial block, even if it - ** has n = 0. - ** -- The message digest is available in MD.buffer[0] ... - ** MD.buffer[3]. (Least-significant byte of each word - ** should be output first.) - ** -- You can print out the digest using MDprint(&MD) - */ +/* NOTE: This code makes no attempt to be fast! - /* Implementation notes: - ** This implementation assumes that ints are 32-bit quantities. - ** If the machine stores the least-significant byte of an int in the - ** least-addressed byte (e.g., VAX and 8086), then LOWBYTEFIRST - ** should be set to TRUE. Otherwise (e.g., SUNS), LOWBYTEFIRST - ** should be set to FALSE. Note that on machines with LOWBYTEFIRST - ** FALSE the routine MDupdate modifies has a side-effect on its input - ** array (the order of bytes in each word are reversed). If this is - ** undesired a call to MDreverse(X) can reverse the bytes of X back - ** into order after each call to MDupdate. - */ - -#define TRUE 1 -#define FALSE 0 - - /* Compile-time includes - */ - -#include <stdio.h> -#include "md4.h" - -#define uchar unsigned char -#define int16 unsigned short -#define uint32 unsigned int - -#include "byteorder.h" - - /* Compile-time declarations of MD4 "magic constants". - */ -#define I0 0x67452301 /* Initial values for MD buffer */ -#define I1 0xefcdab89 -#define I2 0x98badcfe -#define I3 0x10325476 -#define C2 013240474631 /* round 2 constant = sqrt(2) in octal */ -#define C3 015666365641 /* round 3 constant = sqrt(3) in octal */ - /* C2 and C3 are from Knuth, The Art of Programming, Volume 2 - ** (Seminumerical Algorithms), Second Edition (1981), Addison-Wesley. - ** Table 2, page 660. - */ - -#define fs1 3 /* round 1 shift amounts */ -#define fs2 7 -#define fs3 11 -#define fs4 19 -#define gs1 3 /* round 2 shift amounts */ -#define gs2 5 -#define gs3 9 -#define gs4 13 -#define hs1 3 /* round 3 shift amounts */ -#define hs2 9 -#define hs3 11 -#define hs4 15 - - /* Compile-time macro declarations for MD4. - ** Note: The "rot" operator uses the variable "tmp". - ** It assumes tmp is declared as unsigned int, so that the >> - ** operator will shift in zeros rather than extending the sign bit. - */ -#define f(X,Y,Z) ((X&Y) | ((~X)&Z)) -#define g(X,Y,Z) ((X&Y) | (X&Z) | (Y&Z)) -#define h(X,Y,Z) (X^Y^Z) -#define rot(X,S) (tmp=X,(tmp<<S) | (tmp>>(32-S))) -#define ff(A,B,C,D,i,s) A = rot((A + f(B,C,D) + X[i]),s) -#define gg(A,B,C,D,i,s) A = rot((A + g(B,C,D) + X[i] + C2),s) -#define hh(A,B,C,D,i,s) A = rot((A + h(B,C,D) + X[i] + C3),s) + It assumes that a int is at least 32 bits long +*/ - /* MDprint(MDp) - ** Print message digest buffer MDp as 32 hexadecimal digits. - ** Order is from low-order byte of buffer[0] to high-order byte of - ** buffer[3]. - ** Each byte is printed with high-order hexadecimal digit first. - ** This is a user-callable routine. - */ - void - MDprint(MDp) - MDptr MDp; - { int i,j; - for (i=0;i<4;i++) - for (j=0;j<32;j=j+8) - printf("%02x",(MDp->buffer[i]>>j) & 0xFF); - } +static 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(uint32 *M) +{ + int j; + uint32 AA, BB, CC, DD; + uint32 X[16]; + + for (j=0;j<16;j++) + X[j] = M[j]; + + AA = A; BB = B; CC = C; DD = D; + + ROUND1(A,B,C,D, 0, 3); ROUND1(D,A,B,C, 1, 7); + ROUND1(C,D,A,B, 2, 11); ROUND1(B,C,D,A, 3, 19); + ROUND1(A,B,C,D, 4, 3); ROUND1(D,A,B,C, 5, 7); + ROUND1(C,D,A,B, 6, 11); ROUND1(B,C,D,A, 7, 19); + ROUND1(A,B,C,D, 8, 3); ROUND1(D,A,B,C, 9, 7); + ROUND1(C,D,A,B, 10, 11); ROUND1(B,C,D,A, 11, 19); + ROUND1(A,B,C,D, 12, 3); ROUND1(D,A,B,C, 13, 7); + ROUND1(C,D,A,B, 14, 11); ROUND1(B,C,D,A, 15, 19); + + ROUND2(A,B,C,D, 0, 3); ROUND2(D,A,B,C, 4, 5); + ROUND2(C,D,A,B, 8, 9); ROUND2(B,C,D,A, 12, 13); + ROUND2(A,B,C,D, 1, 3); ROUND2(D,A,B,C, 5, 5); + ROUND2(C,D,A,B, 9, 9); ROUND2(B,C,D,A, 13, 13); + ROUND2(A,B,C,D, 2, 3); ROUND2(D,A,B,C, 6, 5); + ROUND2(C,D,A,B, 10, 9); ROUND2(B,C,D,A, 14, 13); + ROUND2(A,B,C,D, 3, 3); ROUND2(D,A,B,C, 7, 5); + ROUND2(C,D,A,B, 11, 9); ROUND2(B,C,D,A, 15, 13); + + ROUND3(A,B,C,D, 0, 3); ROUND3(D,A,B,C, 8, 9); + ROUND3(C,D,A,B, 4, 11); ROUND3(B,C,D,A, 12, 15); + ROUND3(A,B,C,D, 2, 3); ROUND3(D,A,B,C, 10, 9); + ROUND3(C,D,A,B, 6, 11); ROUND3(B,C,D,A, 14, 15); + ROUND3(A,B,C,D, 1, 3); ROUND3(D,A,B,C, 9, 9); + ROUND3(C,D,A,B, 5, 11); ROUND3(B,C,D,A, 13, 15); + ROUND3(A,B,C,D, 3, 3); ROUND3(D,A,B,C, 11, 9); + ROUND3(C,D,A,B, 7, 11); ROUND3(B,C,D,A, 15, 15); + + A += AA; B += BB; C += CC; D += DD; + + A &= 0xFFFFFFFF; B &= 0xFFFFFFFF; + C &= 0xFFFFFFFF; 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; + + A = 0x67452301; + B = 0xefcdab89; + C = 0x98badcfe; + D = 0x10325476; + + while (n > 64) { + copy64(M, in); + mdfour64(M); + in += 64; + n -= 64; + } - /* MDbegin(MDp) - ** Initialize message digest buffer MDp. - ** This is a user-callable routine. - */ - void - MDbegin(MDp) - MDptr MDp; - { int i; - MDp->buffer[0] = I0; - MDp->buffer[1] = I1; - MDp->buffer[2] = I2; - MDp->buffer[3] = I3; - for (i=0;i<8;i++) MDp->count[i] = 0; - MDp->done = 0; - } + 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(M); + } else { + copy4(buf+120, b); + copy64(M, buf); + mdfour64(M); + copy64(M, buf+64); + mdfour64(M); + } - /* MDreverse(X) - ** Reverse the byte-ordering of every int in X. - ** Assumes X is an array of 16 ints. - ** The macro revx reverses the byte-ordering of the next word of X. - */ - void MDreverse(X) - unsigned int *X; - { register unsigned int t; - register unsigned int i; + for (i=0;i<128;i++) + buf[i] = 0; + copy64(M, buf); - for(i = 0; i < 16; i++) { - t = X[i]; - SIVAL(X,i*4,t); - } - } + copy4(out, A); + copy4(out+4, B); + copy4(out+8, C); + copy4(out+12, D); - /* MDblock(MDp,X) - ** Update message digest buffer MDp->buffer using 16-word data block X. - ** Assumes all 16 words of X are full of data. - ** Does not update MDp->count. - ** This routine is not user-callable. - */ - static void - MDblock(MDp,X) - MDptr MDp; - unsigned int *X; - { - register unsigned int tmp, A, B, C, D; - MDreverse(X); - A = MDp->buffer[0]; - B = MDp->buffer[1]; - C = MDp->buffer[2]; - D = MDp->buffer[3]; - /* Update the message digest buffer */ - ff(A , B , C , D , 0 , fs1); /* Round 1 */ - ff(D , A , B , C , 1 , fs2); - ff(C , D , A , B , 2 , fs3); - ff(B , C , D , A , 3 , fs4); - ff(A , B , C , D , 4 , fs1); - ff(D , A , B , C , 5 , fs2); - ff(C , D , A , B , 6 , fs3); - ff(B , C , D , A , 7 , fs4); - ff(A , B , C , D , 8 , fs1); - ff(D , A , B , C , 9 , fs2); - ff(C , D , A , B , 10 , fs3); - ff(B , C , D , A , 11 , fs4); - ff(A , B , C , D , 12 , fs1); - ff(D , A , B , C , 13 , fs2); - ff(C , D , A , B , 14 , fs3); - ff(B , C , D , A , 15 , fs4); - gg(A , B , C , D , 0 , gs1); /* Round 2 */ - gg(D , A , B , C , 4 , gs2); - gg(C , D , A , B , 8 , gs3); - gg(B , C , D , A , 12 , gs4); - gg(A , B , C , D , 1 , gs1); - gg(D , A , B , C , 5 , gs2); - gg(C , D , A , B , 9 , gs3); - gg(B , C , D , A , 13 , gs4); - gg(A , B , C , D , 2 , gs1); - gg(D , A , B , C , 6 , gs2); - gg(C , D , A , B , 10 , gs3); - gg(B , C , D , A , 14 , gs4); - gg(A , B , C , D , 3 , gs1); - gg(D , A , B , C , 7 , gs2); - gg(C , D , A , B , 11 , gs3); - gg(B , C , D , A , 15 , gs4); - hh(A , B , C , D , 0 , hs1); /* Round 3 */ - hh(D , A , B , C , 8 , hs2); - hh(C , D , A , B , 4 , hs3); - hh(B , C , D , A , 12 , hs4); - hh(A , B , C , D , 2 , hs1); - hh(D , A , B , C , 10 , hs2); - hh(C , D , A , B , 6 , hs3); - hh(B , C , D , A , 14 , hs4); - hh(A , B , C , D , 1 , hs1); - hh(D , A , B , C , 9 , hs2); - hh(C , D , A , B , 5 , hs3); - hh(B , C , D , A , 13 , hs4); - hh(A , B , C , D , 3 , hs1); - hh(D , A , B , C , 11 , hs2); - hh(C , D , A , B , 7 , hs3); - hh(B , C , D , A , 15 , hs4); - MDp->buffer[0] += A; - MDp->buffer[1] += B; - MDp->buffer[2] += C; - MDp->buffer[3] += D; - } + A = B = C = D = 0; +} - /* MDupdate(MDp,X,count) - ** Input: MDp -- an MDptr - ** X -- a pointer to an array of unsigned characters. - ** count -- the number of bits of X to use. - ** (if not a multiple of 8, uses high bits of last byte.) - ** Update MDp using the number of bits of X given by count. - ** This is the basic input routine for an MD4 user. - ** The routine completes the MD computation when count < 512, so - ** every MD computation should end with one call to MDupdate with a - ** count less than 512. A call with count 0 will be ignored if the - ** MD has already been terminated (done != 0), so an extra call with - ** count 0 can be given as a "courtesy close" to force termination - ** if desired. - */ - void - MDupdate(MDp,X,count) - MDptr MDp; - unsigned char *X; - unsigned int count; - { unsigned int i, tmp, bit, byte, mask; - unsigned char XX[64]; - unsigned char *p; - /* return with no error if this is a courtesy close with count - ** zero and MDp->done is true. - */ - if (count == 0 && MDp->done) return; - /* check to see if MD is already done and report error */ - if (MDp->done) - { printf("\nError: MDupdate MD already done."); return; } - /* Add count to MDp->count */ - tmp = count; - p = MDp->count; - while (tmp) - { tmp += *p; - *p++ = tmp; - tmp = tmp >> 8; - } - /* Process data */ - if (count == 512) - { /* Full block of data to handle */ - MDblock(MDp,(unsigned int *)X); - } - else if (count > 512) /* Check for count too large */ - { printf("\nError: MDupdate called with illegal count value %d." - ,count); - return; - } - else /* partial block -- must be last block so finish up */ - { /* Find out how many bytes and residual bits there are */ - byte = count >> 3; - bit = count & 7; - /* Copy X into XX since we need to modify it */ - for (i=0;i<=byte;i++) XX[i] = X[i]; - for (i=byte+1;i<64;i++) XX[i] = 0; - /* Add padding '1' bit and low-order zeros in last byte */ - mask = 1 << (7 - bit); - XX[byte] = (XX[byte] | mask) & ~( mask - 1); - /* If room for bit count, finish up with this block */ - if (byte <= 55) - { for (i=0;i<8;i++) XX[56+i] = MDp->count[i]; - MDblock(MDp,(unsigned int *)XX); - } - else /* need to do two blocks to finish up */ - { MDblock(MDp,(unsigned int *)XX); - for (i=0;i<56;i++) XX[i] = 0; - for (i=0;i<8;i++) XX[56+i] = MDp->count[i]; - MDblock(MDp,(unsigned int *)XX); - } - /* Set flag saying we're done with MD computation */ - MDp->done = 1; - } - } - /* - ** End of md4.c - */ -#else -void md4_dummy() {;} -#endif diff --git a/source3/lib/md5.c b/source3/lib/md5.c new file mode 100644 index 0000000000..627725bb25 --- /dev/null +++ b/source3/lib/md5.c @@ -0,0 +1,245 @@ +/* + * 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" + +/* + * 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. + */ +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/source3/lib/messages.c b/source3/lib/messages.c new file mode 100644 index 0000000000..8602c2f32d --- /dev/null +++ b/source3/lib/messages.c @@ -0,0 +1,534 @@ +/* + Unix SMB/CIFS implementation. + Samba internal messaging functions + Copyright (C) Andrew Tridgell 2000 + Copyright (C) 2001 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. +*/ + +/** + @defgroups messages Internal messaging framework + @{ + @file messages.c + + This module is used 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. + + 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. +****************************************************************************/ + +void ping_message(int msg_type, pid_t src, void *buf, size_t len) +{ + 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); +} + +/**************************************************************************** + Return current debug level. +****************************************************************************/ + +void debuglevel_message(int msg_type, pid_t src, void *buf, size_t len) +{ + DEBUG(1,("INFO: Received REQ_DEBUGLEVEL message from PID %u\n",(unsigned int)src)); + message_send_pid(src, MSG_DEBUGLEVEL, DEBUGLEVEL_CLASS, sizeof(DEBUGLEVEL_CLASS), True); +} + +/**************************************************************************** + Initialise the messaging functions. +****************************************************************************/ + +BOOL message_init(void) +{ + if (tdb) return True; + + tdb = tdb_open_log(lock_path("messages.tdb"), + 0, TDB_CLEAR_IF_FIRST|TDB_DEFAULT, + O_RDWR|O_CREAT,0600); + + 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); + message_register(MSG_REQ_DEBUGLEVEL, debuglevel_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. +****************************************************************************/ + +BOOL message_send_pid(pid_t pid, int msg_type, const void *buf, size_t len, + BOOL duplicates_allowed) +{ + TDB_DATA kbuf; + TDB_DATA dbuf; + struct message_rec rec; + void *p; + + rec.msg_version = MESSAGE_VERSION; + rec.msg_type = msg_type; + rec.dest = pid; + rec.src = sys_getpid(); + rec.len = len; + + /* Doing kill with a non-positive pid causes messages to be + * sent to places we don't want. */ + SMB_ASSERT(pid > 0); + + kbuf = message_key_pid(pid); + + /* lock the record for the destination */ + tdb_chainlock(tdb, kbuf); + + dbuf = tdb_fetch(tdb, kbuf); + + if (!dbuf.dptr) { + /* its a new record */ + p = (void *)malloc(len + sizeof(rec)); + if (!p) goto failed; + + memcpy(p, &rec, sizeof(rec)); + if (len > 0) memcpy((void *)((char*)p+sizeof(rec)), buf, len); + + dbuf.dptr = p; + dbuf.dsize = len + sizeof(rec); + tdb_store(tdb, kbuf, dbuf, TDB_REPLACE); + SAFE_FREE(p); + goto ok; + } + + if (!duplicates_allowed) { + char *ptr; + struct message_rec prec; + + for(ptr = (char *)dbuf.dptr; ptr < dbuf.dptr + 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))) { + DEBUG(10,("message_send_pid: discarding duplicate message.\n")); + SAFE_FREE(dbuf.dptr); + tdb_chainunlock(tdb, kbuf); + return True; + } + } + memcpy(&prec, ptr, sizeof(prec)); + ptr += sizeof(rec) + prec.len; + } + } + + /* we're adding to an existing entry */ + p = (void *)malloc(dbuf.dsize + len + sizeof(rec)); + if (!p) goto failed; + + memcpy(p, dbuf.dptr, dbuf.dsize); + memcpy((void *)((char*)p+dbuf.dsize), &rec, sizeof(rec)); + if (len > 0) memcpy((void *)((char*)p+dbuf.dsize+sizeof(rec)), buf, len); + + SAFE_FREE(dbuf.dptr); + dbuf.dptr = p; + dbuf.dsize += len + sizeof(rec); + tdb_store(tdb, kbuf, dbuf, TDB_REPLACE); + SAFE_FREE(dbuf.dptr); + + ok: + tdb_chainunlock(tdb, kbuf); + errno = 0; /* paranoia */ + return message_notify(pid); + + failed: + tdb_chainunlock(tdb, kbuf); + errno = 0; /* paranoia */ + return False; +} + +/**************************************************************************** + Retrieve the next message for the current process. +****************************************************************************/ + +static BOOL message_recv(int *msg_type, pid_t *src, void **buf, size_t *len) +{ + TDB_DATA kbuf; + TDB_DATA dbuf; + struct message_rec rec; + + kbuf = message_key_pid(sys_getpid()); + + tdb_chainlock(tdb, kbuf); + + dbuf = tdb_fetch(tdb, kbuf); + if (dbuf.dptr == NULL || dbuf.dsize == 0) goto failed; + + memcpy(&rec, dbuf.dptr, sizeof(rec)); + + if (rec.msg_version != MESSAGE_VERSION) { + DEBUG(0,("message version %d received (expected %d)\n", rec.msg_version, MESSAGE_VERSION)); + goto failed; + } + + if (rec.len > 0) { + (*buf) = (void *)malloc(rec.len); + if (!(*buf)) goto failed; + + memcpy(*buf, dbuf.dptr+sizeof(rec), rec.len); + } else { + *buf = NULL; + } + + *len = rec.len; + *msg_type = rec.msg_type; + *src = rec.src; + + if (dbuf.dsize - (sizeof(rec)+rec.len) > 0) + memmove(dbuf.dptr, dbuf.dptr+sizeof(rec)+rec.len, dbuf.dsize - (sizeof(rec)+rec.len)); + dbuf.dsize -= sizeof(rec)+rec.len; + + if (dbuf.dsize == 0) + tdb_delete(tdb, kbuf); + else + tdb_store(tdb, kbuf, dbuf, TDB_REPLACE); + + SAFE_FREE(dbuf.dptr); + tdb_chainunlock(tdb, kbuf); + return True; + + failed: + tdb_chainunlock(tdb, kbuf); + return False; +} + +/**************************************************************************** + 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. +****************************************************************************/ + +void message_dispatch(void) +{ + int msg_type; + pid_t src; + void *buf; + size_t 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; + + while (message_recv(&msg_type, &src, &buf, &len)) { + DEBUG(10,("message_dispatch: received msg_type=%d src_pid=%d\n", + msg_type, (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, buf, len); + n_handled++; + } + } + if (!n_handled) { + DEBUG(5,("message_dispatch: warning: no handlers registed for " + "msg_type %d in pid%d\n", + msg_type, sys_getpid())); + } + SAFE_FREE(buf); + } +} + +/**************************************************************************** + Register a dispatch function for a particular message type. +****************************************************************************/ + +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; + 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; + + /* 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. + * + * @return 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; + 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; +} + +static VOLATILE sig_atomic_t gotalarm; + +/*************************************************************** + Signal function to tell us we timed out. +****************************************************************/ + +static void gotalarm_sig(void) +{ + gotalarm = 1; +} + +/** + * Lock the messaging tdb based on a string - this is used as a primitive + * form of mutex between smbd instances. + * + * @param name A string identifying the name of the mutex. + */ + +BOOL message_named_mutex(char *name, unsigned int timeout) +{ + TDB_DATA key; + int ret; + + if (!message_init()) + return False; + + key.dptr = name; + key.dsize = strlen(name)+1; + + if (timeout) { + gotalarm = 0; + CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig); + alarm(timeout); + } + + ret = tdb_chainlock(tdb, key); + + if (timeout) { + alarm(0); + CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN); + if (gotalarm) + return False; + } + + if (ret == 0) + DEBUG(10,("message_named_mutex: got mutex for %s\n", name )); + + return (ret == 0); +} + +/** + * Unlock a named mutex. + * + * @param name A string identifying the name of the mutex. + */ + +void message_named_mutex_release(char *name) +{ + TDB_DATA key; + + key.dptr = name; + key.dsize = strlen(name)+1; + + tdb_chainunlock(tdb, key); + DEBUG(10,("message_named_mutex: released mutex for %s\n", name )); +} + +/** @} **/ diff --git a/source3/lib/ms_fnmatch.c b/source3/lib/ms_fnmatch.c new file mode 100644 index 0000000000..106efa5bbc --- /dev/null +++ b/source3/lib/ms_fnmatch.c @@ -0,0 +1,225 @@ +/* + 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, int 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, int 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/source3/lib/netatalk.c b/source3/lib/netatalk.c new file mode 100644 index 0000000000..035f3794a0 --- /dev/null +++ b/source3/lib/netatalk.c @@ -0,0 +1,155 @@ +/* + Unix SMB/CIFS 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. +*/ + +/* + netatalk.c : routines for improving interaction between Samba and netatalk. + Copyright (C) John D. Blair <jdblair@cobaltnet.com> 1998 + Cobalt Networks, Inc. +*/ + +#include "includes.h" + +#ifdef WITH_NETATALK + +/***************** + ntalk_resourcepath: creates the path to the netatalk resource fork for + a given file + + fname: normal filename + doublename: buffer that will contain the location of the resource fork + length: length of this buffer (to prevent overflows) + + NOTE: doesn't currently gracefully deal with buffer overflows- it just + doesn't allow them to occur. +******************/ +void ntalk_resourcepath(const char *fname, char *doublename, const int length) +{ + int i; + int charnum; + int lastslash; + char appledouble[] = APPLEDOUBLE; + + /* copy fname to doublename and find last slash */ + for (i = 0; (fname[i] != 0) && (i <= length); i++) { + if (fname[i] == '/') { + lastslash = i; + } + doublename[i] = fname[i]; + } + lastslash++; /* location just after last slash */ + + /* insert .AppleDouble */ + charnum = lastslash; + for (i = 0; (appledouble[i] != 0) && (i <= length); i++) { + doublename[charnum] = appledouble[i]; + charnum++; + } + + /* append last part of file name */ + for (i = lastslash; (fname[i] != 0) && (i <= length); i++) { + doublename[charnum] = fname[i]; + charnum++; + } + + doublename[charnum] = 0; +} + +/********************** + ntalk_mkresdir: creates a new .AppleDouble directory (if necessary) + for the resource fork of a specified file +**********************/ +int ntalk_mkresdir(const char *fname) +{ + char fdir[255]; + int i; + int lastslash; + SMB_STRUCT_STAT dirstats; + char appledouble[] = APPLEDOUBLE; + + /* find directory containing fname */ + for (i = 0; (fname[i] != 0) && (i <= 254); i++) { + fdir[i] = fname[i]; + if (fdir[i] == '/') { + lastslash = i; + } + } + lastslash++; + fdir[lastslash] = 0; + sys_lstat(fdir, &dirstats); + + /* append .AppleDouble */ + for (i = 0; (appledouble[i] != 0) && (lastslash <= 254); i++) { + fdir[lastslash] = appledouble[i]; + lastslash++; + } + fdir[lastslash] = 0; + + /* create this directory */ + /* silently ignore EEXIST error */ + if ((mkdir(fdir, dirstats.st_mode) < 0) && (errno != EEXIST)) { + return errno; + } + + /* set ownership of this dir to the same as its parent */ + /* holy race condition, batman! */ + /* warning: this doesn't check for errors */ + chown(fdir, dirstats.st_uid, dirstats.st_gid); + + printf("%s\n", fdir); + + return 1; +} + +/********************** + ntalk_unlink: unlink a file and its resource fork +**********************/ +int ntalk_unlink(const char *fname) +{ + char buf[255]; + + ntalk_resourcepath(fname, buf, 255); + unlink(buf); + return unlink(fname); +} + +/********************** + ntalk_chown: chown a file and its resource fork +**********************/ +int ntalk_chown(const char *fname, const uid_t uid, const gid_t gid) +{ + char buf[255]; + + ntalk_resourcepath(fname, buf, 255); + chown(buf, uid, gid); + return chown(fname, uid, gid); +} + +/********************** + ntalk_chmod: chmod a file and its resource fork +**********************/ +int ntalk_chmod(const char *fname, mode_t perms) +{ + char buf[255]; + + ntalk_resourcepath(fname, buf, 255); + chmod(buf, perms); + return chmod(fname, perms); +} + +#endif /* WITH_NETATALK */ diff --git a/source3/lib/pam_errors.c b/source3/lib/pam_errors.c new file mode 100644 index 0000000000..f74e4bf176 --- /dev/null +++ b/source3/lib/pam_errors.c @@ -0,0 +1,125 @@ +/* + * 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 */ +const static 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 */ +const static 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_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/source3/lib/pidfile.c b/source3/lib/pidfile.c new file mode 100644 index 0000000000..28fd959b54 --- /dev/null +++ b/source3/lib/pidfile.c @@ -0,0 +1,112 @@ +/* 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(char *name) +{ + int fd; + char pidstr[20]; + unsigned ret; + pstring pidFile; + + slprintf(pidFile, sizeof(pidFile)-1, "%s/%s.pid", lp_lockdir(), 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 lock directory. open it and leave it locked. + This must be done after a call to lp_load() as it uses the lp_lockdir() + function to generate the path to the pidfile. */ + +void pidfile_create(char *name) +{ + int fd; + char buf[20]; + pstring pidFile; + pid_t pid; + + slprintf(pidFile, sizeof(pidFile)-1, "%s/%s.pid", lp_lockdir(), 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) sys_getpid()); + if (write(fd, buf, sizeof(buf)) != sizeof(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/source3/lib/readline.c b/source3/lib/readline.c new file mode 100644 index 0000000000..d80c571dd3 --- /dev/null +++ b/source3/lib/readline.c @@ -0,0 +1,119 @@ +/* + 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_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(char *prompt, void (*callback)(void), + char **(completion_fn)(char *text, int start, int end)) +{ + fd_set fds; + static pstring line; + struct timeval timeout; + int fd = fileno(stdin); + char *ret; + + x_fprintf(dbf, "%s", prompt); + x_fflush(dbf); + + 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 = fgets(line, sizeof(line), stdin); + return ret; + } + if (callback) + callback(); + } +} + +/**************************************************************************** + Display the prompt and wait for input. Call callback() regularly. +****************************************************************************/ + +char *smb_readline(char *prompt, void (*callback)(void), + char **(completion_fn)(char *text, int start, int end)) +{ +#if HAVE_LIBREADLINE + if (isatty(fileno(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); +} + +/**************************************************************************** +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/source3/lib/replace.c b/source3/lib/replace.c new file mode 100644 index 0000000000..dd50ff035e --- /dev/null +++ b/source3/lib/replace.c @@ -0,0 +1,416 @@ +/* + 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 */ + +#ifdef REPLACE_INET_NTOA +char *rep_inet_ntoa(struct in_addr ip) +{ + unsigned char *p = (unsigned char *)&ip.s_addr; + static char buf[18]; +#if WORDS_BIGENDIAN + slprintf(buf, 17, "%d.%d.%d.%d", + (int)p[0], (int)p[1], (int)p[2], (int)p[3]); +#else /* WORDS_BIGENDIAN */ + slprintf(buf, 17, "%d.%d.%d.%d", + (int)p[3], (int)p[2], (int)p[1], (int)p[0]); +#endif /* WORDS_BIGENDIAN */ + return buf; +} +#endif /* REPLACE_INET_NTOA */ + +#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 */ diff --git a/source3/lib/select.c b/source3/lib/select.c new file mode 100644 index 0000000000..550941ba77 --- /dev/null +++ b/source3/lib/select.c @@ -0,0 +1,159 @@ +/* + Unix SMB/CIFS implementation. + 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 != sys_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 = sys_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)) { + FD_CLR(select_pipe[0], readfds2); + ret--; + if (ret == 0) { + ret = -1; + errno = EINTR; + } + } + + saved_errno = errno; + + while (pipe_written != pipe_read) { + char c; + /* Due to the linux kernel bug in 2.0.x, we + * always increment here even if the read failed... */ + read(select_pipe[0], &c, 1); + pipe_read++; + } + + errno = saved_errno; + + 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; + + readfds2 = (readfds ? &readfds_buf : NULL); + writefds2 = (writefds ? &writefds_buf : NULL); + errorfds2 = (errorfds ? &errorfds_buf : NULL); + + do { + if (readfds) + readfds_buf = *readfds; + if (writefds) + writefds_buf = *writefds; + if (errorfds) + errorfds_buf = *errorfds; + ret = sys_select(maxfd, readfds2, writefds2, errorfds2, tval); + } 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/source3/lib/signal.c b/source3/lib/signal.c new file mode 100644 index 0000000000..99f908235c --- /dev/null +++ b/source3/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); +#else + ; +#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 )) +{ +#ifdef HAVE_SIGACTION + struct sigaction act; + + 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,NULL); +#else /* !HAVE_SIGACTION */ + /* FIXME: need to handle sigvec and systems with broken signal() */ + 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/source3/lib/smbpasswd.c b/source3/lib/smbpasswd.c new file mode 100644 index 0000000000..c27af5540b --- /dev/null +++ b/source3/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; + 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/source3/lib/smbrun.c b/source3/lib/smbrun.c new file mode 100644 index 0000000000..67f82ed0ad --- /dev/null +++ b/source3/lib/smbrun.c @@ -0,0 +1,180 @@ +/* + 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=sys_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 (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 */ + } + +#ifndef __INSURE__ + /* close all other file descriptors, leaving only 0, 1 and 2. 0 and + 2 point to /dev/null from the startup code */ + { + int fd; + for (fd=3;fd<256;fd++) close(fd); + } +#endif + + execl("/bin/sh","sh","-c",cmd,NULL); + + /* not reached */ + exit(82); + return 1; +} diff --git a/source3/lib/snprintf.c b/source3/lib/snprintf.c new file mode 100644 index 0000000000..9a9dcdbae1 --- /dev/null +++ b/source3/lib/snprintf.c @@ -0,0 +1,962 @@ +/* + * 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" +#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 + +static size_t dopr(char *buffer, size_t maxlen, const char *format, + va_list args); +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) +{ + char ch; + LLONG value; + LDOUBLE fvalue; + char *strvalue; + int min; + int max; + int state; + int flags; + int cflags; + size_t currlen; + + 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); + 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); + 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; + my_modf(intpart*0.1, &intpart); + temp = temp*0.1; + 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; + my_modf(fracpart*0.1, &fracpart); + temp = temp*0.1; + 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 (fplace > 0) + dopr_outch (buffer, currlen, maxlen, fconvert[--fplace]); + } + + while (zpadlen > 0) { + dopr_outch (buffer, currlen, maxlen, '0'); + --zpadlen; + } + + 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; + + ret = vsnprintf(NULL, 0, format, ap); + if (ret <= 0) return ret; + + (*ptr) = (char *)malloc(ret+1); + if (!*ptr) return -1; + ret = vsnprintf(*ptr, ret+1, format, ap); + + 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 + +#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); + free(msg); +} +#endif /* HAVE_SYSLOG */ +#endif /* HAVE_VSYSLOG */ + +#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, 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++) { + snprintf(buf1, sizeof(buf1), "%1.1f", v0*pow(10, x)); + sprintf(buf2, "%1.1f", v0*pow(10, x)); + if (strcmp(buf1, buf2)) { + printf("we seem to support %d digits\n", x-1); + break; + } + } + } + + return 0; +} +#endif /* SNPRINTF_TEST */ diff --git a/source3/lib/substitute.c b/source3/lib/substitute.c new file mode 100644 index 0000000000..e878ee8cbf --- /dev/null +++ b/source3/lib/substitute.c @@ -0,0 +1,358 @@ +/* + 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" + +fstring local_machine=""; +fstring remote_arch="UNKNOWN"; +userdom_struct current_user_info; +fstring remote_proto="UNKNOWN"; +fstring remote_machine=""; +extern pstring global_myname; + +/******************************************************************* + Given a pointer to a %$(NAME) expand it as an environment variable. + Return the number of characters by which the pointer should be advanced. + Based on code by Branko Cibej <branko.cibej@hermes.si> + When this is called p points at the '%' character. +********************************************************************/ + +static size_t expand_env_var(char *p, int len) +{ + fstring envname; + char *envval; + char *q, *r; + int copylen; + + if (p[1] != '$') + return 1; + + if (p[2] != '(') + return 2; + + /* + * Look for the terminating ')'. + */ + + if ((q = strchr_m(p,')')) == NULL) { + DEBUG(0,("expand_env_var: Unterminated environment variable [%s]\n", p)); + return 2; + } + + /* + * Extract the name from within the %$(NAME) string. + */ + + r = p+3; + copylen = MIN((q-r),(sizeof(envname)-1)); + strncpy(envname,r,copylen); + envname[copylen] = '\0'; + + if ((envval = getenv(envname)) == NULL) { + DEBUG(0,("expand_env_var: Environment variable [%s] not set\n", envname)); + return 2; + } + + /* + * Copy the full %$(NAME) into envname so it + * can be replaced. + */ + + copylen = MIN((q+1-p),(sizeof(envname)-1)); + strncpy(envname,p,copylen); + envname[copylen] = '\0'; + string_sub(p,envname,envval,len); + return 0; /* Allow the environment contents to be parsed. */ +} + +/******************************************************************* + Patch from jkf@soton.ac.uk + Added this to implement %p (NIS auto-map version of %H) +*******************************************************************/ + +static char *automount_path(const char *user_name) +{ + static pstring server_path; + + /* use the passwd entry as the default */ + /* this will be the default if WITH_AUTOMOUNT is not used or fails */ + + pstrcpy(server_path, get_user_home_dir(user_name)); + +#if (defined(HAVE_NETGROUP) && defined (WITH_AUTOMOUNT)) + + if (lp_nis_home_map()) { + char *home_path_start; + char *automount_value = automount_lookup(user_name); + + if(strlen(automount_value) > 0) { + home_path_start = strchr_m(automount_value,':'); + if (home_path_start != NULL) { + DEBUG(5, ("NIS lookup succeeded. Home path is: %s\n", + home_path_start?(home_path_start+1):"")); + pstrcpy(server_path, home_path_start+1); + } + } else { + /* NIS key lookup failed: default to user home directory from password file */ + DEBUG(5, ("NIS lookup failed. Using Home path from passwd file. Home path is: %s\n", server_path )); + } + } +#endif + + DEBUG(4,("Home server path: %s\n", server_path)); + + return server_path; +} + +/******************************************************************* + Patch from jkf@soton.ac.uk + This is Luke's original function with the NIS lookup code + moved out to a separate function. +*******************************************************************/ + +static char *automount_server(const char *user_name) +{ + static pstring server_name; + + /* use the local machine name as the default */ + /* this will be the default if WITH_AUTOMOUNT is not used or fails */ + if (*local_machine) + pstrcpy(server_name, local_machine); + else + pstrcpy(server_name, global_myname); + +#if (defined(HAVE_NETGROUP) && defined (WITH_AUTOMOUNT)) + + if (lp_nis_home_map()) { + int home_server_len; + char *automount_value = automount_lookup(user_name); + home_server_len = strcspn(automount_value,":"); + DEBUG(5, ("NIS lookup succeeded. Home server length: %d\n",home_server_len)); + if (home_server_len > sizeof(pstring)) + home_server_len = sizeof(pstring); + strncpy(server_name, automount_value, home_server_len); + server_name[home_server_len] = '\0'; + } +#endif + + DEBUG(4,("Home server: %s\n", server_name)); + + return server_name; +} + +/**************************************************************************** + Do some standard substitutions in a string. +****************************************************************************/ + +void standard_sub_basic(const char *smb_name, char *str) +{ + char *p, *s; + fstring pidstr; + struct passwd *pass; + + for (s=str; (p=strchr_m(s, '%'));s=p) { + fstring tmp_str; + + int l = sizeof(pstring) - (int)(p-str); + + switch (*(p+1)) { + case 'U' : + fstrcpy(tmp_str, smb_name); + strlower(tmp_str); + string_sub(p,"%U",tmp_str,l); + break; + case 'G' : + fstrcpy(tmp_str, smb_name); + if ((pass = Get_Pwnam(tmp_str))!=NULL) { + string_sub(p,"%G",gidtoname(pass->pw_gid),l); + } else { + p += 2; + } + break; + case 'D' : + fstrcpy(tmp_str, current_user_info.domain); + strupper(tmp_str); + string_sub(p,"%D", tmp_str,l); + break; + case 'I' : string_sub(p,"%I", client_addr(),l); break; + case 'L' : + if (*local_machine) { + string_sub(p,"%L", local_machine,l); + } else { + string_sub(p,"%L", global_myname,l); + } + break; + case 'M' : string_sub(p,"%M", client_name(),l); break; + case 'R' : string_sub(p,"%R", remote_proto,l); break; + case 'T' : string_sub(p,"%T", timestring(False),l); break; + case 'a' : string_sub(p,"%a", remote_arch,l); break; + case 'd' : + slprintf(pidstr,sizeof(pidstr)-1, "%d",(int)sys_getpid()); + string_sub(p,"%d", pidstr,l); + break; + case 'h' : string_sub(p,"%h", myhostname(),l); break; + case 'm' : string_sub(p,"%m", remote_machine,l); break; + case 'v' : string_sub(p,"%v", VERSION,l); break; + case '$' : p += expand_env_var(p,l); break; /* Expand environment variables */ + case '\0': + p++; + break; /* don't run off the end of the string */ + + default: p+=2; + break; + } + } +} + +/**************************************************************************** + Do some standard substitutions in a string. +****************************************************************************/ + +void standard_sub_advanced(int snum, const char *user, const char *connectpath, gid_t gid, const char *smb_name, char *str) +{ + char *p, *s, *home; + + for (s=str; (p=strchr_m(s, '%'));s=p) { + int l = sizeof(pstring) - (int)(p-str); + + switch (*(p+1)) { + case 'N' : string_sub(p,"%N", automount_server(user),l); break; + case 'H': + if ((home = get_user_home_dir(user))) { + string_sub(p,"%H",home, l); + } else { + p += 2; + } + break; + case 'P': + string_sub(p,"%P", connectpath, l); + break; + + case 'S': + string_sub(p,"%S", lp_servicename(snum), l); + break; + + case 'g': + string_sub(p,"%g", gidtoname(gid), l); + break; + case 'u': + string_sub(p,"%u", user, l); + break; + + /* Patch from jkf@soton.ac.uk Left the %N (NIS + * server name) in standard_sub_basic as it is + * a feature for logon servers, hence uses the + * username. The %p (NIS server path) code is + * here as it is used instead of the default + * "path =" string in [homes] and so needs the + * service name, not the username. */ + case 'p': + string_sub(p,"%p", automount_path(lp_servicename(snum)), l); + break; + case '\0': + p++; + break; /* don't run off the end of the string */ + + default: p+=2; + break; + } + } + + standard_sub_basic(smb_name, str); +} + +/**************************************************************************** + Do some standard substitutions in a string. +****************************************************************************/ + +void standard_sub_conn(connection_struct *conn, char *str) +{ + standard_sub_advanced(SNUM(conn), conn->user, conn->connectpath, conn->gid, current_user_info.smb_name, str); +} + +/**************************************************************************** + Like standard_sub but for a homes share where snum still points to the [homes] + share. No user specific snum created yet so servicename should be the username. +****************************************************************************/ + +void standard_sub_home(int snum, const char *user, char *str) +{ + char *p, *s; + + for (s=str; (p=strchr_m(s, '%'));s=p) { + int l = sizeof(pstring) - (int)(p-str); + + switch (*(p+1)) { + case 'S': + string_sub(p,"%S", user, l); + break; + case 'p': + string_sub(p,"%p", automount_path(user), l); + break; + case '\0': + p++; + break; /* don't run off the end of the string */ + + default: p+=2; + break; + } + } + + standard_sub_advanced(snum, user, "", -1, current_user_info.smb_name, str); +} + +/**************************************************************************** + Like standard_sub but by snum. +****************************************************************************/ + +void standard_sub_snum(int snum, char *str) +{ + extern struct current_user current_user; + static uid_t cached_uid = -1; + static fstring cached_user; + /* calling uidtoname() on every substitute would be too expensive, so + we cache the result here as nearly every call is for the same uid */ + + if (cached_uid != current_user.uid) { + fstrcpy(cached_user, uidtoname(current_user.uid)); + cached_uid = current_user.uid; + } + + standard_sub_advanced(snum, cached_user, "", -1, current_user_info.smb_name, str); +} + +/******************************************************************* + Substitute strings with useful parameters. +********************************************************************/ + +void standard_sub_vuser(char *str, user_struct *vuser) +{ + standard_sub_advanced(-1, vuser->user.unix_name, "", -1, current_user_info.smb_name, str); +} + +/******************************************************************* + Substitute strings with useful parameters. +********************************************************************/ + +void standard_sub_vsnum(char *str, user_struct *vuser, int snum) +{ + standard_sub_advanced(snum, vuser->user.unix_name, "", -1, current_user_info.smb_name, str); +} diff --git a/source3/lib/sysacls.c b/source3/lib/sysacls.c new file mode 100644 index 0000000000..22245992f5 --- /dev/null +++ b/source3/lib/sysacls.c @@ -0,0 +1,3210 @@ +/* + 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: + if ((pw = sys_getpwuid(ap->a_id)) == NULL) { + slprintf(idbuf, sizeof(idbuf)-1, "%ld", + (long)ap->a_id); + id = idbuf; + } else { + id = pw->pw_name; + } + 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; + + 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: + if ((pw = sys_getpwuid(ap->a_id)) == NULL) { + slprintf(idbuf, sizeof(idbuf)-1, "%ld", + (long)ap->a_id); + id = idbuf; + } else { + id = pw->pw_name; + } + 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/source3/lib/system.c b/source3/lib/system.c index 938746e9c9..8c7eec939e 100644 --- a/source3/lib/system.c +++ b/source3/lib/system.c @@ -1,8 +1,8 @@ /* - Unix SMB/Netbios implementation. - Version 1.9. + Unix SMB/CIFS implementation. Samba system utilities - Copyright (C) Andrew Tridgell 1992-1995 + 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 @@ -21,202 +21,1214 @@ #include "includes.h" -extern int DEBUGLEVEL; - /* The idea is that this file will eventually have wrappers around all - important system calls in samba. The aim is twofold: + 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. */ + /******************************************************************* -this replaces the normal select() system call -return if some data has arrived on one of the file descriptors -return -1 means error + A wrapper for usleep in case we don't have one. ********************************************************************/ -#ifdef NO_SELECT -static int pollfd(int fd) -{ - int r=0; - -#ifdef HAS_RDCHK - r = rdchk(fd); -#elif defined(TCRDCHK) - (void)ioctl(fd, TCRDCHK, &r); -#else - (void)ioctl(fd, FIONREAD, &r); -#endif - return(r); -} - -int sys_select(fd_set *fds,struct timeval *tval) +int sys_usleep(long usecs) { - fd_set fds2; - int counter=0; - int found=0; +#ifndef HAVE_USLEEP + struct timeval tval; +#endif - FD_ZERO(&fds2); + /* + * We need this braindamage as the glibc usleep + * is not SPEC1170 complient... grumble... JRA. + */ - while (1) - { - int i; - for (i=0;i<255;i++) { - if (FD_ISSET(i,fds) && pollfd(i)>0) { - found++; - FD_SET(i,&fds2); + if(usecs < 0 || usecs > 1000000) { + errno = EINVAL; + return -1; } - } - if (found) { - memcpy((void *)fds,(void *)&fds2,sizeof(fds2)); - return(found); - } - - if (tval && tval.tv_sec < counter) return(0); - sleep(1); - counter++; - } +#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 */ } -#else -int sys_select(fd_set *fds,struct timeval *tval) +/******************************************************************* +A stat() wrapper that will deal with 64 bit filesizes. +********************************************************************/ + +int sys_stat(const char *fname,SMB_STRUCT_STAT *sbuf) { - struct timeval t2; - int selrtn; + 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; +} - do { - if (tval) memcpy((void *)&t2,(void *)tval,sizeof(t2)); - errno = 0; - selrtn = select(16,SELECT_CAST fds,NULL,NULL,tval?&t2:NULL); - } while (selrtn<0 && errno == EINTR); +/******************************************************************* + An fstat() wrapper that will deal with 64 bit filesizes. +********************************************************************/ - return(selrtn); -} +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; +} /******************************************************************* -just a unlink wrapper + An lstat() wrapper that will deal with 64 bit filesizes. ********************************************************************/ -int sys_unlink(char *fname) + +int sys_lstat(const char *fname,SMB_STRUCT_STAT *sbuf) { - return(unlink(dos_to_unix(fname,False))); + 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; } - /******************************************************************* -a simple open() wrapper + An ftruncate() wrapper that will deal with 64 bit filesizes. ********************************************************************/ -int sys_open(char *fname,int flags,int mode) + +int sys_ftruncate(int fd, SMB_OFF_T offset) { - return(open(dos_to_unix(fname,False),flags,mode)); +#if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_OFF64_T) && defined(HAVE_FTRUNCATE64) + return ftruncate64(fd, offset); +#else + return ftruncate(fd, offset); +#endif } - /******************************************************************* -a simple opendir() wrapper + An lseek() wrapper that will deal with 64 bit filesizes. ********************************************************************/ -DIR *sys_opendir(char *dname) + +SMB_OFF_T sys_lseek(int fd, SMB_OFF_T offset, int whence) { - return(opendir(dos_to_unix(dname,False))); +#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 +} /******************************************************************* -and a stat() wrapper + An ftell() wrapper that will deal with 64 bit filesizes. ********************************************************************/ -int sys_stat(char *fname,struct stat *sbuf) + +SMB_OFF_T sys_ftell(FILE *fp) { - return(stat(dos_to_unix(fname,False),sbuf)); +#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 } /******************************************************************* -don't forget lstat() + A creat() wrapper that will deal with 64 bit filesizes. ********************************************************************/ -int sys_lstat(char *fname,struct stat *sbuf) + +int sys_creat(const char *path, mode_t mode) { - return(lstat(dos_to_unix(fname,False),sbuf)); +#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 +} /******************************************************************* -mkdir() gets a wrapper + An fopen() wrapper that will deal with 64 bit filesizes. ********************************************************************/ -int sys_mkdir(char *dname,int mode) + +FILE *sys_fopen(const char *path, const char *type) { - return(mkdir(dos_to_unix(dname,False),mode)); +#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 +} /******************************************************************* -do does rmdir() + An mknod() wrapper that will deal with 64 bit filesizes. ********************************************************************/ -int sys_rmdir(char *dname) + +int sys_mknod(const char *path, mode_t mode, SMB_DEV_T dev) { - return(rmdir(dos_to_unix(dname,False))); +#if defined(HAVE_MKNOD) || defined(HAVE_MKNOD64) +#if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_MKNOD64) && defined(HAVE_DEV64_T) + return mknod64(path, mode, dev); +#else + return mknod(path, mode, dev); +#endif +#else + /* No mknod system call. */ + errno = ENOSYS; + return -1; +#endif } +/******************************************************************* + Wrapper for realpath. +********************************************************************/ + +char *sys_realpath(const char *path, char *resolved_path) +{ +#if defined(HAVE_REALPATH) + return realpath(path, resolved_path); +#else + /* As realpath is not a system call we can't return ENOSYS. */ + errno = EINVAL; + return NULL; +#endif +} /******************************************************************* -I almost forgot chdir() +The wait() calls vary between systems ********************************************************************/ -int sys_chdir(char *dname) + +int sys_waitpid(pid_t pid,int *status,int options) { - return(chdir(dos_to_unix(dname,False))); +#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; +} /******************************************************************* -now for utime() +system wrapper for symlink ********************************************************************/ -int sys_utime(char *fname,struct utimbuf *times) + +int sys_symlink(const char *oldpath, const char *newpath) { - return(utime(dos_to_unix(fname,False),times)); +#ifndef HAVE_SYMLINK + errno = ENOSYS; + return -1; +#else + return symlink(oldpath, newpath); +#endif } /******************************************************************* -for rename() +system wrapper for readlink ********************************************************************/ -int sys_rename(char *from, char *to) + +int sys_readlink(const char *path, char *buf, size_t bufsiz) { -#ifdef KANJI - pstring zfrom, zto; - strcpy (zfrom, dos_to_unix (from, False)); - strcpy (zto, dos_to_unix (to, False)); - return rename (zfrom, zto); -#else - return rename (from, to); -#endif /* KANJI */ +#ifndef HAVE_READLINK + errno = ENOSYS; + return -1; +#else + return readlink(path, buf, bufsiz); +#endif } +/******************************************************************* +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 +} /******************************************************************* chown isn't used much but OS/2 doesn't have it ********************************************************************/ -int sys_chown(char *fname,int uid,int gid) + +int sys_chown(const char *fname,uid_t uid,gid_t gid) { -#ifdef NO_CHOWN - DEBUG(1,("Warning - chown(%s,%d,%d) not done\n",fname,uid,gid)); +#ifndef HAVE_CHOWN + static int done; + if (!done) { + DEBUG(1,("WARNING: no chown!\n")); + done=1; + } #else - return(chown(fname,uid,gid)); + return(chown(fname,uid,gid)); #endif } /******************************************************************* os/2 also doesn't have chroot ********************************************************************/ -int sys_chroot(char *dname) +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 */ + +/* + * We only wrap pw_name and pw_passwd for now as these + * are the only potentially modified fields. + */ + +/************************************************************************** + Helper function for getpwnam/getpwuid wrappers. +****************************************************************************/ + +struct saved_pw { + fstring pw_name; + fstring pw_passwd; + fstring pw_gecos; + pstring pw_dir; + pstring pw_shell; + struct passwd pass; +}; + +static struct saved_pw pw_mod; /* This is the structure returned - can be modified. */ +static struct saved_pw pw_cache; /* This is the structure saved - used to check cache. */ + +static int num_lookups; /* Counter so we don't always use cache. */ +#ifndef PW_RET_CACHE_MAX_LOOKUPS +#define PW_RET_CACHE_MAX_LOOKUPS 100 +#endif + +static void copy_pwent(struct saved_pw *dst, struct passwd *pass) { -#ifdef NO_CHROOT - DEBUG(1,("Warning - chroot(%s) not done\n",dname)); + memcpy((char *)&dst->pass, pass, sizeof(struct passwd)); + + fstrcpy(dst->pw_name, pass->pw_name); + dst->pass.pw_name = dst->pw_name; + + fstrcpy(dst->pw_passwd, pass->pw_passwd); + dst->pass.pw_passwd = dst->pw_passwd; + + fstrcpy(dst->pw_gecos, pass->pw_gecos); + dst->pass.pw_gecos = dst->pw_gecos; + + pstrcpy(dst->pw_dir, pass->pw_dir); + dst->pass.pw_dir = dst->pw_dir; + + pstrcpy(dst->pw_shell, pass->pw_shell); + dst->pass.pw_shell = dst->pw_shell; +} + +static struct passwd *setup_pwret(struct passwd *pass) +{ + if (pass == NULL) { + /* Clear the caches. */ + memset(&pw_cache, '\0', sizeof(struct saved_pw)); + memset(&pw_mod, '\0', sizeof(struct saved_pw)); + num_lookups = 0; + return NULL; + } + + copy_pwent( &pw_mod, pass); + + if (pass != &pw_cache.pass) { + + /* If it's a cache miss we must also refill the cache. */ + + copy_pwent( &pw_cache, pass); + num_lookups = 1; + + } else { + + /* Cache hit. */ + + num_lookups++; + num_lookups = (num_lookups % PW_RET_CACHE_MAX_LOOKUPS); + } + + return &pw_mod.pass; +} + +/************************************************************************** + Wrappers for setpwent(), getpwent() and endpwent() +****************************************************************************/ + +void sys_setpwent(void) +{ + setup_pwret(NULL); /* Clear cache. */ + setpwent(); +} + +struct passwd *sys_getpwent(void) +{ + return setup_pwret(getpwent()); +} + +void sys_endpwent(void) +{ + setup_pwret(NULL); /* Clear cache. */ + endpwent(); +} + +/************************************************************************** + Wrapper for getpwnam(). Always returns a static that can be modified. +****************************************************************************/ + +struct passwd *sys_getpwnam(const char *name) +{ + if (!name || !name[0]) + return NULL; + + /* check for a cache hit first */ + if (num_lookups && pw_cache.pass.pw_name && !strcmp(name, pw_cache.pass.pw_name)) { + return setup_pwret(&pw_cache.pass); + } + + return setup_pwret(getpwnam(name)); +} + +/************************************************************************** + Wrapper for getpwuid(). Always returns a static that can be modified. +****************************************************************************/ + +struct passwd *sys_getpwuid(uid_t uid) +{ + if (num_lookups && pw_cache.pass.pw_name && (uid == pw_cache.pass.pw_uid)) { + return setup_pwret(&pw_cache.pass); + } + + return setup_pwret(getpwuid(uid)); +} + +#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 fork. Ensures that mypid is reset. Used so we can write + a sys_getpid() that only does a system call *once*. +****************************************************************************/ + +static pid_t mypid = (pid_t)-1; + +pid_t sys_fork(void) +{ + pid_t forkret = fork(); + + if (forkret == (pid_t)0) /* Child - reset mypid so sys_getpid does a system call. */ + mypid = (pid_t) -1; + + return forkret; +} + +/************************************************************************** + Wrapper for getpid. Ensures we only do a system call *once*. +****************************************************************************/ + +pid_t sys_getpid(void) +{ + if (mypid == (pid_t)-1) + mypid = getpid(); + + return mypid; +} + +/************************************************************************** + 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 = sys_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_LIBDL) || defined(HAVE_DLOPEN) + return dlopen(name, flags); +#else + return NULL; +#endif +} + +void *sys_dlsym(void *handle, char *symbol) +{ +#if defined(HAVE_LIBDL) || defined(HAVE_DLSYM) + return dlsym(handle, symbol); +#else + return NULL; +#endif +} + +int sys_dlclose (void *handle) +{ +#if defined(HAVE_LIBDL) || defined(HAVE_DLCLOSE) + return dlclose(handle); +#else + return 0; +#endif +} + +const char *sys_dlerror(void) +{ +#if defined(HAVE_LIBDL) || defined(HAVE_DLERROR) + return dlerror(); +#else + return NULL; +#endif +} + +/************************************************************************** + Wrapper for Admin Logs. +****************************************************************************/ + +void sys_adminlog(int priority, const char *format_str, ...) +{ + va_list ap; + int ret; + char **msgbuf = NULL; + + if (!lp_admin_log()) + return; + + va_start( ap, format_str ); + ret = vasprintf( msgbuf, format_str, ap ); + va_end( ap ); + + if (ret == -1) + return; + +#if defined(HAVE_SYSLOG) + syslog( priority, "%s", *msgbuf ); #else - return(chroot(dname)); + DEBUG(0,("%s", *msgbuf )); #endif + SAFE_FREE(*msgbuf); } diff --git a/source3/lib/talloc.c b/source3/lib/talloc.c new file mode 100644 index 0000000000..6ac784a929 --- /dev/null +++ b/source3/lib/talloc.c @@ -0,0 +1,441 @@ +/* + 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; + size_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_ctx; +}; + + +/** + * 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. + **/ +TALLOC_CTX *list_head = NULL; + + +/** + * Add to the global list + **/ +static void talloc_enroll(TALLOC_CTX *t) +{ + t->next_ctx = list_head; + list_head = t; +} + + +static void talloc_disenroll(TALLOC_CTX *t) +{ + TALLOC_CTX **ttmp; + + /* Use a double-* so that no special case is required for the + * list head. */ + for (ttmp = &list_head; *ttmp; ttmp = &((*ttmp)->next_ctx)) + if (*ttmp == t) { + /* ttmp is the link that points to t, either + * list_head or the next_ctx link in its + * predecessor */ + *ttmp = t->next_ctx; + t->next_ctx = NULL; /* clobber */ + return; + } + abort(); /* oops, this talloc was already + * clobbered or something else went + * wrong. */ +} + + +/** Create a new talloc context. **/ +TALLOC_CTX *talloc_init(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. + * Please call this in preference to talloc_init(). + **/ + TALLOC_CTX *talloc_init_named(char const *fmt, ...) +{ + TALLOC_CTX *t; + va_list ap; + + t = talloc_init(); + if (t && fmt) { + va_start(ap, fmt); + t->name = talloc_vasprintf(t, fmt, ap); + va_end(ap); + } + + 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); + memset(t, 0, sizeof(TALLOC_CTX)); + SAFE_FREE(t); +} + +/** Return the current total size of the pool. */ +size_t talloc_pool_size(TALLOC_CTX *t) +{ + if (t) + return t->total_alloc_size; + else + return 0; +} + +const char * talloc_pool_name(TALLOC_CTX const *t) +{ + if (t) + return t->name; + else + 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) +{ + if (p) + return talloc_memdup(t, p, strlen(p) + 1); + else + 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; + + len = vsnprintf(NULL, 0, fmt, ap); + + ret = talloc(t, len+1); + if (ret) + vsnprintf(ret, len+1, fmt, ap); + + 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; + + s_len = strlen(s); + len = vsnprintf(NULL, 0, fmt, ap); + + s = talloc_realloc(t, s, s_len + len+1); + if (!s) return NULL; + + vsnprintf(s+s_len, len+1, fmt, ap); + + 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) sys_getpid()); + s = talloc_asprintf_append(rt, s, "%-40s %8s %8s\n", + "name", "chunks", "bytes"); + s = talloc_asprintf_append(rt, s, "%-40s %8s %8s\n", + "----------------------------------------", + "--------", + "--------"); + + for (it = list_head; it; it = it->next_ctx) { + 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; + } + + 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; + } + } +} + + +/** @} */ diff --git a/source3/lib/tallocmsg.c b/source3/lib/tallocmsg.c new file mode 100644 index 0000000000..608cdad452 --- /dev/null +++ b/source3/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_named("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/source3/lib/talloctort.c b/source3/lib/talloctort.c new file mode 100644 index 0000000000..3a64803776 --- /dev/null +++ b/source3/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_named("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/source3/lib/time.c b/source3/lib/time.c new file mode 100644 index 0000000000..5fc43612dd --- /dev/null +++ b/source3/lib/time.c @@ -0,0 +1,749 @@ +/* + Unix SMB/CIFS implementation. + time handling 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" + +/* + 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 + +/******************************************************************* + External access to time_t_min and time_t_max. +********************************************************************/ + +time_t get_time_t_min(void) +{ + return TIME_T_MIN; +} + +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*extra_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*extra_time_offset; + int d = TimeZoneFaster(lt); + time_t t = lt + d; + + /* if overflow occurred, ignore all the adjustments so far */ + if (((lte < lt) ^ (extra_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(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); + + /* this takes us from kludge-GMT to real GMT */ + ret -= get_serverzone(); + ret += LocTimeDiff(ret); + + return(ret); +} + + + +/**************************************************************************** +interprets an nt time into a unix time_t +****************************************************************************/ +time_t interpret_long_date(char *p) +{ + NTTIME nt; + nt.low = IVAL(p,0); + nt.high = IVAL(p,4); + return nt_time_to_unix(&nt); +} + +/**************************************************************************** +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 -= LocTimeDiff(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; + } + + /* this converts GMT to kludge-GMT */ + t -= LocTimeDiff(t) - get_serverzone(); + + 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 == 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; + + 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 = 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(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(time_t t) +{ + static fstring buf; + struct tm *tm = LocalTime(&t); + + if (!tm) + slprintf(buf,sizeof(buf)-1,"%ld seconds since the Epoch",(long)t); + else +#ifndef HAVE_STRFTIME + fstrcpy(buf, asctime(tm)); + if(buf[strlen(buf)-1] == '\n') + buf[strlen(buf)-1] = 0; +#else /* !HAVE_STRFTIME */ + strftime(buf, sizeof(buf)-1, "%a, %d %b %Y %H:%M:%S %Z", tm); +#endif /* !HAVE_STRFTIME */ + return buf; +} + + + +/**************************************************************************** + Return the date and time as a string +****************************************************************************/ + +char *timestring(BOOL hires) +{ + static fstring TimeBuf; + 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) { + slprintf(TimeBuf, + sizeof(TimeBuf)-1, + "%ld.%06ld seconds since the Epoch", + (long)tp.tv_sec, + (long)tp.tv_usec); + } else { + slprintf(TimeBuf, + sizeof(TimeBuf)-1, + "%ld seconds since the Epoch", + (long)t); + } + } else { +#ifdef HAVE_STRFTIME + if (hires) { + strftime(TimeBuf,sizeof(TimeBuf)-1,"%Y/%m/%d %H:%M:%S",tm); + slprintf(TimeBuf+strlen(TimeBuf), + sizeof(TimeBuf)-1 - strlen(TimeBuf), + ".%06ld", + (long)tp.tv_usec); + } else { + strftime(TimeBuf,100,"%Y/%m/%d %H:%M:%S",tm); + } +#else + if (hires) { + slprintf(TimeBuf, + sizeof(TimeBuf)-1, + "%s.%06ld", + asctime(tm), + (long)tp.tv_usec); + } else { + fstrcpy(TimeBuf, 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; +} diff --git a/source3/lib/ufc.c b/source3/lib/ufc.c index 8417285821..ecc04d9e97 100644 --- a/source3/lib/ufc.c +++ b/source3/lib/ufc.c @@ -16,12 +16,14 @@ */ -#ifdef UFC_CRYPT +#include "includes.h" + +#ifndef HAVE_CRYPT /* * UFC-crypt: ultra fast crypt(3) implementation * - * Copyright (C) 1991, 1992, Free Software Foundation, Inc. + * Copyright (C) 1991-1998, Free Software Foundation, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -42,7 +44,6 @@ * Support routines * */ -#include "includes.h" #ifndef long32 @@ -281,9 +282,7 @@ static ufc_long longmask[32] = { * bzero and some don't have memset. */ -static void clearmem(start, cnt) - char *start; - int cnt; +static void clearmem(char *start, int cnt) { while(cnt--) *start++ = '\0'; } @@ -299,7 +298,7 @@ static int initialized = 0; * by fcrypt users. */ -static void ufc_init_des() +static void ufc_init_des(void) { int comes_from_bit; int bit, sg; ufc_long j; @@ -350,13 +349,13 @@ static void ufc_init_des() clearmem((char*)eperm32tab, sizeof(eperm32tab)); for(bit = 0; bit < 48; bit++) { - ufc_long mask1,comes_from; + ufc_long inner_mask1,comes_from; comes_from = perm32[esel[bit]-1]-1; - mask1 = bytemask[comes_from % 8]; + inner_mask1 = bytemask[comes_from % 8]; for(j = 256; j--;) { - if(j & mask1) + if(j & inner_mask1) eperm32tab[comes_from / 8][j][bit / 24] |= BITMASK(bit % 24); } } @@ -431,7 +430,7 @@ static void ufc_init_des() clearmem((char*)efp, sizeof efp); for(bit = 0; bit < 64; bit++) { int o_bit, o_long; - ufc_long word_value, mask1, mask2; + ufc_long word_value, inner_mask1, inner_mask2; int comes_from_f_bit, comes_from_e_bit; int comes_from_word, bit_within_word; @@ -451,12 +450,12 @@ static void ufc_init_des() comes_from_word = comes_from_e_bit / 6; /* 0..15 */ bit_within_word = comes_from_e_bit % 6; /* 0..5 */ - mask1 = longmask[bit_within_word + 26]; - mask2 = longmask[o_bit]; + inner_mask1 = longmask[bit_within_word + 26]; + inner_mask2 = longmask[o_bit]; for(word_value = 64; word_value--;) { - if(word_value & mask1) - efp[comes_from_word][word_value][o_long] |= mask2; + if(word_value & inner_mask1) + efp[comes_from_word][word_value][o_long] |= inner_mask2; } } initialized++; @@ -468,9 +467,7 @@ static void ufc_init_des() */ #ifdef _UFC_32_ -static void shuffle_sb(k, saltbits) - long32 *k; - ufc_long saltbits; +static void shuffle_sb(long32 *k, ufc_long saltbits) { ufc_long j; long32 x; for(j=4096; j--;) { @@ -482,9 +479,7 @@ static void shuffle_sb(k, saltbits) #endif #ifdef _UFC_64_ -static void shuffle_sb(k, saltbits) - long64 *k; - ufc_long saltbits; +static void shuffle_sb(long64 *k, ufc_long saltbits) { ufc_long j; long64 x; for(j=4096; j--;) { @@ -503,9 +498,9 @@ static unsigned char current_salt[3] = "&&"; /* invalid value */ static ufc_long current_saltbits = 0; static int direction = 0; -static void setup_salt(char *s1) +static void setup_salt(const char *s1) { ufc_long i, j, saltbits; - unsigned char *s2 = (unsigned char *)s1; + const unsigned char *s2 = (const unsigned char *)s1; if(!initialized) ufc_init_des(); @@ -543,8 +538,7 @@ static void setup_salt(char *s1) current_saltbits = saltbits; } -static void ufc_mk_keytab(key) - char *key; +static void ufc_mk_keytab(char *key) { ufc_long v1, v2, *k1; int i; #ifdef _UFC_32_ @@ -593,8 +587,7 @@ static void ufc_mk_keytab(key) * Undo an extra E selection and do final permutations */ -ufc_long *_ufc_dofinalperm(l1, l2, r1, r2) - ufc_long l1,l2,r1,r2; +ufc_long *_ufc_dofinalperm(ufc_long l1, ufc_long l2, ufc_long r1, ufc_long r2) { ufc_long v1, v2, x; static ufc_long ary[2]; @@ -632,9 +625,7 @@ ufc_long *_ufc_dofinalperm(l1, l2, r1, r2) * prefixing with the salt */ -static char *output_conversion(v1, v2, salt) - ufc_long v1, v2; - char *salt; +static char *output_conversion(ufc_long v1, ufc_long v2, const char *salt) { static char outbuf[14]; int i, s; @@ -656,13 +647,13 @@ static char *output_conversion(v1, v2, salt) return outbuf; } -ufc_long *_ufc_doit(); - /* * UNIX crypt function */ + +static ufc_long *_ufc_doit(ufc_long , ufc_long, ufc_long, ufc_long, ufc_long); -char *ufc_crypt(char *key,char *salt) +char *ufc_crypt(const char *key,const char *salt) { ufc_long *s; char ktab[9]; @@ -702,8 +693,7 @@ extern long32 _ufc_sb0[], _ufc_sb1[], _ufc_sb2[], _ufc_sb3[]; #define SBA(sb, v) (*(long32*)((char*)(sb)+(v))) -ufc_long *_ufc_doit(l1, l2, r1, r2, itr) - ufc_long l1, l2, r1, r2, itr; +static ufc_long *_ufc_doit(ufc_long l1, ufc_long l2, ufc_long r1, ufc_long r2, ufc_long itr) { int i; long32 s, *k; @@ -742,8 +732,7 @@ extern long64 _ufc_sb0[], _ufc_sb1[], _ufc_sb2[], _ufc_sb3[]; #define SBA(sb, v) (*(long64*)((char*)(sb)+(v))) -ufc_long *_ufc_doit(l1, l2, r1, r2, itr) - ufc_long l1, l2, r1, r2, itr; +static ufc_long *_ufc_doit(ufc_long l1, ufc_long l2, ufc_long r1, ufc_long r2, ufc_long itr) { int i; long64 l, r, s, *k; @@ -777,6 +766,6 @@ ufc_long *_ufc_doit(l1, l2, r1, r2, itr) #else -int ufc_dummy_procedure(void) -{return 0;} + int ufc_dummy_procedure(void); + int ufc_dummy_procedure(void) {return 0;} #endif diff --git a/source3/lib/username.c b/source3/lib/username.c index 3d214fbbda..1504fd6a06 100644 --- a/source3/lib/username.c +++ b/source3/lib/username.c @@ -1,8 +1,8 @@ /* - Unix SMB/Netbios implementation. - Version 1.9. + Unix SMB/CIFS implementation. Username handling - Copyright (C) Andrew Tridgell 1992-1995 + 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 @@ -20,227 +20,667 @@ */ #include "includes.h" -#include "loadparm.h" -extern int DEBUGLEVEL; +/* 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...). +*****************************************************************/ + +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. tries as-is then lower case + Get a users home directory. ****************************************************************************/ -char *get_home_dir(char *user) + +char *get_user_home_dir(const char *user) { - static struct passwd *pass; + static struct passwd *pass; - pass = Get_Pwnam(user,False); + /* Ensure the user exists. */ - if (!pass) return(NULL); - return(pass->pw_dir); + pass = Get_Pwnam(user); + + if (!pass) + return(NULL); + /* Return home directory from struct passwd. */ + + return(pass->pw_dir); } +/**************************************************************************** + Get a users service home directory. +****************************************************************************/ + +char *get_user_service_home_dir(const char *user) +{ + static struct passwd *pass; + int snum; + + /* Ensure the user exists. */ + + pass = Get_Pwnam(user); + + if (!pass) + return(NULL); + + /* If a path is specified in [homes] then use it instead of the + user's home directory from struct passwd. */ + + if ((snum = lp_servicenumber(HOMES_NAME)) != -1) { + static pstring home_dir; + + pstrcpy(home_dir, lp_pathname(snum)); + standard_sub_home(snum, user, home_dir); + + if (home_dir[0]) + return home_dir; + } + + /* Return home directory from struct passwd. */ + + return(pass->pw_dir); +} /******************************************************************* -map a username from a dos name to a unix name by looking in the username -map + Map a username from a dos name to a unix name by looking in the username + map. Note that this modifies the name in place. + This is the main function that should be called *once* on + any incoming or new username - in order to canonicalize the name. + This is being done to de-couple the case conversions from the user mapping + function. Previously, the map_username was being called + every time Get_Pwnam was called. + Returns True if username was changed, false otherwise. ********************************************************************/ -void map_username(char *user) -{ - static int depth=0; - static BOOL initialised=False; - static fstring last_from,last_to; - FILE *f; - char *s; - char *mapfile = lp_username_map(); - if (!*mapfile || depth) return; - - if (!*user) return; - - if (!initialised) { - *last_from = *last_to = 0; - initialised = True; - } - - if (strequal(user,last_to)) return; - - if (strequal(user,last_from)) { - DEBUG(3,("Mapped user %s to %s\n",user,last_to)); - strcpy(user,last_to); - return; - } + +BOOL map_username(char *user) +{ + static BOOL initialised=False; + static fstring last_from,last_to; + XFILE *f; + char *mapfile = lp_username_map(); + char *s; + pstring buf; + BOOL mapped_user = False; + + if (!*user) + return False; + + if (!*mapfile) + return False; + + if (!initialised) { + *last_from = *last_to = 0; + initialised = True; + } + + if (strequal(user,last_to)) + return False; + + if (strequal(user,last_from)) { + DEBUG(3,("Mapped user %s to %s\n",user,last_to)); + fstrcpy(user,last_to); + return True; + } - f = fopen(mapfile,"r"); - if (!f) { - DEBUG(0,("can't open username map %s\n",mapfile)); - return; - } + f = x_fopen(mapfile,O_RDONLY, 0); + if (!f) { + DEBUG(0,("can't open username map %s. Error %s\n",mapfile, strerror(errno) )); + return False; + } + + DEBUG(4,("Scanning username map %s\n",mapfile)); - DEBUG(4,("Scanning username map %s\n",mapfile)); + while((s=fgets_slash(buf,sizeof(buf),f))!=NULL) { + char *unixname = s; + char *dosname = strchr_m(unixname,'='); + char **dosuserlist; + BOOL return_if_mapped = False; - depth++; + if (!dosname) + continue; - for (; (s=fgets_slash(NULL,80,f)); free(s)) { - char *unixname = s; - char *dosname = strchr(unixname,'='); + *dosname++ = 0; + + while (isspace((int)*unixname)) + unixname++; + + if ('!' == *unixname) { + return_if_mapped = True; + unixname++; + while (*unixname && isspace((int)*unixname)) + unixname++; + } + + if (!*unixname || strchr_m("#;",*unixname)) + continue; - if (!dosname) continue; - *dosname++ = 0; + { + int l = strlen(unixname); + while (l && isspace((int)unixname[l-1])) { + unixname[l-1] = 0; + l--; + } + } - while (isspace(*unixname)) unixname++; - if (!*unixname || strchr("#;",*unixname)) continue; + dosuserlist = lp_list_make(dosname); + if (!dosuserlist) { + DEBUG(0,("Unable to build user list\n")); + return False; + } - { - int l = strlen(unixname); - while (l && isspace(unixname[l-1])) { - unixname[l-1] = 0; - l--; - } - } + if (strchr_m(dosname,'*') || user_in_list(user, dosuserlist)) { + DEBUG(3,("Mapped user %s to %s\n",user,unixname)); + mapped_user = True; + fstrcpy(last_from,user); + sscanf(unixname,"%s",user); + fstrcpy(last_to,user); + if(return_if_mapped) { + lp_list_free (&dosuserlist); + x_fclose(f); + return True; + } + } + + lp_list_free (&dosuserlist); + } - if (strchr(dosname,'*') || user_in_list(user,dosname)) { - DEBUG(3,("Mapped user %s to %s\n",user,unixname)); - StrnCpy(last_from,user,sizeof(last_from)-1); - sscanf(unixname,"%s",user); - StrnCpy(last_to,user,sizeof(last_to)-1); - } - } + x_fclose(f); - fclose(f); + /* + * Setup the last_from and last_to as an optimization so + * that we don't scan the file again for the same user. + */ + fstrcpy(last_from,user); + fstrcpy(last_to,user); - depth--; + return mapped_user; } /**************************************************************************** -internals of Get_Pwnam wrapper + * 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(char *s) -{ - struct passwd *ret; - - ret = getpwnam(s); - if (ret) - { -#ifdef GETPWANAM - struct passwd_adjunct *pwret; - pwret = getpwanam(s); - if (pwret) - { - free(ret->pw_passwd); - ret->pw_passwd = pwret->pwa_passwd; + +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 = sys_getpwnam(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 = sys_getpwnam(user); + if(ret) + goto done; } -#endif - } + /* 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 = sys_getpwnam(user2); + if(ret) + goto done; + } - return(ret); -} + /* 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, sys_getpwnam, lp_usernamelevel()); +done: + DEBUG(5,("Get_Pwnam_internals %s find user [%s]!\n",ret ? "did":"didn't", user)); + return ret; +} /**************************************************************************** -a wrapper for getpwnam() that tries with all lower and all upper case -if the initial name fails. Also tried with first letter capitalised -Note that this changes user! + Get_Pwnam wrapper for modification. + NOTE: This can potentially modify 'user'! ****************************************************************************/ -struct passwd *Get_Pwnam(char *user,BOOL allow_change) + +struct passwd *Get_Pwnam_Modify(char *user) { - fstring user2; + fstring user2; + struct passwd *ret; - struct passwd *ret; + fstrcpy(user2, user); - if (!user || !(*user)) - return(NULL); + ret = Get_Pwnam_internals(user, user2); + + /* If caller wants the modified username, ensure they get it */ + fstrcpy(user,user2); - StrnCpy(user2,user,sizeof(user2)-1); + /* We can safely assume ret is NULL if none of the above succeed */ + return(ret); +} - if (!allow_change) { - user = &user2[0]; - } +/**************************************************************************** + 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); - map_username(user); + DEBUG(5,("Finding user %s\n", user)); - ret = _Get_Pwnam(user); - if (ret) return(ret); + ret = Get_Pwnam_internals(user, user2); + + DEBUG(5,("Get_Pwnam %s find user [%s]!\n",ret ? "did":"didn't", user)); - strlower(user); - ret = _Get_Pwnam(user); - if (ret) return(ret); + return ret; +} - strupper(user); - ret = _Get_Pwnam(user); - if (ret) return(ret); +/**************************************************************************** + Check if a user is in a netgroup user list. +****************************************************************************/ - /* try with first letter capitalised */ - if (strlen(user) > 1) - strlower(user+1); - ret = _Get_Pwnam(user); - if (ret) return(ret); +static BOOL user_in_netgroup_list(const char *user, const char *ngname) +{ +#ifdef HAVE_NETGROUP + static 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; + } - if (allow_change) - strcpy(user,user2); + 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")); - return(NULL); + if (innetgr(ngname, NULL, user, mydomain)) + return (True); +#endif /* HAVE_NETGROUP */ + return False; } - /**************************************************************************** -check if a user is in a user list + 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; + BOOL ret = False; + + *winbind_answered = False; + + /* + * 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. + */ + + if ((gid = nametogid(gname)) == (gid_t)-1) { + DEBUG(0,("user_in_winbind_group_list: winbind_lookup_name for group %s failed.\n", + gname )); + goto err; + } + + 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. ****************************************************************************/ -BOOL user_in_list(char *user,char *list) -{ - pstring tok; - char *p=list; - - while (next_token(&p,tok,LIST_SEP)) - { - if (strequal(user,tok)) - return(True); - -#ifdef NETGROUP - if (*tok == '@') - { - static char *mydomain = NULL; - if (mydomain == 0) - yp_get_default_domain(&mydomain); - - DEBUG(5,("looking for user %s of domain %s in netgroup %s\n", - user, mydomain, &tok[1])); - DEBUG(5,("innetgr is %s\n", - innetgr(&tok[1], (char *) 0, user, mydomain) - ? "TRUE" : "FALSE")); - - if (innetgr(&tok[1], (char *)0, user, mydomain)) - return (True); + +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; + + 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. + */ + + if (pass) { + if (strequal(gname,gidtoname(pass->pw_gid))) { + DEBUG(10,("user_in_unix_group_list: group %s is primary group.\n", gname )); + return True; + } + } + + 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); + return(True); + } } -#endif + free_userlist(user_list); + return False; +} -#if HAVE_GETGRNAM - if (*tok == '@') - { - struct group *gptr; - char **member; - struct passwd *pass = Get_Pwnam(user,False); +/**************************************************************************** + Check if a user is in a group list. Ask winbind first, then use UNIX. +****************************************************************************/ + +BOOL user_in_group_list(const char *user, const char *gname) +{ + BOOL winbind_answered = False; + BOOL ret; - if (pass) { - gptr = getgrgid(pass->pw_gid); - if (gptr && strequal(gptr->gr_name,&tok[1])) - return(True); - } + ret = user_in_winbind_group_list(user, gname, &winbind_answered); + if (!winbind_answered) + ret = user_in_unix_group_list(user, gname); - gptr = (struct group *)getgrnam(&tok[1]); + if (ret) + DEBUG(10,("user_in_group_list: user |%s| is in group |%s|\n", user, gname)); + return ret; +} - if (gptr) - { - member = gptr->gr_mem; - while (member && *member) - { - if (strequal(*member,user)) - return(True); - member++; +/**************************************************************************** + Check if a user is in a user list - can check combinations of UNIX + and netgroup lists. +****************************************************************************/ + +BOOL user_in_list(const char *user,char **list) +{ + 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| in group |%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)) + return True; + } else if (**list == '+') { + + if((*(*list +1)) == '&') { + /* + * Search UNIX list followed by netgroup. + */ + if(user_in_group_list(user, *list +2)) + 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)) + 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)) + 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; + } + } + } } - } - } -#endif - } - return(False); + + 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); +} + +/**************************************************************************** + These wrappers allow appliance mode to work. In appliance mode the username + takes the form DOMAIN/user. +****************************************************************************/ + +struct passwd *smb_getpwnam(char *user, BOOL allow_change) +{ + struct passwd *pw; + char *p; + char *sep; + extern pstring global_myname; + + if (allow_change) + pw = Get_Pwnam_Modify(user); + else + pw = Get_Pwnam(user); + + if (pw) + return pw; + + /* + * If it is a domain qualified name and it isn't in our password + * database but the domain portion matches our local machine name then + * lookup just the username portion locally. + */ + + sep = lp_winbind_separator(); + p = strchr_m(user,*sep); + if (p && strncasecmp(global_myname, user, strlen(global_myname))==0) { + if (allow_change) + pw = Get_Pwnam_Modify(p+1); + else + pw = Get_Pwnam(p+1); + } + return NULL; +} diff --git a/source3/lib/util.c b/source3/lib/util.c index 7bd6298c4c..7e2ad49639 100644 --- a/source3/lib/util.c +++ b/source3/lib/util.c @@ -1,8 +1,9 @@ /* - Unix SMB/Netbios implementation. - Version 1.9. + Unix SMB/CIFS implementation. Samba utility functions - Copyright (C) Andrew Tridgell 1992-1995 + Copyright (C) Andrew Tridgell 1992-1998 + Copyright (C) Jeremy Allison 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 @@ -20,41 +21,51 @@ */ #include "includes.h" -#include "loadparm.h" -pstring scope = ""; +#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. + */ -int DEBUGLEVEL = 1; +#if defined(GROUP) +#undef GROUP +#endif -BOOL passive = False; +#if defined(GROUP_OBJ) +#undef GROUP_OBJ +#endif -int Protocol = PROTOCOL_COREPLUS; +#endif /* BROKEN_NISPLUS_INCLUDE_FILES */ -int serverzone=0; +#include <rpcsvc/nis.h> -/* a default finfo structure to ensure all fields are sensible */ -file_info def_finfo = {-1,0,0,0,0,0,0,""}; +#else /* !WITH_NISPLUS_HOME */ -/* these are some file handles where debug info will be stored */ -FILE *dbf = NULL; +#include "rpcsvc/ypclnt.h" -/* the client file descriptor */ -int Client = -1; +#endif /* WITH_NISPLUS_HOME */ +#endif /* HAVE_NETGROUP && WITH_AUTOMOUNT */ -/* info on the client */ -struct from_host Client_info= -{"UNKNOWN","0.0.0.0",NULL}; +#ifdef WITH_SSL +#include <openssl/ssl.h> +#undef Realloc /* SSLeay defines this and samba has a function of this name */ +extern SSL *ssl; +extern int sslFd; +#endif /* WITH_SSL */ -/* the last IP received from */ -struct in_addr lastip; +int Protocol = PROTOCOL_COREPLUS; -/* the last port received from */ -int lastport=0; +/* a default finfo structure to ensure all fields are sensible */ +file_info def_finfo = {-1,0,0,0,0,0,0,"",""}; -/* my IP, the broadcast IP and the Netmask */ -struct in_addr myip; -struct in_addr bcast_ip; -struct in_addr Netmask; +/* this is used by the chaining code */ +int chain_size = 0; int trans_num = 0; @@ -63,13 +74,6 @@ int trans_num = 0; */ int case_default = CASE_LOWER; - -/* size of reads during a direct file to file transfer */ -int ReadSize = 16*1024; - -pstring debugf = "/tmp/log.samba"; -int syslog_level; - /* the following control case operations - they are put here so the client can link easily */ BOOL case_sensitive; @@ -78,916 +82,110 @@ BOOL use_mangled_map = False; BOOL short_case_preserve; BOOL case_mangle; -fstring remote_machine=""; -fstring local_machine=""; -fstring remote_arch="UNKNOWN"; -fstring remote_proto="UNKNOWN"; -pstring myhostname=""; -pstring user_socket_options=""; -pstring sesssetup_user=""; - - -static char *filename_dos(char *path,char *buf); - -static BOOL stdout_logging = False; - - -/******************************************************************* - get ready for syslog stuff - ******************************************************************/ -void setup_logging(char *pname,BOOL interactive) -{ -#ifdef SYSLOG - if (!interactive) { - char *p = strrchr(pname,'/'); - if (p) pname = p+1; - openlog(pname, LOG_PID, LOG_DAEMON); - } -#endif - if (interactive) { - stdout_logging = True; - dbf = stdout; - } -} - - -BOOL append_log=False; - - -/**************************************************************************** -reopen the log files -****************************************************************************/ -void reopen_logs(void) -{ - extern FILE *dbf; - pstring fname; - - if (DEBUGLEVEL > 0) - { - strcpy(fname,debugf); - if (lp_loaded() && (*lp_logfile())) - strcpy(fname,lp_logfile()); - - if (!strcsequal(fname,debugf) || !dbf || !file_exist(debugf,NULL)) - { - strcpy(debugf,fname); - if (dbf) fclose(dbf); - if (append_log) - dbf = fopen(debugf,"a"); - else - dbf = fopen(debugf,"w"); - if (dbf) setbuf(dbf,NULL); - } - } - else - { - if (dbf) - { - fclose(dbf); - dbf = NULL; - } - } -} - - -/******************************************************************* -write an debug message on the debugfile. This is called by the DEBUG -macro -********************************************************************/ -#ifdef __STDC__ -int Debug1(char *format_str, ...) -{ -#else -int Debug1(va_alist) -va_dcl -{ - char *format_str; -#endif - va_list ap; - -#ifdef __STDC__ - va_start(ap, format_str); -#else - va_start(ap); - format_str = va_arg(ap,char *); -#endif - - if (stdout_logging) { - vfprintf(dbf,format_str,ap); - va_end(ap); - return(0); - } +static enum remote_arch_types ra_type = RA_UNKNOWN; +pstring user_socket_options=DEFAULT_SOCKET_OPTIONS; - { - static int debug_count=0; +pstring global_myname = ""; +fstring global_myworkgroup = ""; +char **my_netbios_names; - debug_count++; - if (debug_count == 100) { - int maxlog = lp_max_log_size() * 1024; - if (dbf && maxlog > 0) - { - struct stat st; - - if (fstat(fileno(dbf),&st) == 0 && st.st_size > maxlog) { - fclose(dbf); dbf = NULL; - reopen_logs(); - if (dbf && file_size(debugf) > maxlog) { - pstring name; - fclose(dbf); dbf = NULL; - sprintf(name,"%s.old",debugf); - sys_rename(debugf,name); - reopen_logs(); - } - } - } - debug_count=0; - } - } - -#ifdef SYSLOG - if (!lp_syslog_only()) -#endif - { - if (!dbf) - { - dbf = fopen(debugf,"w"); - if (dbf) - setbuf(dbf,NULL); - else - return(0); - } - } - -#ifdef SYSLOG - if (syslog_level < lp_syslog()) - { - /* - * map debug levels to syslog() priorities - * note that not all DEBUG(0, ...) calls are - * necessarily errors - */ - static int priority_map[] = { - LOG_ERR, /* 0 */ - LOG_WARNING, /* 1 */ - LOG_NOTICE, /* 2 */ - LOG_INFO, /* 3 */ - }; - int priority; - pstring msgbuf; - - if (syslog_level >= sizeof(priority_map) / sizeof(priority_map[0]) || - syslog_level < 0) - priority = LOG_DEBUG; - else - priority = priority_map[syslog_level]; - - vsprintf(msgbuf, format_str, ap); - - msgbuf[255] = '\0'; - syslog(priority, "%s", msgbuf); - } -#endif - -#ifdef SYSLOG - if (!lp_syslog_only()) -#endif - { - vfprintf(dbf,format_str,ap); - fflush(dbf); - } - - va_end(ap); - return(0); -} /**************************************************************************** -routine to do file locking -****************************************************************************/ -BOOL fcntl_lock(int fd,int op,uint32 offset,uint32 count,int type) + find a suitable temporary directory. The result should be copied immediately + as it may be overwritten by a subsequent call. + ****************************************************************************/ +char *tmpdir(void) { -#if HAVE_FCNTL_LOCK - struct flock lock; - int ret; - -#if 1 - uint32 mask = 0xC0000000; - - /* make sure the count is reasonable, we might kill the lockd otherwise */ - count &= ~mask; - - /* the offset is often strange - remove 2 of its bits if either of - the top two bits are set. Shift the top ones by two bits. This - still allows OLE2 apps to operate, but should stop lockd from - dieing */ - if ((offset & mask) != 0) - offset = (offset & ~mask) | ((offset & mask) >> 2); -#else - unsigned long mask = ((unsigned)1<<31); - - /* interpret negative counts as large numbers */ - if (count < 0) - count &= ~mask; - - /* no negative offsets */ - offset &= ~mask; - - /* count + offset must be in range */ - while ((offset < 0 || (offset + count < 0)) && mask) - { - offset &= ~mask; - mask = mask >> 1; - } -#endif - - - DEBUG(5,("fcntl_lock %d %d %d %d %d\n",fd,op,(int)offset,(int)count,type)); - - lock.l_type = type; - lock.l_whence = SEEK_SET; - lock.l_start = (int)offset; - lock.l_len = (int)count; - lock.l_pid = 0; - - errno = 0; - - ret = fcntl(fd,op,&lock); - - if (errno != 0) - DEBUG(3,("fcntl lock gave errno %d (%s)\n",errno,strerror(errno))); - - /* a lock query */ - if (op == F_GETLK) - { - if ((ret != -1) && - (lock.l_type != F_UNLCK) && - (lock.l_pid != 0) && - (lock.l_pid != getpid())) - { - DEBUG(3,("fd %d is locked by pid %d\n",fd,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,("lock failed at offset %d count %d op %d type %d (%s)\n", - offset,count,op,type,strerror(errno))); - - /* perhaps it doesn't support this sort of locking?? */ - if (errno == EINVAL) - { - DEBUG(3,("locking not supported? returning True\n")); - return(True); - } - - return(False); - } - - /* everything went OK */ - DEBUG(5,("Lock call successful\n")); - - return(True); -#else - return(False); -#endif -} - -/******************************************************************* -lock a file - returning a open file descriptor or -1 on failure -The timeout is in seconds. 0 means no timeout -********************************************************************/ -int file_lock(char *name,int timeout) -{ - int fd = open(name,O_RDWR|O_CREAT,0666); - time_t t=0; - if (fd < 0) return(-1); - -#if HAVE_FCNTL_LOCK - if (timeout) t = time(NULL); - while (!timeout || (time(NULL)-t < timeout)) { - if (fcntl_lock(fd,F_SETLK,0,1,F_WRLCK)) return(fd); - msleep(LOCK_RETRY_TIMEOUT); - } - return(-1); -#else - return(fd); -#endif -} - -/******************************************************************* -unlock a file locked by file_lock -********************************************************************/ -void file_unlock(int fd) -{ - if (fd<0) return; -#if HAVE_FCNTL_LOCK - fcntl_lock(fd,F_SETLK,0,1,F_UNLCK); -#endif - close(fd); -} - -/******************************************************************* -a gettimeofday wrapper -********************************************************************/ -void GetTimeOfDay(struct timeval *tval) -{ -#ifdef GETTIMEOFDAY1 - gettimeofday(tval); -#else - gettimeofday(tval,NULL); -#endif -} - -int extra_time_offset = 0; - -static int timediff = 0; - -/******************************************************************* -init the time differences -********************************************************************/ -void TimeInit(void) -{ - struct tm tm_utc,tm_local; - time_t t; - - t = time(NULL); - - tm_utc = *(gmtime(&t)); - tm_local = *(localtime(&t)); - -#ifdef HAVE_GMTOFF - timediff = -tm_local.tm_gmtoff; -#else - timediff = mktime(&tm_utc) - mktime(&tm_local); -#endif - - if (serverzone == 0) { - serverzone = timediff - DSTDiff(t); - DEBUG(4,("Serverzone is %d\n",serverzone)); - } -} - - -/******************************************************************* -return the DST offset for a particular time -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. -********************************************************************/ -int DSTDiff(time_t t) -{ - static struct dst_table {time_t start,end; BOOL is_dst;} *dst_table = NULL; - static int table_size = 0; - int i; - BOOL is_dst = False; - - if (t == 0) t = time(NULL); - -#ifndef NO_ISDST - for (i=0;i<table_size;i++) - if (t >= dst_table[i].start && t <= dst_table[i].end) break; - - if (i<table_size) { - is_dst = dst_table[i].is_dst; - } else { - time_t low,high; - - dst_table = (struct dst_table *)Realloc(dst_table, - sizeof(dst_table[0])*(i+1)); - if (!dst_table) { - table_size = 0; - return(0); - } - - table_size++; - - dst_table[i].is_dst = is_dst = (localtime(&t)->tm_isdst?True:False);; - dst_table[i].start = dst_table[i].end = t; - - /* no entry will cover more than 6 months */ - low = t - 3*30*24*60*60; - high = t + 3*30*24*60*60; - - /* widen the new entry using two bisection searches */ - while (low+60*60 < dst_table[i].start) { - t = low + (dst_table[i].start-low)/2; - if ((localtime(&t)->tm_isdst?True:False) == is_dst) - dst_table[i].start = t; - else - low = t; - } - - while (high-60*60 > dst_table[i].end) { - t = high + (high-dst_table[i].end)/2; - if ((localtime(&t)->tm_isdst?True:False) == is_dst) - dst_table[i].end = t; - else - high = t; - } - -/* - 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].is_dst)); -*/ - } -#endif - - return((is_dst?60*60:0) - (extra_time_offset*60)); -} - -/**************************************************************************** -return the difference between local and GMT time -****************************************************************************/ -int TimeDiff(time_t t) -{ - static BOOL initialised = False; - if (!initialised) {initialised=True; TimeInit();} - return(timediff - DSTDiff(t)); -} - -/**************************************************************************** -try to optimise the localtime call, it can be quite expenive on some machines -timemul is normally LOCAL_TO_GMT, GMT_TO_LOCAL or 0 -****************************************************************************/ -struct tm *LocalTime(time_t *t,int timemul) -{ - time_t t2 = *t; - - if (timemul) - t2 += timemul * TimeDiff(t2); - - return(gmtime(&t2)); -} - - -/**************************************************************************** -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); -} - - -static char *last_ptr=NULL; - -/**************************************************************************** - 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(char **ptr,char *buff,char *sep) -{ - char *s; - BOOL quoted; - - if (!ptr) ptr = &last_ptr; - 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(sep,*s)) s++; - - /* nothing left? */ - if (! *s) return(False); - - /* copy over the token */ - for (quoted = False; *s && (quoted || !strchr(sep,*s)); s++) - { - if (*s == '\"') - quoted = !quoted; - else - *buff++ = *s; - } - - *ptr = (*s) ? s+1 : s; - *buff = 0; - last_ptr = *ptr; - - return(True); + char *p; + if ((p = getenv("TMPDIR"))) + return p; + return "/tmp"; } /**************************************************************************** -Convert list of tokens to array; dependent on above routine. -Uses last_ptr from above - bit of a hack. + Determine whether we are in the specified group. ****************************************************************************/ -char **toktocliplist(int *ctok, char *sep) -{ - char *s=last_ptr; - int ictok=0; - char **ret, **iret; - - if (!sep) sep = " \t\n\r"; - while(*s && strchr(sep,*s)) s++; - - /* nothing left? */ - if (!*s) return(NULL); - - do { - ictok++; - while(*s && (!strchr(sep,*s))) s++; - while(*s && strchr(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; -} - -#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,void *src,int size) +BOOL in_group(gid_t group, gid_t current_gid, int ngroups, gid_t *groups) { - unsigned long d,s; - int i; - if (dest==src || !size) return(dest); + int i; - d = (unsigned long)dest; - s = (unsigned long)src; + if (group == current_gid) + return(True); - if ((d >= (s+size)) || (s >= (d+size))) { - /* no overlap */ - memcpy(dest,src,size); - return(dest); - } + for (i=0;i<ngroups;i++) + if (group == groups[i]) + return(True); - 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); + return(False); } -#endif - /**************************************************************************** -prompte a dptr (to make it recently used) + Like atoi but gets the value up to the separator character. ****************************************************************************/ -void array_promote(char *array,int elsize,int element) -{ - char *p; - if (element == 0) - return; - p = (char *)malloc(elsize); - - if (!p) - { - DEBUG(5,("Ahh! Can't malloc\n")); - return; - } - memcpy(p,array + element * elsize, elsize); - memmove(array + elsize,array,elsize*element); - memcpy(array,p,elsize); - free(p); -} - -enum SOCK_OPT_TYPES {OPT_BOOL,OPT_INT,OPT_ON}; - -struct +char *Atoic(char *p, int *n, char *c) { - char *name; - int level; - int option; - int value; - int opttype; -} 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_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 - {NULL,0,0,0,0}}; - - - -/**************************************************************************** -set user socket options -****************************************************************************/ -void set_socket_options(int fd, char *options) -{ - string tok; - - while (next_token(&options,tok," \t,")) - { - int ret=0,i; - int value = 1; - char *p; - BOOL got_value = False; - - if ((p = strchr(tok,'='))) - { - *p = 0; - value = atoi(p+1); - got_value = True; + if (!isdigit((int)*p)) { + DEBUG(5, ("Atoic: malformed number\n")); + return NULL; } - for (i=0;socket_options[i].name;i++) - if (strequal(socket_options[i].name,tok)) - break; + (*n) = atoi(p); - if (!socket_options[i].name) - { - DEBUG(0,("Unknown socket option %s\n",tok)); - continue; - } + while ((*p) && isdigit((int)*p)) + p++; - switch (socket_options[i].opttype) + if (strchr_m(c, *p) == NULL) { - 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; + DEBUG(5, ("Atoic: no separator characters (%s) not found\n", c)); + return NULL; } - - if (ret != 0) - DEBUG(0,("Failed to set socket option %s\n",tok)); - } -} - - -/**************************************************************************** - close the socket communication -****************************************************************************/ -void close_sockets(void ) -{ - close(Client); - Client = 0; + return p; } -/**************************************************************************** - return the date and time as a string -****************************************************************************/ -char *timestring(void ) -{ - static char TimeBuf[100]; - time_t t; - t = time(NULL); -#ifdef NO_STRFTIME - strcpy(TimeBuf, asctime(LocalTime(&t,GMT_TO_LOCAL))); -#elif defined(CLIX) || defined(CONVEX) - strftime(TimeBuf,100,"%m/%d/%y %I:%M:%S %p",LocalTime(&t,GMT_TO_LOCAL)); -#elif defined(AMPM) - strftime(TimeBuf,100,"%D %r",LocalTime(&t,GMT_TO_LOCAL)); -#elif defined(TZ_TIME) - { - strftime(TimeBuf,100,"%D:%T",LocalTime(&t,0)); - sprintf(TimeBuf+strlen(TimeBuf)," %+03d%02d", - -TimeDiff(t)/(60*60),-(TimeDiff(t)/60)%60); - } -#else - strftime(TimeBuf,100,"%D %T",LocalTime(&t,GMT_TO_LOCAL)); -#endif - return(TimeBuf); -} +/************************************************************************* + Reads a list of numbers. + *************************************************************************/ -/**************************************************************************** -determine whether we are in the specified group -****************************************************************************/ -BOOL in_group(gid_t group, int current_gid, int ngroups, int *groups) +char *get_numlist(char *p, uint32 **num, int *count) { - int i; - - if (group == current_gid) return(True); + int val; - for (i=0;i<ngroups;i++) - if (group == groups[i]) - return(True); - - return(False); -} - -/**************************************************************************** -this is a safer strcpy(), meant to prevent core dumps when nasty things happen -****************************************************************************/ -char *StrCpy(char *dest,char *src) -{ - char *d = dest; + if (num == NULL || count == NULL) + return NULL; -#if AJT - /* I don't want to get lazy with these ... */ - if (!dest || !src) { - DEBUG(0,("ERROR: NULL StrCpy() called!\n")); - ajt_panic(); - } -#endif + (*count) = 0; + (*num ) = NULL; - if (!dest) return(NULL); - if (!src) { - *dest = 0; - return(dest); - } - while ((*d++ = *src++)) ; - return(dest); -} + while ((p = Atoic(p, &val, ":,")) != NULL && (*p) != ':') { + uint32 *tn; + + tn = Realloc((*num), ((*count)+1) * sizeof(uint32)); + if (tn == NULL) + { + SAFE_FREE(*num); + return NULL; + } else + (*num) = tn; + (*num)[(*count)] = val; + (*count)++; + p++; + } -/**************************************************************************** -line strncpy but always null terminates. Make sure there is room! -****************************************************************************/ -char *StrnCpy(char *dest,const char *src,int n) -{ - char *d = dest; - if (!dest) return(NULL); - if (!src) { - *dest = 0; - return(dest); - } - while (n-- && (*d++ = *src++)) ; - *d = 0; - return(dest); + return p; } - /******************************************************************* -copy an IP address from one buffer to another + Check if a file exists - call vfs_file_exist for samba files. ********************************************************************/ -void putip(void *dest,void *src) -{ - memcpy(dest,src,4); -} - -/**************************************************************************** -interpret the weird netbios "name". Return the name type -****************************************************************************/ -static int name_interpret(char *in,char *out) +BOOL file_exist(const char *fname,SMB_STRUCT_STAT *sbuf) { - int ret; - int len = (*in++) / 2; - - *out=0; - - if (len > 30 || len<1) return(0); - - while (len--) - { - if (in[0] < 'A' || in[0] > 'P' || in[1] < 'A' || in[1] > 'P') { - *out = 0; - return(0); - } - *out = ((in[0]-'A')<<4) + (in[1]-'A'); - in += 2; - out++; - } - *out = 0; - ret = out[-1]; - -#ifdef NETBIOS_SCOPE - /* Handle any scope names */ - while(*in) - { - *out++ = '.'; /* Scope names are separated by periods */ - len = *(unsigned char *)in++; - StrnCpy(out, in, len); - out += len; - *out=0; - in += len; - } -#endif - return(ret); -} - -/**************************************************************************** -mangle a name into netbios format -****************************************************************************/ -int name_mangle(char *In,char *Out,char name_type) -{ - fstring name; - char buf[20]; - char *in = (char *)&buf[0]; - char *out = (char *)Out; - char *p, *label; - int i; - - if (In[0] != '*') { - StrnCpy(name,In,sizeof(name)-1); - sprintf(buf,"%-15.15s%c",name,name_type); - } else { - buf[0]='*'; - memset(&buf[1],0,16); - } - - *out++ = 32; - for (i=0;i<16;i++) { - char c = toupper(in[i]); - out[i*2] = (c>>4) + 'A'; - out[i*2+1] = (c & 0xF) + 'A'; - } - out[32]=0; - out += 32; - - label = scope; - while (*label) - { - p = strchr(label, '.'); - if (p == 0) - p = label + strlen(label); - *out++ = p - label; - memcpy(out, label, p - label); - out += p - label; - label += p - label + (*p == '.'); - } - *out = 0; - return(name_len(Out)); -} - - -/******************************************************************* - check if a file exists -********************************************************************/ -BOOL file_exist(char *fname,struct stat *sbuf) -{ - struct stat st; - if (!sbuf) sbuf = &st; + SMB_STRUCT_STAT st; + if (!sbuf) + sbuf = &st; if (sys_stat(fname,sbuf) != 0) return(False); @@ -996,11 +194,12 @@ BOOL file_exist(char *fname,struct stat *sbuf) } /******************************************************************* -check a files mod time + Check a files mod time. ********************************************************************/ -time_t file_modtime(char *fname) + +time_t file_modtime(const char *fname) { - struct stat st; + SMB_STRUCT_STAT st; if (sys_stat(fname,&st) != 0) return(0); @@ -1009,444 +208,99 @@ time_t file_modtime(char *fname) } /******************************************************************* - check if a directory exists + Check if a directory exists. ********************************************************************/ -BOOL directory_exist(char *dname,struct stat *st) + +BOOL directory_exist(char *dname,SMB_STRUCT_STAT *st) { - struct stat st2; + SMB_STRUCT_STAT st2; + BOOL ret; + if (!st) st = &st2; if (sys_stat(dname,st) != 0) return(False); - return(S_ISDIR(st->st_mode)); + ret = S_ISDIR(st->st_mode); + if(!ret) + errno = ENOTDIR; + return ret; } /******************************************************************* returns the size in bytes of the named file ********************************************************************/ -uint32 file_size(char *file_name) +SMB_OFF_T get_file_size(char *file_name) { - struct stat buf; + SMB_STRUCT_STAT buf; buf.st_size = 0; - sys_stat(file_name,&buf); + if(sys_stat(file_name,&buf) != 0) + return (SMB_OFF_T)-1; return(buf.st_size); } -/**************************************************************************** -check if it's a null mtime -****************************************************************************/ -static BOOL null_mtime(time_t mtime) -{ - if (mtime == 0 || mtime == 0xFFFFFFFF) - return(True); - return(False); -} - -/******************************************************************* - create a 16 bit dos packed date -********************************************************************/ -static uint16 make_dos_date1(time_t unixdate,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(time_t unixdate,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; - - t = LocalTime(&unixdate,GMT_TO_LOCAL); - - ret = make_dos_date1(unixdate,t); - ret = ((ret&0xFFFF)<<16) | make_dos_time1(unixdate,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 = 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 += GMT_TO_LOCAL*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(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_wday = 1; - t.tm_yday = 1; - t.tm_isdst = -1; - - /* mktime() also does the local to GMT time conversion for us. XXXXX - Do all unixes do this the same?? */ - 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 = IVAL(date_ptr,0); - if (!null_mtime(t)) - t += LOCAL_TO_GMT*TimeDiff(t); - return(t); -} - /******************************************************************* return a string representing an attribute for a file ********************************************************************/ -char *attrib_string(int mode) +char *attrib_string(uint16 mode) { - static char attrstr[10]; + static fstring attrstr; attrstr[0] = 0; - if (mode & aVOLID) strcat(attrstr,"V"); - if (mode & aDIR) strcat(attrstr,"D"); - if (mode & aARCH) strcat(attrstr,"A"); - if (mode & aHIDDEN) strcat(attrstr,"H"); - if (mode & aSYSTEM) strcat(attrstr,"S"); - if (mode & aRONLY) strcat(attrstr,"R"); + if (mode & aVOLID) fstrcat(attrstr,"V"); + if (mode & aDIR) fstrcat(attrstr,"D"); + if (mode & aARCH) fstrcat(attrstr,"A"); + if (mode & aHIDDEN) fstrcat(attrstr,"H"); + if (mode & aSYSTEM) fstrcat(attrstr,"S"); + if (mode & aRONLY) fstrcat(attrstr,"R"); return(attrstr); } - /******************************************************************* - case insensitive string compararison -********************************************************************/ -int StrCaseCmp(char *s, char *t) -{ - for (; tolower(*s) == tolower(*t); ++s, ++t) - if (!*s) return 0; - - return tolower(*s) - tolower(*t); -} - -/******************************************************************* - case insensitive string compararison, length limited -********************************************************************/ -int StrnCaseCmp(char *s, char *t, int n) -{ - while (n-- && *s && *t) { - if (tolower(*s) != tolower(*t)) return(tolower(*s) - tolower(*t)); - s++; t++; - } - if (n) return(tolower(*s) - tolower(*t)); - - return(0); -} - -/******************************************************************* - compare 2 strings -********************************************************************/ -BOOL strequal(char *s1,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. - ******************************************************************/ -BOOL strnequal(char *s1,char *s2,int 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(char *s1,char *s2) -{ - if (s1 == s2) return(True); - if (!s1 || !s2) return(False); - - return(strcmp(s1,s2)==0); -} - - -/******************************************************************* - convert a string to lower case -********************************************************************/ -void strlower(char *s) -{ - while (*s) - { -#ifdef KANJI - if (is_shift_jis (*s)) { - s += 2; - } else if (is_kana (*s)) { - s++; - } else { - if (isupper(*s)) - *s = tolower(*s); - s++; - } -#else - if (isupper(*s)) - *s = tolower(*s); - s++; -#endif /* KANJI */ - } -} - -/******************************************************************* - convert a string to upper case + show a smb message structure ********************************************************************/ -void strupper(char *s) +void show_msg(char *buf) { - while (*s) - { -#ifdef KANJI - if (is_shift_jis (*s)) { - s += 2; - } else if (is_kana (*s)) { - s++; - } else { - if (islower(*s)) - *s = toupper(*s); - s++; + int i; + int bcc=0; + + if (DEBUGLEVEL < 5) return; + + DEBUG(5,("size=%d\nsmb_com=0x%x\nsmb_rcls=%d\nsmb_reh=%d\nsmb_err=%d\nsmb_flg=%d\nsmb_flg2=%d\n", + smb_len(buf), + (int)CVAL(buf,smb_com), + (int)CVAL(buf,smb_rcls), + (int)CVAL(buf,smb_reh), + (int)SVAL(buf,smb_err), + (int)CVAL(buf,smb_flg), + (int)SVAL(buf,smb_flg2))); + DEBUG(5,("smb_tid=%d\nsmb_pid=%d\nsmb_uid=%d\nsmb_mid=%d\nsmt_wct=%d\n", + (int)SVAL(buf,smb_tid), + (int)SVAL(buf,smb_pid), + (int)SVAL(buf,smb_uid), + (int)SVAL(buf,smb_mid), + (int)CVAL(buf,smb_wct))); + + for (i=0;i<(int)CVAL(buf,smb_wct);i++) + { + DEBUG(5,("smb_vwv[%d]=%d (0x%X)\n",i, + SVAL(buf,smb_vwv+2*i),SVAL(buf,smb_vwv+2*i))); } -#else - if (islower(*s)) - *s = toupper(*s); - s++; -#endif - } -} -/******************************************************************* - convert a string to "normal" form -********************************************************************/ -void strnorm(char *s) -{ - if (case_default == CASE_UPPER) - strupper(s); - else - strlower(s); -} + bcc = (int)SVAL(buf,smb_vwv+2*(CVAL(buf,smb_wct))); -/******************************************************************* -check if a string is in "normal" case -********************************************************************/ -BOOL strisnormal(char *s) -{ - if (case_default == CASE_UPPER) - return(!strhaslower(s)); - - return(!strhasupper(s)); -} + DEBUG(5,("smb_bcc=%d\n",bcc)); + if (DEBUGLEVEL < 10) return; -/**************************************************************************** - string replace -****************************************************************************/ -void string_replace(char *s,char oldc,char newc) -{ - while (*s) - { -#ifdef KANJI - if (is_shift_jis (*s)) { - s += 2; - } else if (is_kana (*s)) { - s++; - } else { - if (oldc == *s) - *s = newc; - s++; + if (DEBUGLEVEL < 50) + { + bcc = MIN(bcc, 512); } -#else - if (oldc == *s) - *s = newc; - s++; -#endif /* KANJI */ - } -} -/**************************************************************************** - make a file into unix format -****************************************************************************/ -void unix_format(char *fname) -{ - pstring namecopy; - string_replace(fname,'\\','/'); -#ifndef KANJI - dos2unix_format(fname, True); -#endif /* KANJI */ - - if (*fname == '/') - { - strcpy(namecopy,fname); - strcpy(fname,"."); - strcat(fname,namecopy); - } -} - -/**************************************************************************** - make a file into dos format -****************************************************************************/ -void dos_format(char *fname) -{ -#ifndef KANJI - unix2dos_format(fname, True); -#endif /* KANJI */ - string_replace(fname,'/','\\'); -} - - -/******************************************************************* - show a smb message structure -********************************************************************/ -void show_msg(char *buf) -{ - int i; - int bcc=0; - if (DEBUGLEVEL < 5) - return; - - DEBUG(5,("size=%d\nsmb_com=0x%x\nsmb_rcls=%d\nsmb_reh=%d\nsmb_err=%d\nsmb_flg=%d\nsmb_flg2=%d\n", - smb_len(buf), - (int)CVAL(buf,smb_com), - (int)CVAL(buf,smb_rcls), - (int)CVAL(buf,smb_reh), - (int)SVAL(buf,smb_err), - (int)CVAL(buf,smb_flg), - (int)SVAL(buf,smb_flg2))); - DEBUG(5,("smb_tid=%d\nsmb_pid=%d\nsmb_uid=%d\nsmb_mid=%d\nsmt_wct=%d\n", - (int)SVAL(buf,smb_tid), - (int)SVAL(buf,smb_pid), - (int)SVAL(buf,smb_uid), - (int)SVAL(buf,smb_mid), - (int)CVAL(buf,smb_wct))); - for (i=0;i<(int)CVAL(buf,smb_wct);i++) - DEBUG(5,("smb_vwv[%d]=%d (0x%X)\n",i, - SVAL(buf,smb_vwv+2*i),SVAL(buf,smb_vwv+2*i))); - bcc = (int)SVAL(buf,smb_vwv+2*(CVAL(buf,smb_wct))); - DEBUG(5,("smb_bcc=%d\n",bcc)); - if (DEBUGLEVEL < 10) - return; - for (i=0;i<MIN(bcc,128);i++) - DEBUG(10,("%X ",CVAL(smb_buf(buf),i))); - DEBUG(10,("\n")); -} - -/******************************************************************* - return the length of an smb packet -********************************************************************/ -int smb_len(char *buf) -{ - return( PVAL(buf,3) | (PVAL(buf,2)<<8) | ((PVAL(buf,1)&1)<<16) ); -} - -/******************************************************************* - set the length of an smb packet -********************************************************************/ -void _smb_setlen(char *buf,int len) -{ - buf[0] = 0; - buf[1] = (len&0x10000)>>16; - buf[2] = (len&0xFF00)>>8; - buf[3] = len&0xFF; + dump_data(10, smb_buf(buf), bcc); } /******************************************************************* @@ -1456,10 +310,10 @@ void smb_setlen(char *buf,int len) { _smb_setlen(buf,len); - CVAL(buf,4) = 0xFF; - CVAL(buf,5) = 'S'; - CVAL(buf,6) = 'M'; - CVAL(buf,7) = 'B'; + SCVAL(buf,4,0xFF); + SCVAL(buf,5,'S'); + SCVAL(buf,6,'M'); + SCVAL(buf,7,'B'); } /******************************************************************* @@ -1467,93 +321,35 @@ void smb_setlen(char *buf,int len) ********************************************************************/ int set_message(char *buf,int num_words,int num_bytes,BOOL zero) { - if (zero) - bzero(buf + smb_size,num_words*2 + num_bytes); - CVAL(buf,smb_wct) = num_words; - SSVAL(buf,smb_vwv + num_words*SIZEOFWORD,num_bytes); - smb_setlen(buf,smb_size + num_words*2 + num_bytes - 4); - return (smb_size + num_words*2 + num_bytes); + if (zero) + memset(buf + smb_size,'\0',num_words*2 + num_bytes); + SCVAL(buf,smb_wct,num_words); + SSVAL(buf,smb_vwv + num_words*SIZEOFWORD,num_bytes); + smb_setlen(buf,smb_size + num_words*2 + num_bytes - 4); + return (smb_size + num_words*2 + num_bytes); } /******************************************************************* -return the number of smb words + setup only the byte count for a smb message ********************************************************************/ -int smb_numwords(char *buf) +int set_message_bcc(char *buf,int num_bytes) { - return (CVAL(buf,smb_wct)); + int num_words = CVAL(buf,smb_wct); + SSVAL(buf,smb_vwv + num_words*SIZEOFWORD,num_bytes); + smb_setlen(buf,smb_size + num_words*2 + num_bytes - 4); + return (smb_size + num_words*2 + num_bytes); } /******************************************************************* -return the size of the smb_buf region of a message + setup only the byte count for a smb message, using the end of the + message as a marker ********************************************************************/ -int smb_buflen(char *buf) +int set_message_end(void *outbuf,void *end_ptr) { - return(SVAL(buf,smb_vwv0 + smb_numwords(buf)*2)); + return set_message_bcc((char *)outbuf,PTR_DIFF(end_ptr,smb_buf((char *)outbuf))); } /******************************************************************* - return a pointer to the smb_buf data area -********************************************************************/ -int smb_buf_ofs(char *buf) -{ - return (smb_size + CVAL(buf,smb_wct)*2); -} - -/******************************************************************* - return a pointer to the smb_buf data area -********************************************************************/ -char *smb_buf(char *buf) -{ - return (buf + smb_buf_ofs(buf)); -} - -/******************************************************************* -return the SMB offset into an SMB buffer -********************************************************************/ -int smb_offset(char *p,char *buf) -{ - return(PTR_DIFF(p,buf+4)); -} - - -/******************************************************************* -skip past some strings in a buffer -********************************************************************/ -char *skip_string(char *buf,int n) -{ - while (n--) - buf += strlen(buf) + 1; - return(buf); -} - -/******************************************************************* -trim the specified elements off the front and back of a string -********************************************************************/ -BOOL trim_string(char *s,char *front,char *back) -{ - BOOL ret = False; - while (front && *front && strncmp(s,front,strlen(front)) == 0) - { - char *p = s; - ret = True; - while (1) - { - if (!(*p = p[strlen(front)])) - break; - p++; - } - } - while (back && *back && strlen(s) >= strlen(back) && - (strncmp(s+strlen(s)-strlen(back),back,strlen(back))==0)) - { - ret = True; - s[strlen(s)-strlen(back)] = 0; - } - return(ret); -} - - -/******************************************************************* reduce a file name, removing .. elements. ********************************************************************/ void dos_clean_name(char *s) @@ -1563,25 +359,25 @@ void dos_clean_name(char *s) DEBUG(3,("dos_clean_name [%s]\n",s)); /* remove any double slashes */ - string_sub(s, "\\\\", "\\"); + all_string_sub(s, "\\\\", "\\", 0); while ((p = strstr(s,"\\..\\")) != NULL) { pstring s1; *p = 0; - strcpy(s1,p+3); + pstrcpy(s1,p+3); - if ((p=strrchr(s,'\\')) != NULL) + if ((p=strrchr_m(s,'\\')) != NULL) *p = 0; else *s = 0; - strcat(s,s1); + pstrcat(s,s1); } trim_string(s,NULL,"\\.."); - string_sub(s, "\\.\\", "\\"); + all_string_sub(s, "\\.\\", "\\", 0); } /******************************************************************* @@ -1594,474 +390,119 @@ void unix_clean_name(char *s) DEBUG(3,("unix_clean_name [%s]\n",s)); /* remove any double slashes */ - string_sub(s, "//","/"); + all_string_sub(s, "//","/", 0); + + /* Remove leading ./ characters */ + if(strncmp(s, "./", 2) == 0) { + trim_string(s, "./", NULL); + if(*s == 0) + pstrcpy(s,"./"); + } while ((p = strstr(s,"/../")) != NULL) { pstring s1; *p = 0; - strcpy(s1,p+3); + pstrcpy(s1,p+3); - if ((p=strrchr(s,'/')) != NULL) + if ((p=strrchr_m(s,'/')) != NULL) *p = 0; else *s = 0; - strcat(s,s1); + pstrcat(s,s1); } trim_string(s,NULL,"/.."); } - /******************************************************************* -a wrapper for the normal chdir() function +convert '\' to '/' +reduce a file name, removing or reducing /../ , /./ , // elements. +remove also any trailing . and / +return a new allocated string. ********************************************************************/ -int ChDir(char *path) -{ - int res; - static pstring LastDir=""; - - if (strcsequal(path,".")) return(0); - - if (*path == '/' && strcsequal(LastDir,path)) return(0); - DEBUG(3,("chdir to %s\n",path)); - res = sys_chdir(path); - if (!res) - strcpy(LastDir,path); - return(res); -} - - -/******************************************************************* - return the absolute current directory path. A dumb version. -********************************************************************/ -static char *Dumb_GetWd(char *s) -{ -#ifdef USE_GETCWD - return ((char *)getcwd(s,sizeof(pstring))); -#else - return ((char *)getwd(s)); -#endif -} - - -/* number of list structures for a caching GetWd function. */ -#define MAX_GETWDCACHE (50) - -struct -{ - ino_t inode; - dev_t dev; - char *text; - BOOL valid; -} ino_list[MAX_GETWDCACHE]; - -BOOL use_getwd_cache=True; - -/******************************************************************* - return the absolute current directory path -********************************************************************/ -char *GetWd(char *str) -{ - pstring s; - static BOOL getwd_cache_init = False; - struct stat st, st2; - int i; - - *s = 0; - - if (!use_getwd_cache) - return(Dumb_GetWd(str)); - - /* init the cache */ - if (!getwd_cache_init) - { - getwd_cache_init = True; - for (i=0;i<MAX_GETWDCACHE;i++) - { - string_init(&ino_list[i].text,""); - ino_list[i].valid = False; +smb_ucs2_t *unix_clean_path(const smb_ucs2_t *s) +{ + smb_ucs2_t *ns; + smb_ucs2_t *p, *r, *t; + + DEBUG(3, ("unix_clean_path\n")); /* [%unicode]\n")); */ + if(!s) return NULL; + + /* convert '\' to '/' */ + ns = strdup_w(s); + if (!ns) return NULL; + unix_format_w(ns); + + /* remove all double slashes */ + p = ns; + ns = all_string_sub_wa(p, "//", "/"); + SAFE_FREE(p); + if (!ns) return NULL; + + /* remove any /./ */ + p = ns; + ns = all_string_sub_wa(p, "/./", "/"); + SAFE_FREE(p); + if (!ns) return NULL; + + /* reduce any /../ */ + t = ns; + while (*t && (r = strstr_wa(t, "/.."))) { + t = &(r[3]); + if (*t == UCS2_CHAR('/') || *t == 0) { + *r = 0; + p = strrchr_w(ns, UCS2_CHAR('/')); + if (!p) p = ns; + if (*t == 0) *p = 0; + else memmove(p, t, (strlen_w(t) + 1) * sizeof(smb_ucs2_t)); + t = p; + } } - } - - /* Get the inode of the current directory, if this doesn't work we're - in trouble :-) */ - - if (stat(".",&st) == -1) - { - DEBUG(0,("Very strange, couldn't stat \".\"\n")); - return(Dumb_GetWd(str)); - } - - - for (i=0; i<MAX_GETWDCACHE; i++) - if (ino_list[i].valid) - { - - /* If we have found an entry with a matching inode and dev number - then find the inode number for the directory in the cached string. - If this agrees with that returned by the stat for the current - directory then all is o.k. (but make sure it is a directory all - the same...) */ - - if (st.st_ino == ino_list[i].inode && - st.st_dev == ino_list[i].dev) - { - if (stat(ino_list[i].text,&st2) == 0) - { - if (st.st_ino == st2.st_ino && - st.st_dev == st2.st_dev && - (st2.st_mode & S_IFMT) == S_IFDIR) - { - strcpy (str, ino_list[i].text); - - /* promote it for future use */ - array_promote((char *)&ino_list[0],sizeof(ino_list[0]),i); - return (str); - } - else - { - /* If the inode is different then something's changed, - scrub the entry and start from scratch. */ - ino_list[i].valid = False; - } - } - } - } + /* remove any leading ./ trailing /. */ + trim_string_wa(ns, "./", "/."); - /* We don't have the information to hand so rely on traditional methods. - The very slow getcwd, which spawns a process on some systems, or the - not quite so bad getwd. */ - - if (!Dumb_GetWd(s)) - { - DEBUG(0,("Getwd failed, errno %d\n",errno)); - return (NULL); - } + /* remove any leading and trailing / */ + trim_string_wa(ns, "/", "/"); - strcpy(str,s); - - DEBUG(5,("GetWd %s, inode %d, dev %x\n",s,(int)st.st_ino,(int)st.st_dev)); - - /* add it to the cache */ - i = MAX_GETWDCACHE - 1; - string_set(&ino_list[i].text,s); - ino_list[i].dev = st.st_dev; - ino_list[i].inode = st.st_ino; - ino_list[i].valid = True; - - /* put it at the top of the list */ - array_promote((char *)&ino_list[0],sizeof(ino_list[0]),i); - - return (str); + return ns; } - - -/******************************************************************* -reduce a file name, removing .. elements and checking that -it is below dir in the heirachy. This uses GetWd() and so must be run -on the system that has the referenced file system. - -widelinks are allowed if widelinks is true -********************************************************************/ -BOOL reduce_name(char *s,char *dir,BOOL widelinks) -{ -#ifndef REDUCE_PATHS - return True; -#else - pstring dir2; - pstring wd; - pstring basename; - pstring newname; - char *p=NULL; - BOOL relative = (*s != '/'); - - *dir2 = *wd = *basename = *newname = 0; - - if (widelinks) - { - unix_clean_name(s); - /* can't have a leading .. */ - if (strncmp(s,"..",2) == 0 && (s[2]==0 || s[2]=='/')) - { - DEBUG(3,("Illegal file name? (%s)\n",s)); - return(False); - } - return(True); - } - - DEBUG(3,("reduce_name [%s] [%s]\n",s,dir)); - - /* remove any double slashes */ - string_sub(s,"//","/"); - - strcpy(basename,s); - p = strrchr(basename,'/'); - - if (!p) - return(True); - - if (!GetWd(wd)) - { - DEBUG(0,("couldn't getwd for %s %s\n",s,dir)); - return(False); - } - - if (ChDir(dir) != 0) - { - DEBUG(0,("couldn't chdir to %s\n",dir)); - return(False); - } - - if (!GetWd(dir2)) - { - DEBUG(0,("couldn't getwd for %s\n",dir)); - ChDir(wd); - return(False); - } - - - if (p && (p != basename)) - { - *p = 0; - if (strcmp(p+1,".")==0) - p[1]=0; - if (strcmp(p+1,"..")==0) - *p = '/'; - } - - if (ChDir(basename) != 0) - { - ChDir(wd); - DEBUG(3,("couldn't chdir for %s %s basename=%s\n",s,dir,basename)); - return(False); - } - - if (!GetWd(newname)) - { - ChDir(wd); - DEBUG(2,("couldn't get wd for %s %s\n",s,dir2)); - return(False); - } - - if (p && (p != basename)) - { - strcat(newname,"/"); - strcat(newname,p+1); - } - - { - int l = strlen(dir2); - if (dir2[l-1] == '/') - l--; - - if (strncmp(newname,dir2,l) != 0) - { - ChDir(wd); - DEBUG(2,("Bad access attempt? s=%s dir=%s newname=%s l=%d\n",s,dir2,newname,l)); - return(False); - } - - if (relative) - { - if (newname[l] == '/') - strcpy(s,newname + l + 1); - else - strcpy(s,newname+l); - } - else - strcpy(s,newname); - } - - ChDir(wd); - - if (strlen(s) == 0) - strcpy(s,"./"); - - DEBUG(3,("reduced to %s\n",s)); - return(True); -#endif -} - -/**************************************************************************** -expand some *s -****************************************************************************/ -static void expand_one(char *Mask,int len) -{ - char *p1; - while ((p1 = strchr(Mask,'*')) != NULL) - { - int lfill = (len+1) - strlen(Mask); - int l1= (p1 - Mask); - pstring tmp; - strcpy(tmp,Mask); - memset(tmp+l1,'?',lfill); - strcpy(tmp + l1 + lfill,Mask + l1 + 1); - strcpy(Mask,tmp); - } -} - -/**************************************************************************** -expand a wildcard expression, replacing *s with ?s -****************************************************************************/ -void expand_mask(char *Mask,BOOL doext) -{ - pstring mbeg,mext; - pstring dirpart; - pstring filepart; - BOOL hasdot = False; - char *p1; - BOOL absolute = (*Mask == '\\'); - - *mbeg = *mext = *dirpart = *filepart = 0; - - /* parse the directory and filename */ - if (strchr(Mask,'\\')) - dirname_dos(Mask,dirpart); - - filename_dos(Mask,filepart); - - strcpy(mbeg,filepart); - if ((p1 = strchr(mbeg,'.')) != NULL) - { - hasdot = True; - *p1 = 0; - p1++; - strcpy(mext,p1); - } - else - { - strcpy(mext,""); - if (strlen(mbeg) > 8) - { - strcpy(mext,mbeg + 8); - mbeg[8] = 0; - } - } - - if (*mbeg == 0) - strcpy(mbeg,"????????"); - if ((*mext == 0) && doext && !hasdot) - strcpy(mext,"???"); - - if (strequal(mbeg,"*") && *mext==0) - strcpy(mext,"*"); - - /* expand *'s */ - expand_one(mbeg,8); - if (*mext) - expand_one(mext,3); - - strcpy(Mask,dirpart); - if (*dirpart || absolute) strcat(Mask,"\\"); - strcat(Mask,mbeg); - strcat(Mask,"."); - strcat(Mask,mext); - - DEBUG(6,("Mask expanded to [%s]\n",Mask)); -} - - -/**************************************************************************** -does a string have any uppercase chars in it? -****************************************************************************/ -BOOL strhasupper(char *s) -{ - while (*s) - { -#ifdef KANJI - if (is_shift_jis (*s)) { - s += 2; - } else if (is_kana (*s)) { - s++; - } else { - if (isupper(*s)) return(True); - s++; - } -#else - if (isupper(*s)) return(True); - s++; -#endif /* KANJI */ - } - return(False); -} - -/**************************************************************************** -does a string have any lowercase chars in it? -****************************************************************************/ -BOOL strhaslower(char *s) -{ - while (*s) - { -#ifdef KANJI - if (is_shift_jis (*s)) { - s += 2; - } else if (is_kana (*s)) { - s++; - } else { - if (islower(*s)) return(True); - s++; - } -#else - if (islower(*s)) return(True); - s++; -#endif /* KANJI */ - } - return(False); -} - -/**************************************************************************** -find the number of chars in a string -****************************************************************************/ -int count_chars(char *s,char c) -{ - int count=0; - while (*s) - { - if (*s == c) - count++; - s++; - } - return(count); -} - - /**************************************************************************** make a dir struct ****************************************************************************/ -void make_dir_struct(char *buf,char *mask,char *fname,unsigned int size,int mode,time_t date) +void make_dir_struct(char *buf,char *mask,char *fname,SMB_OFF_T size,int mode,time_t date) { char *p; pstring mask2; - strcpy(mask2,mask); + pstrcpy(mask2,mask); if ((mode & aDIR) != 0) size = 0; memset(buf+1,' ',11); - if ((p = strchr(mask2,'.')) != NULL) + if ((p = strchr_m(mask2,'.')) != NULL) { *p = 0; - memcpy(buf+1,mask2,MIN(strlen(mask2),8)); - memcpy(buf+9,p+1,MIN(strlen(p+1),3)); + push_ascii(buf+1,mask2,8, 0); + push_ascii(buf+9,p+1,3, 0); *p = '.'; } else - memcpy(buf+1,mask2,MIN(strlen(mask2),11)); + push_ascii(buf+1,mask2,11, 0); - bzero(buf+21,DIR_STRUCT_SIZE-21); - CVAL(buf,21) = mode; + memset(buf+21,'\0',DIR_STRUCT_SIZE-21); + SCVAL(buf,21,mode); put_dos_date(buf,22,date); SSVAL(buf,26,size & 0xFFFF); - SSVAL(buf,28,size >> 16); - StrnCpy(buf+30,fname,12); + SSVAL(buf,28,(size >> 16)&0xFFFF); + push_ascii(buf+30,fname,12, 0); if (!case_sensitive) strupper(buf+30); - DEBUG(8,("put name [%s] into dir struct\n",buf+30)); + DEBUG(8,("put name [%s] from [%s] into dir struct\n",buf+30, fname)); } @@ -2072,12 +513,15 @@ void close_low_fds(void) { int fd; int i; - close(0); close(1); close(2); + close(0); close(1); +#ifndef __INSURE__ + close(2); +#endif /* 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++) { - fd = open("/dev/null",O_RDWR,0); - if (fd < 0) fd = open("/dev/null",O_WRONLY,0); + 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; @@ -2089,48 +533,6 @@ void close_low_fds(void) } } - -/**************************************************************************** -write to a socket -****************************************************************************/ -int write_socket(int fd,char *buf,int len) -{ - int ret=0; - - if (passive) - return(len); - DEBUG(6,("write_socket(%d,%d)\n",fd,len)); - ret = write_data(fd,buf,len); - - DEBUG(6,("write_socket(%d,%d) wrote %d\n",fd,len,ret)); - return(ret); -} - -/**************************************************************************** -read from a socket -****************************************************************************/ -int read_udp_socket(int fd,char *buf,int len) -{ - int ret; - struct sockaddr sock; - int socklen; - - socklen = sizeof(sock); - bzero((char *)&sock,socklen); - bzero((char *)&lastip,sizeof(lastip)); - ret = recvfrom(fd,buf,len,0,&sock,&socklen); - if (ret <= 0) - { - DEBUG(2,("read socket failed. ERRNO=%d\n",errno)); - return(0); - } - - lastip = *(struct in_addr *) &sock.sa_data[2]; - lastport = ntohs(((struct sockaddr_in *)&sock)->sin_port); - - return(ret); -} - /**************************************************************************** Set a fd into blocking/nonblocking mode. Uses POSIX O_NONBLOCK if available, else @@ -2139,7 +541,7 @@ if BSD use FNDELAY ****************************************************************************/ int set_blocking(int fd, BOOL set) { -int val; + int val; #ifdef O_NONBLOCK #define FLAG_TO_SET O_NONBLOCK #else @@ -2150,7 +552,7 @@ int val; #endif #endif - if((val = fcntl(fd, F_GETFL, 0))==-1) + if((val = fcntl(fd, F_GETFL, 0)) == -1) return -1; if(set) /* Turn blocking on - ie. clear nonblock flag */ val &= ~FLAG_TO_SET; @@ -2160,2351 +562,1656 @@ int val; #undef FLAG_TO_SET } - /**************************************************************************** -Calculate the difference in timeout values. Return 1 if val1 > val2, -0 if val1 == val2, -1 if val1 < val2. Stores result in retval. retval -may be == val1 or val2 + Transfer some data between two fd's. ****************************************************************************/ -static int tval_sub( struct timeval *retval, struct timeval *val1, struct timeval *val2) -{ - int usecdiff = val1->tv_usec - val2->tv_usec; - int secdiff = val1->tv_sec - val2->tv_sec; - if(usecdiff < 0) { - usecdiff = 1000000 + usecdiff; - secdiff--; - } - retval->tv_sec = secdiff; - retval->tv_usec = usecdiff; - if(secdiff < 0) - return -1; - if(secdiff > 0) - return 1; - return (usecdiff < 0 ) ? -1 : ((usecdiff > 0 ) ? 1 : 0); -} -/**************************************************************************** -read data from a device with a timout in msec. -mincount = if timeout, minimum to read before returning -maxcount = number to be read. -****************************************************************************/ -int read_with_timeout(int fd,char *buf,int mincnt,int maxcnt,long time_out,BOOL exact) -{ - fd_set fds; - int selrtn; - int readret; - int nread = 0; - struct timeval timeout, tval1, tval2, tvaldiff; - int error_limit = 5; +#ifndef TRANSFER_BUF_SIZE +#define TRANSFER_BUF_SIZE 65536 +#endif - /* just checking .... */ - if (maxcnt <= 0) return(0); +ssize_t transfer_file_internal(int infd, int outfd, size_t n, ssize_t (*read_fn)(int, void *, size_t), + ssize_t (*write_fn)(int, const void *, size_t)) +{ + char *buf; + size_t total = 0; + ssize_t read_ret; + ssize_t write_ret; + size_t num_to_read_thistime; + size_t num_written = 0; - if(time_out == -2) - time_out = DEFAULT_PIPE_TIMEOUT; + if ((buf = malloc(TRANSFER_BUF_SIZE)) == NULL) + return -1; - /* Blocking read */ - if(time_out < 0) { - if (mincnt == 0) mincnt = maxcnt; + while (total < n) { + num_to_read_thistime = MIN((n - total), TRANSFER_BUF_SIZE); - while (nread < mincnt) - { - readret = read(fd, buf + nread, maxcnt - nread); - if (readret <= 0) return(nread); - nread += readret; - } - return(nread); - } - - /* Non blocking read */ - if(time_out == 0) { - set_blocking(fd, False); - nread = read_data(fd, buf, mincnt); - if (nread < maxcnt) - nread += read(fd,buf+nread,maxcnt-nread); - if(nread == -1 && errno == EWOULDBLOCK) - nread = 0; - set_blocking(fd,True); - return nread; - } + read_ret = (*read_fn)(infd, buf, num_to_read_thistime); + if (read_ret == -1) { + DEBUG(0,("transfer_file_internal: read failure. Error = %s\n", strerror(errno) )); + SAFE_FREE(buf); + return -1; + } + if (read_ret == 0) + break; - /* Most difficult - timeout read */ - /* If this is ever called on a disk file and - mincnt is greater then the filesize then - system performance will suffer severely as - select always return true on disk files */ - - /* Set initial timeout */ - timeout.tv_sec = time_out / 1000; - timeout.tv_usec = 1000 * (time_out % 1000); - - /* As most UNIXes don't modify the value of timeout - when they return from select we need to get the timeofday (in usec) - now, and also after the select returns so we know - how much time has elapsed */ - - if (exact) - GetTimeOfDay( &tval1); - nread = 0; /* Number of bytes we have read */ - - for(;;) - { - FD_ZERO(&fds); - FD_SET(fd,&fds); - - selrtn = sys_select(&fds,&timeout); - - /* Check if error */ - if(selrtn == -1) { - errno = EBADF; - return -1; - } - - /* Did we timeout ? */ - if (selrtn == 0) { - if (nread < mincnt) return -1; - break; /* Yes */ - } - - readret = read(fd, buf+nread, maxcnt-nread); - if (readret == 0 && nread < mincnt) { - /* error_limit should not really be needed, but some systems - do strange things ... I don't want to just continue - indefinately in case we get an infinite loop */ - if (error_limit--) continue; - return(-1); - } + num_written = 0; + + while (num_written < read_ret) { + write_ret = (*write_fn)(outfd,buf + num_written, read_ret - num_written); + + if (write_ret == -1) { + DEBUG(0,("transfer_file_internal: write failure. Error = %s\n", strerror(errno) )); + SAFE_FREE(buf); + return -1; + } + if (write_ret == 0) + return (ssize_t)total; + + num_written += (size_t)write_ret; + } - if (readret < 0) { - /* force a particular error number for - portability */ - DEBUG(5,("read gave error %s\n",strerror(errno))); - errno = EBADF; - return -1; - } - - nread += readret; - - /* If we have read more than mincnt then return */ - if (nread >= mincnt) - break; - - /* We need to do another select - but first reduce the - time_out by the amount of time already elapsed - if - this is less than zero then return */ - if (exact) { - GetTimeOfDay(&tval2); - (void)tval_sub( &tvaldiff, &tval2, &tval1); - - if (tval_sub(&timeout, &timeout, &tvaldiff) <= 0) - break; /* We timed out */ - } - - /* Save the time of day as we need to do the select - again (saves a system call) */ - tval1 = tval2; - } + total += (size_t)read_ret; + } - /* Return the number we got */ - return(nread); + SAFE_FREE(buf); + return (ssize_t)total; } -/**************************************************************************** -read data from the client. Maxtime is in milliseconds -****************************************************************************/ -int read_max_udp(int fd,char *buffer,int bufsize,int maxtime) +SMB_OFF_T transfer_file(int infd,int outfd,SMB_OFF_T n) { - fd_set fds; - int selrtn; - int nread; - struct timeval timeout; - - FD_ZERO(&fds); - FD_SET(fd,&fds); - - timeout.tv_sec = maxtime / 1000; - timeout.tv_usec = (maxtime % 1000) * 1000; - - selrtn = sys_select(&fds,maxtime>0?&timeout:NULL); - - if (!FD_ISSET(fd,&fds)) - return 0; - - nread = read_udp_socket(fd, buffer, bufsize); - - /* return the number got */ - return(nread); + return (SMB_OFF_T)transfer_file_internal(infd, outfd, (size_t)n, read, write); } /******************************************************************* -find the difference in milliseconds between two struct timeval -values + Sleep for a specified number of milliseconds. ********************************************************************/ -int TvalDiff(struct timeval *tvalold,struct timeval *tvalnew) -{ - return((tvalnew->tv_sec - tvalold->tv_sec)*1000 + - ((int)tvalnew->tv_usec - (int)tvalold->tv_usec)/1000); -} -/**************************************************************************** -send a keepalive packet (rfc1002) -****************************************************************************/ -BOOL send_keepalive(int client) +void msleep(unsigned int t) { - unsigned char buf[4]; - - buf[0] = 0x85; - buf[1] = buf[2] = buf[3] = 0; + unsigned int tdiff=0; + struct timeval tval,t1,t2; + fd_set fds; - return(write_data(client,(char *)buf,4) == 4); + GetTimeOfDay(&t1); + GetTimeOfDay(&t2); + + while (tdiff < t) { + tval.tv_sec = (t-tdiff)/1000; + tval.tv_usec = 1000*((t-tdiff)%1000); + + /* Never wait for more than 1 sec. */ + if (tval.tv_sec > 1) { + tval.tv_sec = 1; + tval.tv_usec = 0; + } + + FD_ZERO(&fds); + errno = 0; + sys_select_intr(0,&fds,NULL,NULL,&tval); + + GetTimeOfDay(&t2); + if (t2.tv_sec < t1.tv_sec) { + /* Someone adjusted time... */ + t1 = t2; + } + + tdiff = TvalDiff(&t1,&t2); + } } - - /**************************************************************************** - read data from the client, reading exactly N bytes. + Become a daemon, discarding the controlling terminal. ****************************************************************************/ -int read_data(int fd,char *buffer,int N) + +void become_daemon(void) { - int ret; - int total=0; - - while (total < N) - { - ret = read(fd,buffer + total,N - total); + if (sys_fork()) { + _exit(0); + } - /* this is for portability */ - if (ret < 0) - errno = EBADF; + /* 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 */ - if (ret <= 0) - return total; - total += ret; - } - return total; + /* Close fd's 0,1,2. Needed if started by rsh */ + close_low_fds(); } /**************************************************************************** - write data to a fd +put up a yes/no prompt ****************************************************************************/ -int write_data(int fd,char *buffer,int N) +BOOL yesno(char *p) { - int total=0; - int ret; + pstring ans; + printf("%s",p); - while (total < N) - { - ret = write(fd,buffer + total,N - total); + if (!fgets(ans,sizeof(ans)-1,stdin)) + return(False); - if (ret <= 0) - return total; + if (*ans == 'y' || *ans == 'Y') + return(True); - total += ret; - } - return total; + return(False); } - -/* variables used by the read prediction module */ -int rp_fd = -1; -int rp_offset = 0; -int rp_length = 0; -int rp_alloced = 0; -int rp_predict_fd = -1; -int rp_predict_offset = 0; -int rp_predict_length = 0; -int rp_timeout = 5; -time_t rp_time = 0; -char *rp_buffer = NULL; -BOOL predict_skip=False; -time_t smb_last_time=(time_t)0; - /**************************************************************************** -handle read prediction on a file + Expand a pointer to be a particular size. ****************************************************************************/ -int read_predict(int fd,int offset,char *buf,char **ptr,int num) -{ - int ret = 0; - int possible = rp_length - (offset - rp_offset); - - possible = MIN(possible,num); - - /* give data if possible */ - if (fd == rp_fd && - offset >= rp_offset && - possible>0 && - smb_last_time-rp_time < rp_timeout) - { - ret = possible; - if (buf) - memcpy(buf,rp_buffer + (offset-rp_offset),possible); - else - *ptr = rp_buffer + (offset-rp_offset); - DEBUG(5,("read-prediction gave %d bytes of %d\n",ret,num)); - } - if (ret == num) { - predict_skip = True; - } else { - predict_skip = False; +void *Realloc(void *p,size_t size) +{ + void *ret=NULL; - /* prepare the next prediction */ - rp_predict_fd = fd; - rp_predict_offset = offset + num; - rp_predict_length = num; + if (size == 0) { + SAFE_FREE(p); + DEBUG(5,("Realloc asked for 0 bytes\n")); + return NULL; } - if (ret < 0) ret = 0; + 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); } /**************************************************************************** -pre-read some data + Free memory, checks for NULL. +use directly SAFE_FREE() +exist only because we need to pass a function pointer somewhere --SSS ****************************************************************************/ -void do_read_prediction() + +void safe_free(void *p) { - if (predict_skip) return; + SAFE_FREE(p); +} - if (rp_predict_fd == -1) - return; +/**************************************************************************** + Get my own name and IP. +****************************************************************************/ - rp_fd = rp_predict_fd; - rp_offset = rp_predict_offset; - rp_length = 0; +BOOL get_myname(char *my_name) +{ + pstring hostname; - rp_predict_fd = -1; + *hostname = 0; - rp_predict_length = MIN(rp_predict_length,2*ReadSize); - rp_predict_length = MAX(rp_predict_length,1024); - rp_offset = (rp_offset/1024)*1024; - rp_predict_length = (rp_predict_length/1024)*1024; + /* get my host name */ + if (gethostname(hostname, sizeof(hostname)) == -1) { + DEBUG(0,("gethostname failed\n")); + return False; + } - if (rp_predict_length > rp_alloced) - { - rp_buffer = Realloc(rp_buffer,rp_predict_length); - rp_alloced = rp_predict_length; - if (!rp_buffer) - { - DEBUG(0,("can't allocate read-prediction buffer\n")); - rp_predict_fd = -1; - rp_fd = -1; - rp_alloced = 0; - return; - } - } + /* Ensure null termination. */ + hostname[sizeof(hostname)-1] = '\0'; - if (lseek(rp_fd,rp_offset,SEEK_SET) != rp_offset) { - rp_fd = -1; - rp_predict_fd = -1; - return; - } + if (my_name) { + /* split off any parts after an initial . */ + char *p = strchr_m(hostname,'.'); - rp_length = read(rp_fd,rp_buffer,rp_predict_length); - rp_time = time(NULL); - if (rp_length < 0) - rp_length = 0; + if (p) + *p = 0; + + fstrcpy(my_name,hostname); + } + + return(True); } /**************************************************************************** -invalidate read-prediction on a fd + Interpret a protocol description string, with a default. ****************************************************************************/ -void invalidate_read_prediction(int fd) + +int interpret_protocol(char *str,int def) { - if (rp_fd == fd) - rp_fd = -1; - if (rp_predict_fd == fd) - rp_predict_fd = -1; + 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); } - /**************************************************************************** -transfer some data between two fd's + Return true if a string could be a pure IP address. ****************************************************************************/ -int transfer_file(int infd,int outfd,int n,char *header,int headlen,int align) + +BOOL is_ipaddress(const char *str) { - static char *buf=NULL; - char *buf1,*abuf; - static int size = 0; - int total = 0; + BOOL pure_address = True; + int i; + + for (i=0; pure_address && str[i]; i++) + if (!(isdigit((int)str[i]) || str[i] == '.')) + pure_address = False; - DEBUG(4,("transfer_file %d (head=%d) called\n",n,headlen)); + /* Check that a pure number is not misinterpreted as an IP */ + pure_address = pure_address && (strchr_m(str, '.') != NULL); - if ((size < ReadSize) && buf) { - free(buf); - buf = NULL; - } + return pure_address; +} - size = MAX(ReadSize,1024); +/**************************************************************************** +interpret an internet address or name into an IP address in 4 byte form +****************************************************************************/ - while (!buf && size>0) { - buf = (char *)Realloc(buf,size+8); - if (!buf) size /= 2; - } - if (!buf) { - DEBUG(0,("Can't allocate transfer buffer!\n")); - exit(1); - } +uint32 interpret_addr(const char *str) +{ + struct hostent *hp; + uint32 res; - abuf = buf + (align%8); + if (strcmp(str,"0.0.0.0") == 0) return(0); + if (strcmp(str,"255.255.255.255") == 0) return(0xFFFFFFFF); - if (header) - n += headlen; + /* 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); + } - while (n > 0) - { - int s = MIN(n,size); - int ret,ret2=0; - - ret = 0; - - if (header && (headlen >= MIN(s,1024))) { - buf1 = header; - s = headlen; - ret = headlen; - headlen = 0; - header = NULL; - } else { - buf1 = abuf; - } + if (res == (uint32)-1) return(0); - if (header && headlen > 0) - { - ret = MIN(headlen,size); - memcpy(buf1,header,ret); - headlen -= ret; - header += ret; - if (headlen <= 0) header = NULL; - } + return(res); +} - if (s > ret) - ret += read(infd,buf1+ret,s-ret); +/******************************************************************* + a convenient addition to interpret_addr() + ******************************************************************/ +struct in_addr *interpret_addr2(const char *str) +{ + static struct in_addr ret; + uint32 a = interpret_addr(str); + ret.s_addr = a; + return(&ret); +} - if (ret > 0) - { - ret2 = (outfd>=0?write_data(outfd,buf1,ret):ret); - if (ret2 > 0) total += ret2; - /* if we can't write then dump excess data */ - if (ret2 != ret) - transfer_file(infd,-1,n-(ret+headlen),NULL,0,0); - } - if (ret <= 0 || ret2 != ret) - return(total); - n -= ret; - } - return(total); +/******************************************************************* + 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 */ -/**************************************************************************** -read 4 bytes of a smb packet and return the smb length of the packet -possibly store the result in the buffer -****************************************************************************/ -int read_smb_length(int fd,char *inbuf,int timeout) +void zero_ip(struct in_addr *ip) { - char *buffer; - char buf[4]; - int len=0, msg_type; - BOOL ok=False; + static BOOL init; + static struct in_addr ipzero; - if (inbuf) - buffer = inbuf; - else - buffer = buf; + if (!init) { + ipzero = *interpret_addr2("0.0.0.0"); + init = True; + } - while (!ok) - { - if (timeout > 0) - ok = (read_with_timeout(fd,buffer,4,4,timeout,False) == 4); - else - ok = (read_data(fd,buffer,4) == 4); + *ip = ipzero; +} - if (!ok) - { - if (timeout>0) - { - DEBUG(10,("select timeout (%d)\n", timeout)); - return(-1); - } - else - { - DEBUG(6,("couldn't read from client\n")); - exit(1); - } - } +#if (defined(HAVE_NETGROUP) && defined(WITH_AUTOMOUNT)) +/****************************************************************** + Remove any mount options such as -rsize=2048,wsize=2048 etc. + Based on a fix from <Thomas.Hepper@icem.de>. +*******************************************************************/ - len = smb_len(buffer); - msg_type = CVAL(buffer,0); +static void strip_mount_options( pstring *str) +{ + if (**str == '-') + { + char *p = *str; + while(*p && !isspace(*p)) + p++; + while(*p && isspace(*p)) + p++; + if(*p) { + pstring tmp_str; - if (msg_type == 0x85) - { - DEBUG(5,( "Got keepalive packet\n")); - ok = False; - } + pstrcpy(tmp_str, p); + pstrcpy(*str, tmp_str); } - - DEBUG(10,("got smb length of %d\n",len)); - - return(len); + } } +/******************************************************************* + Patch from jkf@soton.ac.uk + Split Luke's automount_server into YP lookup and string splitter + so can easily implement automount_path(). + As we may end up doing both, cache the last YP result. +*******************************************************************/ - -/**************************************************************************** - read an smb from a fd and return it's length -The timeout is in milli seconds -****************************************************************************/ -BOOL receive_smb(int fd,char *buffer,int timeout) +#ifdef WITH_NISPLUS_HOME +char *automount_lookup(const char *user_name) { - int len; - BOOL ok; - - bzero(buffer,smb_size + 100); - - len = read_smb_length(fd,buffer,timeout); - if (len == -1) - return(False); - - if (len > BUFFER_SIZE) + static fstring last_key = ""; + static pstring last_value = ""; + + char *nis_map = (char *)lp_nis_home_map_name(); + + char buffer[NIS_MAXATTRVAL + 1]; + nis_result *result; + nis_object *object; + entry_obj *entry; + + if (strcmp(user_name, last_key)) + { + slprintf(buffer, sizeof(buffer)-1, "[key=%s],%s", user_name, nis_map); + DEBUG(5, ("NIS+ querystring: %s\n", buffer)); + + if (result = nis_list(buffer, FOLLOW_PATH|EXPAND_NAME|HARD_LOOKUP, NULL, NULL)) { - DEBUG(0,("Invalid packet length! (%d bytes)\n",len)); - if (len > BUFFER_SIZE + (SAFETY_MARGIN/2)) - exit(1); + if (result->status != NIS_SUCCESS) + { + DEBUG(3, ("NIS+ query failed: %s\n", nis_sperrno(result->status))); + fstrcpy(last_key, ""); pstrcpy(last_value, ""); + } + else + { + object = result->objects.objects_val; + if (object->zo_data.zo_type == ENTRY_OBJ) + { + entry = &object->zo_data.objdata_u.en_data; + DEBUG(5, ("NIS+ entry type: %s\n", entry->en_type)); + DEBUG(3, ("NIS+ result: %s\n", entry->en_cols.en_cols_val[1].ec_value.ec_value_val)); + + pstrcpy(last_value, entry->en_cols.en_cols_val[1].ec_value.ec_value_val); + pstring_sub(last_value, "&", user_name); + fstrcpy(last_key, user_name); + } + } } + nis_freeresult(result); + } - ok = (read_data(fd,buffer+4,len) == len); + strip_mount_options(&last_value); - if (!ok) - { - close_sockets(); - exit(1); - } - - return(True); + DEBUG(4, ("NIS+ Lookup: %s resulted in %s\n", user_name, last_value)); + return last_value; } +#else /* WITH_NISPLUS_HOME */ +char *automount_lookup(const char *user_name) +{ + static fstring last_key = ""; + static pstring last_value = ""; + int nis_error; /* returned by yp all functions */ + char *nis_result; /* yp_match inits this */ + int nis_result_len; /* and set this */ + char *nis_domain; /* yp_get_default_domain inits this */ + char *nis_map = (char *)lp_nis_home_map_name(); -/**************************************************************************** - send an smb to a fd -****************************************************************************/ -BOOL send_smb(int fd,char *buffer) -{ - int len; - int ret,nwritten=0; - len = smb_len(buffer) + 4; + if ((nis_error = yp_get_default_domain(&nis_domain)) != 0) { + DEBUG(3, ("YP Error: %s\n", yperr_string(nis_error))); + return last_value; + } - while (nwritten < len) - { - ret = write_socket(fd,buffer+nwritten,len - nwritten); - if (ret <= 0) - { - DEBUG(0,("Error writing %d bytes to client. %d. Exiting\n",len,ret)); - close_sockets(); - exit(1); - } - nwritten += ret; + DEBUG(5, ("NIS Domain: %s\n", nis_domain)); + + if (!strcmp(user_name, last_key)) { + nis_result = last_value; + nis_result_len = strlen(last_value); + nis_error = 0; + + } else { + + if ((nis_error = yp_match(nis_domain, nis_map, + user_name, strlen(user_name), + &nis_result, &nis_result_len)) == 0) { + if (!nis_error && nis_result_len >= sizeof(pstring)) { + nis_result_len = sizeof(pstring)-1; + } + fstrcpy(last_key, user_name); + strncpy(last_value, nis_result, nis_result_len); + last_value[nis_result_len] = '\0'; + strip_mount_options(&last_value); + + } else if(nis_error == YPERR_KEY) { + + /* If Key lookup fails user home server is not in nis_map + use default information for server, and home directory */ + last_value[0] = 0; + DEBUG(3, ("YP Key not found: while looking up \"%s\" in map \"%s\"\n", + user_name, nis_map)); + DEBUG(3, ("using defaults for server and home directory\n")); + } else { + DEBUG(3, ("YP Error: \"%s\" while looking up \"%s\" in map \"%s\"\n", + yperr_string(nis_error), user_name, nis_map)); } + } - return True; + DEBUG(4, ("YP Lookup: %s resulted in %s\n", user_name, last_value)); + return last_value; } +#endif /* WITH_NISPLUS_HOME */ +#endif -/**************************************************************************** -find a pointer to a netbios name -****************************************************************************/ -char *name_ptr(char *buf,int ofs) +/******************************************************************* +are two IPs on the same subnet? +********************************************************************/ +BOOL same_net(struct in_addr ip1,struct in_addr ip2,struct in_addr mask) { - unsigned char c = *(unsigned char *)(buf+ofs); + uint32 net1,net2,nmask; - if ((c & 0xC0) == 0xC0) - { - uint16 l; - char p[2]; - memcpy(p,buf+ofs,2); - p[0] &= ~0xC0; - l = RSVAL(p,0); - DEBUG(5,("name ptr to pos %d from %d is %s\n",l,ofs,buf+l)); - return(buf + l); - } - else - return(buf+ofs); -} + nmask = ntohl(mask.s_addr); + net1 = ntohl(ip1.s_addr); + net2 = ntohl(ip2.s_addr); + + return((net1 & nmask) == (net2 & nmask)); +} -/**************************************************************************** -extract a netbios name from a buf -****************************************************************************/ -int name_extract(char *buf,int ofs,char *name) -{ - char *p = name_ptr(buf,ofs); - int d = PTR_DIFF(p,buf+ofs); - strcpy(name,""); - if (d < -50 || d > 50) return(0); - return(name_interpret(p,name)); -} - /**************************************************************************** -return the total storage length of a mangled name +check if a process exists. Does this work on all unixes? ****************************************************************************/ -int name_len(char *s) -{ - char *s0=s; - unsigned char c = *(unsigned char *)s; - if ((c & 0xC0) == 0xC0) - return(2); - while (*s) s += (*s)+1; - return(PTR_DIFF(s,s0)+1); -} -/**************************************************************************** -send a single packet to a port on another machine -****************************************************************************/ -BOOL send_one_packet(char *buf,int len,struct in_addr ip,int port,int type) +BOOL process_exists(pid_t pid) { - BOOL ret; - int out_fd; - struct sockaddr_in sock_out; + /* 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); +} - if (passive) - return(True); - /* create a socket to write to */ - out_fd = socket(AF_INET, type, 0); - if (out_fd == -1) - { - DEBUG(0,("socket failed")); - return False; - } - - /* set the address and port */ - bzero((char *)&sock_out,sizeof(sock_out)); - putip((char *)&sock_out.sin_addr,(char *)&ip); - sock_out.sin_port = htons( port ); - sock_out.sin_family = AF_INET; - - if (DEBUGLEVEL > 0) - DEBUG(3,("sending a packet of len %d to (%s) on port %d of type %s\n", - len,inet_ntoa(ip),port,type==SOCK_DGRAM?"DGRAM":"STREAM")); - - /* send it */ - ret = (sendto(out_fd,buf,len,0,(struct sockaddr *)&sock_out,sizeof(sock_out)) >= 0); +/******************************************************************* + Convert a uid into a user name. +********************************************************************/ - if (!ret) - DEBUG(0,("Packet send to %s(%d) failed ERRNO=%d\n", - inet_ntoa(ip),port,errno)); +char *uidtoname(uid_t uid) +{ + static fstring name; + struct passwd *pass; - close(out_fd); - return(ret); + pass = sys_getpwuid(uid); + if (pass) return(pass->pw_name); + slprintf(name, sizeof(name) - 1, "%d",(int)uid); + return(name); } + /******************************************************************* -sleep for a specified number of milliseconds + Convert a gid into a group name. ********************************************************************/ -void msleep(int t) -{ - int tdiff=0; - struct timeval tval,t1,t2; - fd_set fds; - GetTimeOfDay(&t1); - GetTimeOfDay(&t2); - - while (tdiff < t) { - tval.tv_sec = (t-tdiff)/1000; - tval.tv_usec = 1000*((t-tdiff)%1000); - - FD_ZERO(&fds); - errno = 0; - sys_select(&fds,&tval); +char *gidtoname(gid_t gid) +{ + static fstring name; + struct group *grp; - GetTimeOfDay(&t2); - tdiff = TvalDiff(&t1,&t2); - } + grp = getgrgid(gid); + if (grp) return(grp->gr_name); + slprintf(name,sizeof(name) - 1, "%d",(int)gid); + return(name); } -/**************************************************************************** -check if a string is part of a list -****************************************************************************/ -BOOL in_list(char *s,char *list,BOOL casesensitive) +/******************************************************************* + Convert a user name into a uid. If winbindd is present uses this. +********************************************************************/ + +uid_t nametouid(char *name) { - pstring tok; - char *p=list; + struct passwd *pass; + char *p; + uid_t u; - if (!list) return(False); + u = (uid_t)strtol(name, &p, 0); + if ((p != name) && (*p == '\0')) + return u; - while (next_token(&p,tok,LIST_SEP)) - { - if (casesensitive) { - if (strcmp(tok,s) == 0) - return(True); - } else { - if (StrCaseCmp(tok,s) == 0) - return(True); - } - } - return(False); + pass = getpwnam_alloc(name); + if (pass) { + return(pass->pw_uid); + passwd_free(&pass); + } + return (uid_t)-1; } -/* this is used to prevent lots of mallocs of size 1 */ -static char *null_string = NULL; +/******************************************************************* + Convert a name to a gid_t if possible. Return -1 if not a group. If winbindd + is present does a shortcut lookup... +********************************************************************/ -/**************************************************************************** -set a string value, allocing the space for the string -****************************************************************************/ -BOOL string_init(char **dest,char *src) +gid_t nametogid(const char *name) { - int l; - if (!src) - src = ""; + struct group *grp; + char *p; + gid_t g; - l = strlen(src); + g = (gid_t)strtol(name, &p, 0); + if ((p != name) && (*p == '\0')) + return g; - if (l == 0) - { - if (!null_string) - null_string = (char *)malloc(1); - - *null_string = 0; - *dest = null_string; - } - else - { - *dest = (char *)malloc(l+1); - strcpy(*dest,src); - } - return(True); + grp = getgrnam(name); + if (grp) + return(grp->gr_gid); + return (gid_t)-1; } -/**************************************************************************** -free a string value -****************************************************************************/ -void string_free(char **s) +/******************************************************************* +something really nasty happened - panic! +********************************************************************/ +void smb_panic(char *why) { - if (!s || !(*s)) return; - if (*s == null_string) - *s = NULL; - if (*s) free(*s); - *s = NULL; + char *cmd = lp_panic_action(); + if (cmd && *cmd) { + system(cmd); + } + DEBUG(0,("PANIC: %s\n", why)); + dbgflush(); + abort(); } -/**************************************************************************** -set a string value, allocing the space for the string, and deallocating any -existing space -****************************************************************************/ -BOOL string_set(char **dest,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! +/******************************************************************* +a readdir wrapper which just returns the file name +********************************************************************/ +char *readdirname(DIR *p) +{ + SMB_STRUCT_DIRENT *ptr; + char *dname; -This routine looks for pattern in s and replaces it with -insert. It may do multiple replacements. + if (!p) return(NULL); + + ptr = (SMB_STRUCT_DIRENT *)sys_readdir(p); + if (!ptr) return(NULL); -return True if a substitution was done. -****************************************************************************/ -BOOL string_sub(char *s,char *pattern,char *insert) -{ - BOOL ret = False; - char *p; - int ls,lp,li; + dname = ptr->d_name; - if (!insert || !pattern || !s) return(False); +#ifdef NEXT2 + if (telldir(p) < 0) return(NULL); +#endif - ls = strlen(s); - lp = strlen(pattern); - li = strlen(insert); +#ifdef HAVE_BROKEN_READDIR + /* using /usr/ucb/cc is BAD */ + dname = dname - 2; +#endif - if (!*pattern) return(False); + { + static pstring buf; + int len = NAMLEN(ptr); + memcpy(buf, dname, len); + buf[len] = 0; + dname = buf; + } - while (lp <= ls && (p = strstr(s,pattern))) - { - ret = True; - memmove(p+li,p+lp,ls + 1 - (PTR_DIFF(p,s) + lp)); - memcpy(p,insert,li); - s = p + li; - ls = strlen(s); - } - return(ret); + return(dname); } +/******************************************************************* + Utility function used to decide if the last component + of a path matches a (possibly wildcarded) entry in a namelist. +********************************************************************/ - -/********************************************************* -* Recursive routine that is called by mask_match. -* Does the actual matching. -*********************************************************/ -BOOL do_match(char *str, char *regexp, int case_sig) +BOOL is_in_path(char *name, name_compare_entry *namelist) { + pstring last_component; char *p; - for( p = regexp; *p && *str; ) { - switch(*p) { - case '?': - str++; p++; - break; + DEBUG(8, ("is_in_path: %s\n", name)); - case '*': - /* Look for a character matching - the one after the '*' */ - p++; - if(!*p) - return True; /* Automatic match */ - while(*str) { - while(*str && (case_sig ? (*p != *str) : (toupper(*p)!=toupper(*str)))) - str++; - if(do_match(str,p,case_sig)) - return True; - if(!*str) - return False; - else - str++; - } - return False; - - default: - if(case_sig) { - if(*str != *p) - return False; - } else { - if(toupper(*str) != toupper(*p)) - return False; - } - str++, p++; - break; - } + /* if we have no list it's obviously not in the path */ + if((namelist == NULL ) || ((namelist != NULL) && (namelist[0].name == NULL))) + { + DEBUG(8,("is_in_path: no name list.\n")); + return False; } - if(!*p && !*str) - return True; - if (!*p && str[0] == '.' && str[1] == 0) - return(True); - - if (!*str && *p == '?') + /* Get the last component of the unix name. */ + p = strrchr_m(name, '/'); + strncpy(last_component, p ? ++p : name, sizeof(last_component)-1); + last_component[sizeof(last_component)-1] = '\0'; + + for(; namelist->name != NULL; namelist++) + { + if(namelist->is_wild) { - while (*p == '?') p++; - return(!*p); + if (mask_match(last_component, namelist->name, case_sensitive)) + { + DEBUG(8,("is_in_path: mask match succeeded\n")); + return True; + } } - - if(!*str && (*p == '*' && p[1] == '\0')) - return True; + else + { + if((case_sensitive && (strcmp(last_component, namelist->name) == 0))|| + (!case_sensitive && (StrCaseCmp(last_component, namelist->name) == 0))) + { + DEBUG(8,("is_in_path: match succeeded\n")); + return True; + } + } + } + DEBUG(8,("is_in_path: match not found\n")); + return False; } - -/********************************************************* -* Routine to match a given string with a regexp - uses -* simplified regexp that takes * and ? only. Case can be -* significant or not. -*********************************************************/ -BOOL mask_match(char *str, char *regexp, int case_sig,BOOL trans2) +/******************************************************************* + Strip a '/' separated list into an array of + name_compare_enties structures suitable for + passing to is_in_path(). We do this for + speed so we can pre-parse all the names in the list + and don't do it for each call to is_in_path(). + namelist is modified here and is assumed to be + a copy owned by the caller. + We also check if the entry contains a wildcard to + remove a potentially expensive call to mask_match + if possible. +********************************************************************/ + +void set_namearray(name_compare_entry **ppname_array, char *namelist) { - char *p; - pstring p1, p2; - fstring ebase,eext,sbase,sext; - - BOOL matched; - - /* Make local copies of str and regexp */ - StrnCpy(p1,regexp,sizeof(pstring)-1); - StrnCpy(p2,str,sizeof(pstring)-1); + char *name_end; + char *nameptr = namelist; + int num_entries = 0; + int i; - if (!strchr(p2,'.')) { - strcat(p2,"."); - } + (*ppname_array) = NULL; -/* - if (!strchr(p1,'.')) { - strcat(p1,"."); - } -*/ + if((nameptr == NULL ) || ((nameptr != NULL) && (*nameptr == '\0'))) + return; -#if 0 - if (strchr(p1,'.')) + /* We need to make two passes over the string. The + first to count the number of elements, the second + to split it. + */ + while(*nameptr) { - string_sub(p1,"*.*","*"); - string_sub(p1,".*","*"); - } -#endif - - /* Remove any *? and ** as they are meaningless */ - for(p = p1; *p; p++) - while( *p == '*' && (p[1] == '?' ||p[1] == '*')) - (void)strcpy( &p[1], &p[2]); - - if (strequal(p1,"*")) return(True); + if ( *nameptr == '/' ) + { + /* cope with multiple (useless) /s) */ + nameptr++; + continue; + } + /* find the next / */ + name_end = strchr_m(nameptr, '/'); - DEBUG(5,("mask_match str=<%s> regexp=<%s>, case_sig = %d\n", p2, p1, case_sig)); + /* oops - the last check for a / didn't find one. */ + if (name_end == NULL) + break; - if (trans2) { - strcpy(ebase,p1); - strcpy(sbase,p2); - } else { - if ((p=strrchr(p1,'.'))) { - *p = 0; - strcpy(ebase,p1); - strcpy(eext,p+1); - } else { - strcpy(ebase,p1); - eext[0] = 0; + /* next segment please */ + nameptr = name_end + 1; + num_entries++; } - if (!strequal(p2,".") && !strequal(p2,"..") && (p=strrchr(p2,'.'))) { - *p = 0; - strcpy(sbase,p2); - strcpy(sext,p+1); - } else { - strcpy(sbase,p2); - strcpy(sext,""); - } - } + if(num_entries == 0) + return; - matched = do_match(sbase,ebase,case_sig) && - (trans2 || do_match(sext,eext,case_sig)); + if(( (*ppname_array) = (name_compare_entry *)malloc( + (num_entries + 1) * sizeof(name_compare_entry))) == NULL) + { + DEBUG(0,("set_namearray: malloc fail\n")); + return; + } + + /* Now copy out the names */ + nameptr = namelist; + i = 0; + while(*nameptr) + { + if ( *nameptr == '/' ) + { + /* cope with multiple (useless) /s) */ + nameptr++; + continue; + } + /* find the next / */ + if ((name_end = strchr_m(nameptr, '/')) != NULL) + { + *name_end = 0; + } - DEBUG(5,("mask_match returning %d\n", matched)); + /* oops - the last check for a / didn't find one. */ + if(name_end == NULL) + break; - return matched; -} + (*ppname_array)[i].is_wild = ms_has_wild(nameptr); + if(((*ppname_array)[i].name = strdup(nameptr)) == NULL) + { + DEBUG(0,("set_namearray: malloc fail (1)\n")); + return; + } + /* next segment please */ + nameptr = name_end + 1; + i++; + } + + (*ppname_array)[i].name = NULL; + return; +} /**************************************************************************** -become a daemon, discarding the controlling terminal +routine to free a namearray. ****************************************************************************/ -void become_daemon(void) + +void free_namearray(name_compare_entry *name_array) { -#ifndef NO_FORK_DEBUG - if (fork()) - exit(0); + if(name_array == NULL) + return; - /* detach from the terminal */ -#ifdef USE_SETSID - setsid(); -#else -#ifdef TIOCNOTTY - { - int i = open("/dev/tty", O_RDWR); - if (i >= 0) - { - ioctl(i, (int) TIOCNOTTY, (char *)0); - close(i); - } - } -#endif -#endif -#endif + SAFE_FREE(name_array->name); + SAFE_FREE(name_array); } /**************************************************************************** -calculate the default netmask for an address + Simple routine to do POSIX file locking. Cruft in NFS and 64->32 bit mapping + is dealt with in posix.c ****************************************************************************/ -static void default_netmask(struct in_addr *inm, struct in_addr *iad) -{ - unsigned long ad = ntohl(iad->s_addr); - unsigned long nm; - /* - ** Guess a netmask based on the class of the IP address given. - */ - if ( (ad & 0x80000000) == 0 ) { - /* class A address */ - nm = 0xFF000000; - } else if ( (ad & 0xC0000000) == 0x80000000 ) { - /* class B address */ - nm = 0xFFFF0000; - } else if ( (ad & 0xE0000000) == 0xC0000000 ) { - /* class C address */ - nm = 0xFFFFFF00; - } else { - /* class D or E; netmask doesn't make much sense - guess 4 bits */ - nm = 0xFFFFFFF0; - } - inm->s_addr = htonl(nm); -} -/**************************************************************************** - get the broadcast address for our address -(troyer@saifr00.ateng.az.honeywell.com) -****************************************************************************/ -void get_broadcast(struct in_addr *if_ipaddr, - struct in_addr *if_bcast, - struct in_addr *if_nmask) -{ - BOOL found = False; -#ifndef NO_GET_BROADCAST - int sock = -1; /* AF_INET raw socket desc */ - char buff[1024]; - struct ifreq *ifr=NULL; - int i; +BOOL fcntl_lock(int fd, int op, SMB_OFF_T offset, SMB_OFF_T count, int type) +{ + SMB_STRUCT_FLOCK lock; + int ret; -#if defined(EVEREST) - int n_interfaces; - struct ifconf ifc; - struct ifreq *ifreqs; -#elif defined(USE_IFREQ) - struct ifreq ifreq; - struct strioctl strioctl; - struct ifconf *ifc; -#else - struct ifconf ifc; -#endif -#endif + DEBUG(8,("fcntl_lock %d %d %.0f %.0f %d\n",fd,op,(double)offset,(double)count,type)); - /* get a default netmask and broadcast */ - default_netmask(if_nmask, if_ipaddr); + lock.l_type = type; + lock.l_whence = SEEK_SET; + lock.l_start = offset; + lock.l_len = count; + lock.l_pid = 0; -#ifndef NO_GET_BROADCAST - /* Create a socket to the INET kernel. */ -#if USE_SOCKRAW - if ((sock = socket(AF_INET, SOCK_RAW, PF_INET )) < 0) -#else - if ((sock = socket(AF_INET, SOCK_DGRAM, 0 )) < 0) -#endif - { - DEBUG(0,( "Unable to open socket to get broadcast address\n")); - return; - } - - /* Get a list of the configured interfaces */ -#ifdef EVEREST - /* This is part of SCO Openserver 5: The ioctls are no longer part - if the lower level STREAMS interface glue. They are now real - ioctl calls */ - - if (ioctl(sock, SIOCGIFANUM, &n_interfaces) < 0) { - DEBUG(0,( "SIOCGIFANUM: %s\n", strerror(errno))); - } else { - DEBUG(0,( "number of interfaces returned is: %d\n", n_interfaces)); + errno = 0; - ifc.ifc_len = sizeof(struct ifreq) * n_interfaces; - ifc.ifc_buf = (caddr_t) alloca(ifc.ifc_len); + ret = fcntl(fd,op,&lock); - if (ioctl(sock, SIOCGIFCONF, &ifc) < 0) - DEBUG(0, ( "SIOCGIFCONF: %s\n", strerror(errno))); - else { - ifr = ifc.ifc_req; + if (errno != 0) + DEBUG(3,("fcntl_lock: fcntl lock gave errno %d (%s)\n",errno,strerror(errno))); - for (i = 0; i < n_interfaces; ++i) { - if (if_ipaddr->s_addr == - ((struct sockaddr_in *) &ifr[i].ifr_addr)->sin_addr.s_addr) { - found = True; - break; - } - } - } - } -#elif defined(USE_IFREQ) - ifc = (struct ifconf *)buff; - ifc->ifc_len = BUFSIZ - sizeof(struct ifconf); - strioctl.ic_cmd = SIOCGIFCONF; - strioctl.ic_dp = (char *)ifc; - strioctl.ic_len = sizeof(buff); - if (ioctl(sock, I_STR, &strioctl) < 0) { - DEBUG(0,( "I_STR/SIOCGIFCONF: %s\n", strerror(errno))); - } else { - ifr = (struct ifreq *)ifc->ifc_req; - - /* Loop through interfaces, looking for given IP address */ - for (i = ifc->ifc_len / sizeof(struct ifreq); --i >= 0; ifr++) { - if (if_ipaddr->s_addr == - (*(struct sockaddr_in *) &ifr->ifr_addr).sin_addr.s_addr) { - found = True; - break; - } - } - } -#elif defined(__FreeBSD__) || defined(NETBSD) - ifc.ifc_len = sizeof(buff); - ifc.ifc_buf = buff; - if (ioctl(sock, SIOCGIFCONF, &ifc) < 0) { - DEBUG(0,("SIOCGIFCONF: %s\n", strerror(errno))); - } else { - ifr = ifc.ifc_req; - /* Loop through interfaces, looking for given IP address */ - i = ifc.ifc_len; - while (i > 0) { - if (if_ipaddr->s_addr == - (*(struct sockaddr_in *) &ifr->ifr_addr).sin_addr.s_addr) { - found = True; - break; - } - i -= ifr->ifr_addr.sa_len + IFNAMSIZ; - ifr = (struct ifreq*) ((char*) ifr + ifr->ifr_addr.sa_len + IFNAMSIZ); - } - } -#else - ifc.ifc_len = sizeof(buff); - ifc.ifc_buf = buff; - if (ioctl(sock, SIOCGIFCONF, &ifc) < 0) { - DEBUG(0,("SIOCGIFCONF: %s\n", strerror(errno))); - } else { - ifr = ifc.ifc_req; - - /* Loop through interfaces, looking for given IP address */ - for (i = ifc.ifc_len / sizeof(struct ifreq); --i >= 0; ifr++) { -#ifdef BSDI - if (ioctl(sock, SIOCGIFADDR, ifr) < 0) break; -#endif - if (if_ipaddr->s_addr == - (*(struct sockaddr_in *) &ifr->ifr_addr).sin_addr.s_addr) { - found = True; - break; - } + /* a lock query */ + if (op == SMB_F_GETLK) + { + if ((ret != -1) && + (lock.l_type != F_UNLCK) && + (lock.l_pid != 0) && + (lock.l_pid != sys_getpid())) + { + DEBUG(3,("fcntl_lock: fd %d is locked by pid %d\n",fd,(int)lock.l_pid)); + return(True); } - } -#endif - - if (!found) { - DEBUG(0,("No interface found for address %s\n", inet_ntoa(*if_ipaddr))); - } else { - /* Get the netmask address from the kernel */ -#ifdef USE_IFREQ - ifreq = *ifr; - - strioctl.ic_cmd = SIOCGIFNETMASK; - strioctl.ic_dp = (char *)&ifreq; - strioctl.ic_len = sizeof(struct ifreq); - if (ioctl(sock, I_STR, &strioctl) < 0) - DEBUG(0,("Failed I_STR/SIOCGIFNETMASK: %s\n", strerror(errno))); - else - *if_nmask = ((struct sockaddr_in *)&ifreq.ifr_addr)->sin_addr; -#else - if (ioctl(sock, SIOCGIFNETMASK, ifr) < 0) - DEBUG(0,("SIOCGIFNETMASK failed\n")); - else - *if_nmask = ((struct sockaddr_in *)&ifr->ifr_addr)->sin_addr; -#endif - DEBUG(2,("Netmask for %s = %s\n", ifr->ifr_name, - inet_ntoa(*if_nmask))); + /* it must be not locked or locked by me */ + return(False); } - /* Close up shop */ - (void) close(sock); - -#endif - - /* sanity check on the netmask */ + /* a lock set or unset */ + if (ret == -1) { - unsigned long nm = ntohl(if_nmask->s_addr); - if ((nm >> 24) != 0xFF) { - DEBUG(0,("Impossible netmask %s - using defaults\n",inet_ntoa(*if_nmask))); - default_netmask(if_nmask, if_ipaddr); - } + 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); } - /* derive the broadcast assuming a 1's broadcast, as this is what - all MS operating systems do, we have to comply even if the unix - box is setup differently */ - { - unsigned long ad = ntohl(if_ipaddr->s_addr); - unsigned long nm = ntohl(if_nmask->s_addr); - unsigned long bc = (ad & nm) | (0xffffffff & ~nm); - if_bcast->s_addr = htonl(bc); - } - - DEBUG(2,("Derived broadcast address %s\n", inet_ntoa(*if_bcast))); -} /* get_broadcast */ + /* everything went OK */ + DEBUG(8,("fcntl_lock: Lock call successful\n")); + return(True); +} -/**************************************************************************** -put up a yes/no prompt -****************************************************************************/ -BOOL yesno(char *p) +/******************************************************************* +is the name specified one of my netbios names +returns true is it is equal, false otherwise +********************************************************************/ +BOOL is_myname(char *s) { - pstring ans; - printf("%s",p); - - if (!fgets(ans,sizeof(ans)-1,stdin)) - return(False); - - if (*ans == 'y' || *ans == 'Y') - return(True); + int n; + BOOL ret = False; - return(False); + for (n=0; my_netbios_names[n]; n++) { + if (strequal(my_netbios_names[n], s)) + ret=True; + } + DEBUG(8, ("is_myname(\"%s\") returns %d\n", s, ret)); + return(ret); } -/**************************************************************************** -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,FILE *f) +BOOL is_myname_or_ipaddr(char *s) { - char *s=s2; - int len = 0; - int c; - BOOL start_of_line = True; - - if (feof(f)) - return(NULL); - - if (!s2) - { - maxlen = MIN(maxlen,8); - s = (char *)Realloc(s,maxlen); - } - - if (!s || maxlen < 2) return(NULL); - - *s = 0; + char **ptr; + + /* optimize for the common case */ + if (strequal(s, global_myname)) + return True; - while (len < maxlen-1) - { - c = getc(f); - switch (c) + /* maybe its an IP address? */ + if (is_ipaddress(s)) { - 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) - 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) + struct iface_struct nics[MAX_INTERFACES]; + int i, n; + uint32 ip; + + ip = interpret_addr(s); + if ((ip==0) || (ip==0xffffffff)) + return False; + + n = get_interfaces(nics, MAX_INTERFACES); + for (i=0; i<n; i++) { + if (ip == nics[i].ip.s_addr) + return True; + } + } + + /* check for an alias */ + ptr = lp_netbios_aliases(); + for ( ; *ptr; ptr++ ) { - maxlen *= 2; - s = (char *)Realloc(s,maxlen); - if (!s) return(NULL); + if (StrCaseCmp(s, *ptr) == 0) + return True; } - } - return(s); -} - - - -/**************************************************************************** -set the length of a file from a filedescriptor. -Returns 0 on success, -1 on failure. -****************************************************************************/ -int set_filelen(int fd, long len) -{ -/* According to W. R. Stevens advanced UNIX prog. Pure 4.3 BSD cannot - extend a file with ftruncate. Provide alternate implementation - for this */ - -#if FTRUNCATE_CAN_EXTEND - return ftruncate(fd, len); -#else - struct stat st; - char c = 0; - long currpos = lseek(fd, 0L, SEEK_CUR); - - if(currpos < 0) - return -1; - /* Do an fstat to see if the file is longer than - the requested size (call ftruncate), - or shorter, in which case seek to len - 1 and write 1 - byte of zero */ - if(fstat(fd, &st)<0) - return -1; - -#ifdef S_ISFIFO - if (S_ISFIFO(st.st_mode)) return 0; -#endif + + + /* no match */ + return False; - if(st.st_size == len) - return 0; - if(st.st_size > len) - return ftruncate(fd, len); - - if(lseek(fd, len-1, SEEK_SET) != len -1) - return -1; - if(write(fd, &c, 1)!=1) - return -1; - /* Seek to where we were */ - lseek(fd, currpos, SEEK_SET); - return 0; -#endif } -/**************************************************************************** -return the byte checksum of some data -****************************************************************************/ -int byte_checksum(char *buf,int len) +/******************************************************************* +set the horrid remote_arch string based on an enum. +********************************************************************/ +void set_remote_arch(enum remote_arch_types type) { - unsigned char *p = (unsigned char *)buf; - int ret = 0; - while (len--) - ret += *p++; - return(ret); + extern fstring remote_arch; + ra_type = type; + switch( type ) + { + case RA_WFWG: + fstrcpy(remote_arch, "WfWg"); + return; + case RA_OS2: + fstrcpy(remote_arch, "OS2"); + return; + case RA_WIN95: + fstrcpy(remote_arch, "Win95"); + return; + case RA_WINNT: + fstrcpy(remote_arch, "WinNT"); + return; + case RA_WIN2K: + fstrcpy(remote_arch, "Win2K"); + return; + case RA_SAMBA: + fstrcpy(remote_arch,"Samba"); + return; + default: + ra_type = RA_UNKNOWN; + fstrcpy(remote_arch, "UNKNOWN"); + break; + } } - - -#ifdef HPUX -/**************************************************************************** -this is a version of setbuffer() for those machines that only have setvbuf -****************************************************************************/ -void setbuffer(FILE *f,char *buf,int bufsize) +/******************************************************************* + Get the remote_arch type. +********************************************************************/ +enum remote_arch_types get_remote_arch(void) { - setvbuf(f,buf,_IOFBF,bufsize); + return ra_type; } -#endif -/**************************************************************************** -parse out a directory name from a path name. Assumes dos style filenames. -****************************************************************************/ -char *dirname_dos(char *path,char *buf) +void out_ascii(FILE *f, unsigned char *buf,int len) { - char *p = strrchr(path,'\\'); - - if (!p) - strcpy(buf,path); - else - { - *p = 0; - strcpy(buf,path); - *p = '\\'; - } - - return(buf); + int i; + for (i=0;i<len;i++) + { + fprintf(f, "%c", isprint(buf[i])?buf[i]:'.'); + } } - -/**************************************************************************** -parse out a filename from a path name. Assumes dos style filenames. -****************************************************************************/ -static char *filename_dos(char *path,char *buf) +void out_data(FILE *f,char *buf1,int len, int per_line) { - char *p = strrchr(path,'\\'); - - if (!p) - strcpy(buf,path); - else - strcpy(buf,p+1); + unsigned char *buf = (unsigned char *)buf1; + int i=0; + if (len<=0) + { + return; + } - return(buf); + fprintf(f, "[%03X] ",i); + for (i=0;i<len;) + { + fprintf(f, "%02X ",(int)buf[i]); + i++; + if (i%(per_line/2) == 0) fprintf(f, " "); + if (i%per_line == 0) + { + out_ascii(f,&buf[i-per_line ],per_line/2); fprintf(f, " "); + out_ascii(f,&buf[i-per_line/2],per_line/2); fprintf(f, "\n"); + if (i<len) fprintf(f, "[%03X] ",i); + } + } + if ((i%per_line) != 0) + { + int n; + + n = per_line - (i%per_line); + fprintf(f, " "); + if (n>(per_line/2)) fprintf(f, " "); + while (n--) + { + fprintf(f, " "); + } + n = MIN(per_line/2,i%per_line); + out_ascii(f,&buf[i-(i%per_line)],n); fprintf(f, " "); + n = (i%per_line) - n; + if (n>0) out_ascii(f,&buf[i-n],n); + fprintf(f, "\n"); + } } - - -/**************************************************************************** -expand a pointer to be a particular size -****************************************************************************/ -void *Realloc(void *p,int size) +void print_asc(int level, const unsigned char *buf,int len) { - void *ret=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",size)); - - return(ret); + int i; + for (i=0;i<len;i++) + DEBUG(level,("%c", isprint(buf[i])?buf[i]:'.')); } -/**************************************************************************** -set the time on a file -****************************************************************************/ -BOOL set_filetime(char *fname,time_t mtime) -{ - struct utimbuf times; - - if (null_mtime(mtime)) return(True); - - times.modtime = times.actime = mtime; +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 (sys_utime(fname,×)) { - DEBUG(4,("set_filetime(%s) failed: %s\n",fname,strerror(errno))); + DEBUG(level,("[%03X] ",i)); + for (i=0;i<len;) { + DEBUG(level,("%02X ",(int)buf[i])); + i++; + if (i%8 == 0) DEBUG(level,(" ")); + if (i%16 == 0) { + print_asc(level,&buf[i-16],8); DEBUG(level,(" ")); + print_asc(level,&buf[i-8],8); DEBUG(level,("\n")); + if (i<len) DEBUG(level,("[%03X] ",i)); + } + } + if (i%16) { + int n; + + n = 16 - (i%16); + DEBUG(level,(" ")); + if (n>8) DEBUG(level,(" ")); + while (n--) DEBUG(level,(" ")); + + n = MIN(8,i%16); + print_asc(level,&buf[i-(i%16)],n); DEBUG(level,(" ")); + n = (i%16) - n; + if (n>0) print_asc(level,&buf[i-n],n); + DEBUG(level,("\n")); } - - return(True); } - -#ifdef NOSTRDUP -/**************************************************************************** -duplicate a string -****************************************************************************/ -char *strdup(char *s) +char *tab_depth(int depth) { - char *ret = NULL; - if (!s) return(NULL); - ret = (char *)malloc(strlen(s)+1); - if (!ret) return(NULL); - strcpy(ret,s); - return(ret); + static pstring spaces; + memset(spaces, ' ', depth * 4); + spaces[depth * 4] = 0; + return spaces; } -#endif - -/**************************************************************************** - Signal handler for SIGPIPE (write on a disconnected socket) -****************************************************************************/ -void Abort(void ) +/***************************************************************************** + * Provide a checksum on a string + * + * Input: s - the null-terminated character string for which the checksum + * will be calculated. + * + * Output: The checksum value calculated for s. + * + * **************************************************************************** + */ +int str_checksum(const char *s) { - DEBUG(0,("Probably got SIGPIPE\nExiting\n")); - exit(2); -} - + int res = 0; + int c; + int i=0; + + while(*s) { + c = *s; + res ^= (c << (i % 15)) ^ (c >> (15-(i%15))); + s++; + i++; + } + return(res); +} /* str_checksum */ -#ifdef REPLACE_STRLEN -/**************************************************************************** -a replacement strlen() that returns int for solaris -****************************************************************************/ -int Strlen(char *s) -{ - int ret=0; - if (!s) return(0); - while (*s++) ret++; - return(ret); -} -#endif -/**************************************************************************** -return a time at the start of the current month -****************************************************************************/ -time_t start_of_month(void) +/***************************************************************** +zero a memory area then free it. Used to catch bugs faster +*****************************************************************/ +void zero_free(void *p, size_t size) { - time_t t = time(NULL); - struct tm *t2; - - t2 = gmtime(&t); - - t2->tm_mday = 1; - t2->tm_hour = 0; - t2->tm_min = 0; - t2->tm_sec = 0; - - return(mktime(t2)); + memset(p, 0, size); + SAFE_FREE(p); } -/******************************************************************* - check for a sane unix date -********************************************************************/ -BOOL sane_unix_date(time_t unixdate) +/***************************************************************** +set our open file limit to a requested max and return the limit +*****************************************************************/ +int set_maxfiles(int requested_max) { - struct tm t,today; - time_t t_today = time(NULL); - - t = *(LocalTime(&unixdate,LOCAL_TO_GMT)); - today = *(LocalTime(&t_today,LOCAL_TO_GMT)); - - if (t.tm_year < 80) - return(False); - - if (t.tm_year > today.tm_year) - return(False); - - if (t.tm_year == today.tm_year && - t.tm_mon > today.tm_mon) - return(False); - - - if (t.tm_year == today.tm_year && - t.tm_mon == today.tm_mon && - t.tm_mday > (today.tm_mday+1)) - return(False); - - return(True); -} +#if (defined(HAVE_GETRLIMIT) && defined(RLIMIT_NOFILE)) + struct rlimit rlp; + int saved_current_limit; + if(getrlimit(RLIMIT_NOFILE, &rlp)) { + DEBUG(0,("set_maxfiles: getrlimit (1) for RLIMIT_NOFILE failed with error %s\n", + strerror(errno) )); + /* just guess... */ + return requested_max; + } + /* + * Set the fd limit to be real_max_open_files + MAX_OPEN_FUDGEFACTOR to + * account for the extra fd we need + * as well as the log files and standard + * handles etc. Save the limit we want to set in case + * we are running on an OS that doesn't support this limit (AIX) + * which always returns RLIM_INFINITY for rlp.rlim_max. + */ -#ifdef NO_FTRUNCATE - /******************************************************************* -ftruncate for operating systems that don't have it -********************************************************************/ -int ftruncate(int f,long l) -{ - struct flock fl; + /* Try raising the hard (max) limit to the requested amount. */ - fl.l_whence = 0; - fl.l_len = 0; - fl.l_start = l; - fl.l_type = F_WRLCK; - return fcntl(f, F_FREESP, &fl); -} -#endif +#if defined(RLIM_INFINITY) + if (rlp.rlim_max != RLIM_INFINITY) { + int orig_max = rlp.rlim_max; + if ( rlp.rlim_max < requested_max ) + rlp.rlim_max = requested_max; + /* This failing is not an error - many systems (Linux) don't + support our default request of 10,000 open files. JRA. */ -/**************************************************************************** -get my own name and IP -****************************************************************************/ -BOOL get_myname(char *myname,struct in_addr *ip) -{ - struct hostent *hp; - pstring hostname; + if(setrlimit(RLIMIT_NOFILE, &rlp)) { + DEBUG(3,("set_maxfiles: setrlimit for RLIMIT_NOFILE for %d max files failed with error %s\n", + (int)rlp.rlim_max, strerror(errno) )); - *hostname = 0; + /* Set failed - restore original value from get. */ + rlp.rlim_max = orig_max; + } + } +#endif - /* get my host name */ - if (gethostname(hostname, MAXHOSTNAMELEN) == -1) - { - DEBUG(0,("gethostname failed\n")); - return False; - } + /* Now try setting the soft (current) limit. */ - /* get host info */ - if ((hp = Get_Hostbyname(hostname)) == 0) - { - DEBUG(0,( "Get_Hostbyname: Unknown host %s.\n",hostname)); - return False; - } + saved_current_limit = rlp.rlim_cur = MIN(requested_max,rlp.rlim_max); - if (myname) - { - /* split off any parts after an initial . */ - char *p = strchr(hostname,'.'); - if (p) *p = 0; + if(setrlimit(RLIMIT_NOFILE, &rlp)) { + DEBUG(0,("set_maxfiles: setrlimit for RLIMIT_NOFILE for %d files failed with error %s\n", + (int)rlp.rlim_cur, strerror(errno) )); + /* just guess... */ + return saved_current_limit; + } - strcpy(myname,hostname); + if(getrlimit(RLIMIT_NOFILE, &rlp)) { + DEBUG(0,("set_maxfiles: getrlimit (2) for RLIMIT_NOFILE failed with error %s\n", + strerror(errno) )); + /* just guess... */ + return saved_current_limit; } - if (ip) - putip((char *)ip,(char *)hp->h_addr); - - return(True); -} +#if defined(RLIM_INFINITY) + if(rlp.rlim_cur == RLIM_INFINITY) + return saved_current_limit; +#endif + if((int)rlp.rlim_cur > saved_current_limit) + return saved_current_limit; -/**************************************************************************** -true if two IP addresses are equal -****************************************************************************/ -BOOL ip_equal(struct in_addr ip1,struct in_addr ip2) -{ - unsigned long a1,a2; - a1 = ntohl(ip1.s_addr); - a2 = ntohl(ip2.s_addr); - return(a1 == a2); + return rlp.rlim_cur; +#else /* !defined(HAVE_GETRLIMIT) || !defined(RLIMIT_NOFILE) */ + /* + * No way to know - just guess... + */ + return requested_max; +#endif } - -/**************************************************************************** -open a socket of the specified type, port and address for incoming data -****************************************************************************/ -int open_socket_in(int type, int port, int dlevel) +/***************************************************************** + splits out the start of the key (HKLM or HKU) and the rest of the key + *****************************************************************/ +BOOL reg_split_key(char *full_keyname, uint32 *reg_type, char *key_name) { - struct hostent *hp; - struct sockaddr_in sock; - pstring host_name; - int res; - - /* get my host name */ -#ifdef MAXHOSTNAMELEN - if (gethostname(host_name, MAXHOSTNAMELEN) == -1) -#else - if (gethostname(host_name, sizeof(host_name)) == -1) -#endif - { DEBUG(0,("gethostname failed\n")); return -1; } + pstring tmp; - /* get host info */ - if ((hp = Get_Hostbyname(host_name)) == 0) - { - DEBUG(0,( "Get_Hostbyname: Unknown host. %s\n",host_name)); - return -1; - } - - bzero((char *)&sock,sizeof(sock)); - memcpy((char *)&sock.sin_addr,(char *)hp->h_addr, hp->h_length); -#if defined(__FreeBSD__) || defined(NETBSD) /* XXX not the right ifdef */ - sock.sin_len = sizeof(sock); -#endif - sock.sin_port = htons( port ); - sock.sin_family = hp->h_addrtype; - sock.sin_addr.s_addr = INADDR_ANY; - res = socket(hp->h_addrtype, type, 0); - if (res == -1) - { DEBUG(0,("socket failed\n")); return -1; } - - { - int one=1; - setsockopt(res,SOL_SOCKET,SO_REUSEADDR,(char *)&one,sizeof(one)); - } + if (!next_token(&full_keyname, tmp, "\\", sizeof(tmp))) + { + return False; + } - /* now we've got a socket - we need to bind it */ - if (bind(res, (struct sockaddr * ) &sock,sizeof(sock)) < 0) - { - if (port) { - if (port == 139 || port == 137) - DEBUG(dlevel,("bind failed on port %d (%s)\n", - port,strerror(errno))); - close(res); + (*reg_type) = 0; - if (dlevel > 0 && port < 1000) - port = 7999; + DEBUG(10, ("reg_split_key: hive %s\n", tmp)); - if (port >= 1000 && port < 9000) - return(open_socket_in(type,port+1,dlevel)); - } + if (strequal(tmp, "HKLM") || strequal(tmp, "HKEY_LOCAL_MACHINE")) + { + (*reg_type) = HKEY_LOCAL_MACHINE; + } + else if (strequal(tmp, "HKU") || strequal(tmp, "HKEY_USERS")) + { + (*reg_type) = HKEY_USERS; + } + else + { + DEBUG(10,("reg_split_key: unrecognised hive key %s\n", tmp)); + return False; + } + + if (next_token(&full_keyname, tmp, "\n\r", sizeof(tmp))) + { + fstrcpy(key_name, tmp); + } + else + { + key_name[0] = 0; + } - return(-1); - } - DEBUG(3,("bind succeeded on port %d\n",port)); + DEBUG(10, ("reg_split_key: name %s\n", key_name)); - return res; + return True; } -/**************************************************************************** - create an outgoing socket - **************************************************************************/ -int open_socket_out(int type, struct in_addr *addr, int port ) +/***************************************************************** +possibly replace mkstemp if it is broken +*****************************************************************/ +int smb_mkstemp(char *template) { - struct sockaddr_in sock_out; - int res; - - /* 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); - - bzero((char *)&sock_out,sizeof(sock_out)); - putip((char *)&sock_out.sin_addr,(char *)addr); - - sock_out.sin_port = htons( port ); - sock_out.sin_family = PF_INET; - - DEBUG(3,("Connecting to %s at port %d\n",inet_ntoa(*addr),port)); - - /* and connect it to the destination */ - if (connect(res,(struct sockaddr *)&sock_out,sizeof(sock_out))<0) { - DEBUG(0,("connect error: %s\n",strerror(errno))); - close(res); - return(-1); - } - - return res; +#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 } -/**************************************************************************** -interpret a protocol description string, with a default -****************************************************************************/ -int interpret_protocol(char *str,int def) +/** + malloc that aborts with smb_panic on fail or zero size. +**/ +void *smb_xmalloc(size_t size) { - 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); + 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; } -/**************************************************************************** -interpret a security level -****************************************************************************/ -int interpret_security(char *str,int def) +/** + Memdup with smb_panic on fail. +**/ +void *smb_xmemdup(const void *p, size_t size) { - if (strequal(str,"SERVER")) - return(SEC_SERVER); - if (strequal(str,"USER")) - return(SEC_USER); - if (strequal(str,"SHARE")) - return(SEC_SHARE); - - DEBUG(0,("Unrecognised security level %s\n",str)); - - return(def); + void *p2; + p2 = smb_xmalloc(size); + memcpy(p2, p, size); + return p2; } - -/**************************************************************************** -interpret an internet address or name into an IP address in 4 byte form -****************************************************************************/ -unsigned long interpret_addr(char *str) +/** + strdup that aborts on malloc fail. +**/ +char *smb_xstrdup(const char *s) { - struct hostent *hp; - unsigned long 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 (isdigit(str[0])) { - res = inet_addr(str); - } else { - /* otherwise assume it's a network name of some sort and use Get_Hostbyname */ - if ((hp = Get_Hostbyname(str)) == 0) { - DEBUG(3,("Get_Hostbyname: Unknown host. %s\n",str)); - return 0; - } - putip((char *)&res,(char *)hp->h_addr); - } - - if (res == (unsigned long)-1) return(0); - - return(res); + char *s1 = strdup(s); + if (!s1) + smb_panic("smb_xstrdup: malloc fail\n"); + return s1; } -/******************************************************************* - a convenient addition to interpret_addr() - ******************************************************************/ -struct in_addr *interpret_addr2(char *str) +/* + vasprintf that aborts on malloc fail +*/ +int smb_xvasprintf(char **ptr, const char *format, va_list ap) { - static struct in_addr ret; - unsigned long a = interpret_addr(str); - putip((char *)&ret,(char *)&a); - return(&ret); + int n; + n = vasprintf(ptr, format, ap); + if (n == -1 || ! *ptr) { + smb_panic("smb_xvasprintf: out of memory"); + } + return n; } -/******************************************************************* - check if an IP is the 0.0.0.0 - ******************************************************************/ -BOOL zero_ip(struct in_addr ip) +/***************************************************************** +like strdup but for memory + *****************************************************************/ +void *memdup(const void *p, size_t size) { - unsigned long a; - putip((char *)&a,(char *)&ip); - return(a == 0); + void *p2; + if (size == 0) return NULL; + p2 = malloc(size); + if (!p2) return NULL; + memcpy(p2, p, size); + return p2; } -#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 interpret_long_date(char *p) +/***************************************************************** +get local hostname and cache result + *****************************************************************/ +char *myhostname(void) { - double d; - time_t ret; - uint32 tlow,thigh; - tlow = IVAL(p,0); - thigh = IVAL(p,4); - - if (thigh == 0) return(0); - - d = ((double)thigh)*4.0*(double)(1<<30); - d += (tlow&0xFFF00000); - d *= 1.0e-7; - - /* now adjust by 369 years to make the secs since 1970 */ - d -= TIME_FIXUP_CONSTANT; - - if (d>=MAXINT) - return(0); - - ret = (time_t)(d+0.5); - - /* this takes us from kludge-GMT to real GMT */ - ret += TimeDiff(ret) - serverzone; - - return(ret); + static pstring ret; + if (ret[0] == 0) { + get_myname(ret); + } + return ret; } -/**************************************************************************** -put a 8 byte filetime from a time_t -This takes real GMT as input and converts to kludge-GMT -****************************************************************************/ -void put_long_date(char *p,time_t t) +/***************************************************************** +a useful function for returning a path in the Samba lock directory + *****************************************************************/ +char *lock_path(char *name) { - uint32 tlow,thigh; - double d; - - if (t==0) { - SIVAL(p,0,0); SIVAL(p,4,0); - return; - } - - /* this converts GMT to kludge-GMT */ - t -= TimeDiff(t) - serverzone; - - d = (double) (t); + static pstring fname; - d += TIME_FIXUP_CONSTANT; + pstrcpy(fname,lp_lockdir()); + trim_string(fname,"","/"); + + if (!directory_exist(fname,NULL)) { + mkdir(fname,0755); + } + + pstrcat(fname,"/"); + pstrcat(fname,name); - d *= 1.0e7; + return fname; +} - thigh = (uint32)(d * (1.0/(4.0*(double)(1<<30)))); - tlow = (uint32)(d - ((double)thigh)*4.0*(double)(1<<30)); - SIVAL(p,0,tlow); - SIVAL(p,4,thigh); +/** + * @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 static #pstring containing the full path. + **/ +char *lib_path(char *name) +{ + static pstring fname; + snprintf(fname, sizeof(fname), "%s/%s", dyn_LIBDIR, name); + return fname; } /******************************************************************* -sub strings with useful parameters + Given a filename - get its directory name + NB: Returned in static storage. Caveats: + o Not safe in thread environment. + o Caller must not free. + o If caller wishes to preserve, they should copy. ********************************************************************/ -void standard_sub_basic(char *s) -{ - if (!strchr(s,'%')) return; - - string_sub(s,"%R",remote_proto); - string_sub(s,"%a",remote_arch); - string_sub(s,"%m",remote_machine); - string_sub(s,"%L",local_machine); - if (!strchr(s,'%')) return; - - string_sub(s,"%v",VERSION); - string_sub(s,"%h",myhostname); - string_sub(s,"%U",sesssetup_user); - - if (!strchr(s,'%')) return; - - string_sub(s,"%I",Client_info.addr); - string_sub(s,"%M",Client_info.name); - string_sub(s,"%T",timestring()); - - if (!strchr(s,'%')) return; - - { - char pidstr[10]; - sprintf(pidstr,"%d",(int)getpid()); - string_sub(s,"%d",pidstr); - } +char *parent_dirname(const char *path) +{ + static pstring dirpath; + char *p; - if (!strchr(s,'%')) return; + if (!path) + return(NULL); - { - struct passwd *pass = Get_Pwnam(sesssetup_user,False); - if (pass) { - string_sub(s,"%G",gidtoname(pass->pw_gid)); - } - } + pstrcpy(dirpath, path); + p = strrchr_m(dirpath, '/'); /* Find final '/', if any */ + if (!p) { + pstrcpy(dirpath, "."); /* No final "/", so dir is "." */ + } else { + if (p == dirpath) + ++p; /* For root "/", leave "/" in place */ + *p = '\0'; + } + return dirpath; } /******************************************************************* -write a string in unicoode format -********************************************************************/ -int PutUniCode(char *dst,char *src) +determine if a pattern contains any Microsoft wildcard characters + *******************************************************************/ +BOOL ms_has_wild(char *s) { - int ret = 0; - while (*src) { - dst[ret++] = src[0]; - dst[ret++] = 0; - src++; - } - dst[ret++]=0; - dst[ret++]=0; - return(ret); + char c; + while ((c = *s++)) { + switch (c) { + case '*': + case '?': + case '<': + case '>': + case '"': + return True; + } + } + return False; +} + +BOOL ms_has_wild_w(const smb_ucs2_t *s) +{ + smb_ucs2_t c; + if (!s) return False; + while ((c = *s++)) { + switch (c) { + case UCS2_CHAR('*'): + case UCS2_CHAR('?'): + case UCS2_CHAR('<'): + case UCS2_CHAR('>'): + case UCS2_CHAR('"'): + return True; + } + } + return False; } - -pstring smbrun_path = SMBRUN; - -/**************************************************************************** -run a command via system() using smbrun -****************************************************************************/ -int smbrun(char *cmd,char *outfile) +/******************************************************************* + a wrapper that handles case sensitivity and the special handling + of the ".." name + *******************************************************************/ +BOOL mask_match(char *string, char *pattern, BOOL is_case_sensitive) { - int ret; - pstring syscmd; + fstring p2, s2; - if (!file_exist(smbrun_path,NULL)) - { - DEBUG(0,("SMBRUN ERROR: Can't find %s. Installation problem?\n",smbrun_path)); - return(1); - } - - sprintf(syscmd,"%s \"(%s 2>&1) > %s\"", - smbrun_path,cmd, - outfile?outfile:"/dev/null"); + if (strcmp(string,"..") == 0) string = "."; + if (strcmp(pattern,".") == 0) return False; + + if (is_case_sensitive) { + return ms_fnmatch(pattern, string, Protocol) == 0; + } - DEBUG(5,("smbrun - running %s ",syscmd)); - ret = system(syscmd); - DEBUG(5,("gave %d\n",ret)); - return(ret); + fstrcpy(p2, pattern); + fstrcpy(s2, string); + strlower(p2); + strlower(s2); + return ms_fnmatch(p2, s2, Protocol) == 0; } +/********************************************************* + Recursive routine that is called by unix_wild_match. +*********************************************************/ -/**************************************************************************** -a wrapper for gethostbyname() that tries with all lower and all upper case -if the initial name fails -****************************************************************************/ -struct hostent *Get_Hostbyname(char *name) -{ - char *name2 = strdup(name); - struct hostent *ret; - - if (!name2) - { - DEBUG(0,("Memory allocation error in Get_Hostbyname! panic\n")); - exit(0); - } +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 (!isalnum(*name2)) - { - free(name2); - return(NULL); - } + if(!*p && !*str) + return True; - ret = gethostbyname(name2); - if (ret != NULL) - { - free(name2); - return(ret); - } + if (!*p && str[0] == '.' && str[1] == 0) + return(True); + + if (!*str && *p == '?') { + while (*p == '?') + p++; + return(!*p); + } - /* try with all lowercase */ - strlower(name2); - ret = gethostbyname(name2); - if (ret != NULL) - { - free(name2); - return(ret); - } + if(!*str && (*p == '*' && p[1] == '\0')) + return True; - /* try with all uppercase */ - strupper(name2); - ret = gethostbyname(name2); - if (ret != NULL) - { - free(name2); - return(ret); - } - - /* nothing works :-( */ - free(name2); - return(NULL); + return False; } +/******************************************************************* + Simple case insensitive interface to a UNIX wildcard matcher. +*******************************************************************/ -/**************************************************************************** -check if a process exists. Does this work on all unixes? -****************************************************************************/ -BOOL process_exists(int pid) +BOOL unix_wild_match(char *pattern, char *string) { -#ifdef LINUX - fstring s; - sprintf(s,"/proc/%d",pid); - return(directory_exist(s,NULL)); -#else - { - static BOOL tested=False; - static BOOL ok=False; - fstring s; - if (!tested) { - tested = True; - sprintf(s,"/proc/%05d",getpid()); - ok = file_exist(s,NULL); - } - if (ok) { - sprintf(s,"/proc/%05d",pid); - return(file_exist(s,NULL)); - } - } - - /* a best guess for non root access */ - if (geteuid() != 0) return(True); + pstring p2, s2; + char *p; - /* otherwise use kill */ - return(pid == getpid() || kill(pid,0) == 0); -#endif -} + pstrcpy(p2, pattern); + pstrcpy(s2, string); + strlower(p2); + strlower(s2); + /* Remove any *? and ** from the pattern as they are meaningless */ + for(p = p2; *p; p++) + while( *p == '*' && (p[1] == '?' ||p[1] == '*')) + pstrcpy( &p[1], &p[2]); + + if (strequal(p2,"*")) + return True; -/******************************************************************* -turn a uid into a user name -********************************************************************/ -char *uidtoname(int uid) -{ - static char name[40]; - struct passwd *pass = getpwuid(uid); - if (pass) return(pass->pw_name); - sprintf(name,"%d",uid); - return(name); + return unix_do_match(p2, s2) == 0; } /******************************************************************* -turn a gid into a group name -********************************************************************/ -char *gidtoname(int gid) + free() a data blob +*******************************************************************/ +static void free_data_blob(DATA_BLOB *d) { - static char name[40]; - struct group *grp = getgrgid(gid); - if (grp) return(grp->gr_name); - sprintf(name,"%d",gid); - return(name); + if ((d) && (d->free)) { + SAFE_FREE(d->data); + } } /******************************************************************* -block sigs -********************************************************************/ -void BlockSignals(BOOL block) + 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) { -#ifdef USE_SIGBLOCK - int block_mask = (sigmask(SIGTERM)|sigmask(SIGQUIT)|sigmask(SIGSEGV) - |sigmask(SIGCHLD)|sigmask(SIGQUIT)|sigmask(SIGBUS)| - sigmask(SIGINT)); - if (block) - sigblock(block_mask); - else - sigunblock(block_mask); -#endif -} + DATA_BLOB ret; -#if AJT -/******************************************************************* -my own panic function - not suitable for general use -********************************************************************/ -void ajt_panic(void) -{ - pstring cmd = "/usr/bin/X11/xedit -display :0 /tmp/ERROR_FAULT &"; - smbrun(cmd,NULL); -} -#endif + if (!length) { + ZERO_STRUCT(ret); + return ret; + } -#ifdef USE_DIRECT -#define DIRECT direct -#else -#define DIRECT dirent -#endif + if (p) { + ret.data = smb_xmemdup(p, length); + } else { + ret.data = smb_xmalloc(length); + } + ret.length = length; + ret.free = free_data_blob; + return ret; +} /******************************************************************* -a readdir wrapper which just returns the file name -also return the inode number if requested -********************************************************************/ -char *readdirname(void *p) + construct a data blob, using supplied TALLOC_CTX +*******************************************************************/ +DATA_BLOB data_blob_talloc(TALLOC_CTX *mem_ctx, const void *p, size_t length) { - struct DIRECT *ptr; - char *dname; - - if (!p) return(NULL); - - ptr = (struct DIRECT *)readdir(p); - if (!ptr) return(NULL); - - dname = ptr->d_name; - -#ifdef KANJI - { - static pstring buf; - strcpy(buf, dname); - unix_to_dos(buf, True); - dname = buf; - } -#endif - -#ifdef NEXT2 - if (telldir(p) < 0) return(NULL); -#endif - -#ifdef SUNOS5 - /* this handles a broken compiler setup, causing a mixture - of BSD and SYSV headers and libraries */ - { - static BOOL broken_readdir = False; - if (!broken_readdir && !(*(dname)) && strequal("..",dname-2)) - { - DEBUG(0,("Your readdir() is broken. You have somehow mixed SYSV and BSD headers and libraries\n")); - broken_readdir = True; - } - if (broken_readdir) - return(dname-2); - } -#endif - - return(dname); -} - - - -#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 - -#ifdef NO_INITGROUPS -#include <sys/types.h> -#include <limits.h> -#include <grp.h> - -#ifndef NULL -#define NULL (void *)0 -#endif + DATA_BLOB ret; -/**************************************************************************** - some systems don't have an initgroups call -****************************************************************************/ -int initgroups(char *name,gid_t id) -{ -#ifdef NO_SETGROUPS - /* yikes! no SETGROUPS or INITGROUPS? how can this work? */ - return(0); -#else - gid_t grouplst[NGROUPS_MAX]; - int i,j; - struct group *g; - char *gr; - - grouplst[0] = id; - i = 1; - while (i < NGROUPS_MAX && - ((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; + if (!p || !length) { + ZERO_STRUCT(ret); + return ret; } - gr = g->gr_mem[++j]; - } - } - endgrent(); - return(setgroups(i,grouplst)); -#endif -} -#endif - - -#if WRAP_MALLOC - -/* undo the wrapping temporarily */ -#undef malloc -#undef realloc -#undef free -/**************************************************************************** -wrapper for malloc() to catch memory errors -****************************************************************************/ -void *malloc_wrapped(int size,char *file,int line) -{ -#ifdef xx_old_malloc - void *res = xx_old_malloc(size); -#else - void *res = malloc(size); -#endif - DEBUG(3,("Malloc called from %s(%d) with size=%d gave ptr=0x%X\n", - file,line, - size,(unsigned int)res)); - return(res); -} + ret.data = talloc_memdup(mem_ctx, p, length); + if (ret.data == NULL) + smb_panic("data_blob_talloc: talloc_memdup failed.\n"); -/**************************************************************************** -wrapper for realloc() to catch memory errors -****************************************************************************/ -void *realloc_wrapped(void *ptr,int size,char *file,int line) -{ -#ifdef xx_old_realloc - void *res = xx_old_realloc(ptr,size); -#else - void *res = realloc(ptr,size); -#endif - DEBUG(3,("Realloc\n")); - DEBUG(3,("free called from %s(%d) with ptr=0x%X\n", - file,line, - (unsigned int)ptr)); - DEBUG(3,("Malloc called from %s(%d) with size=%d gave ptr=0x%X\n", - file,line, - size,(unsigned int)res)); - return(res); + ret.length = length; + ret.free = NULL; + return ret; } -/**************************************************************************** -wrapper for free() to catch memory errors -****************************************************************************/ -void free_wrapped(void *ptr,char *file,int line) -{ -#ifdef xx_old_free - xx_old_free(ptr); -#else - free(ptr); -#endif - DEBUG(3,("free called from %s(%d) with ptr=0x%X\n", - file,line,(unsigned int)ptr)); - return; -} - -/* and re-do the define for spots lower in this file */ -#define malloc(size) malloc_wrapped(size,__FILE__,__LINE__) -#define realloc(ptr,size) realloc_wrapped(ptr,size,__FILE__,__LINE__) -#define free(ptr) free_wrapped(ptr,__FILE__,__LINE__) - -#endif - -#ifdef REPLACE_STRSTR -/**************************************************************************** -Mips version of strstr doesn't seem to work correctly. -There is a #define in includes.h to redirect calls to this function. -****************************************************************************/ -char *Strstr(char *s, char *p) +/******************************************************************* +free a data blob +*******************************************************************/ +void data_blob_free(DATA_BLOB *d) { - int len = strlen(p); - - while ( *s != '\0' ) { - if ( strncmp(s, p, len) == 0 ) - return s; - s++; + if (d) { + if (d->free) { + (d->free)(d); + } + ZERO_STRUCTP(d); } - - return NULL; } -#endif /* REPLACE_STRSTR */ - -#ifdef REPLACE_MKTIME /******************************************************************* -a mktime() replacement for those who don't have it - contributed by -C.A. Lademann <cal@zls.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 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); - - epoch = (t->tm_year - 70) * YEAR + - (t->tm_year / 4 - 70 / 4 - t->tm_year / 100) * DAY; - - y = t->tm_year; - 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; -#ifndef NO_TM_NAME - memcpy(t->tm_name, u->tm_name, LTZNMAX); -#endif - } - - return(epoch); -} -#endif /* REPLACE_MKTIME */ - - - -#ifdef REPLACE_RENAME -/* Rename a file. (from libiberty in GNU binutils) */ -int -rename (zfrom, zto) - const char *zfrom; - const char *zto; +clear a DATA_BLOB's contents +*******************************************************************/ +void data_blob_clear(DATA_BLOB *d) { - if (link (zfrom, zto) < 0) - { - if (errno != EEXIST) - return -1; - if (unlink (zto) < 0 - || link (zfrom, zto) < 0) - return -1; - } - return unlink (zfrom); + if (d->data) { + memset(d->data, 0, d->length); + } } -#endif - -#ifdef REPLACE_INNETGR -/* - * Search for a match in a netgroup. This replaces it on broken systems. - */ -int InNetGr(group, host, user, dom) - char *group, *host, *user, *dom; +/******************************************************************* +free a data blob and clear its contents +*******************************************************************/ +void data_blob_clear_free(DATA_BLOB *d) { - 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); + data_blob_clear(d); + data_blob_free(d); } -#endif +#ifdef __INSURE__ -#if WRAP_MEMCPY -#undef memcpy /******************************************************************* -a wrapper around memcpy for diagnostic purposes +This routine is a trick to immediately catch errors when debugging +with insure. A xterm with a gdb is popped up when insure catches +a error. It is Linux specific. ********************************************************************/ -void *memcpy_wrapped(void *d,void *s,int l,char *fname,int line) -{ - if (l>64 && (((int)d)%4) != (((int)s)%4)) - DEBUG(4,("Misaligned memcpy(0x%X,0x%X,%d) at %s(%d)\n",d,s,l,fname,line)); -#ifdef xx_old_memcpy - return(xx_old_memcpy(d,s,l)); -#else - return(memcpy(d,s,l)); -#endif -} -#define memcpy(d,s,l) memcpy_wrapped(d,s,l,__FILE__,__LINE__) -#endif +int _Insure_trap_error(int a1, int a2, int a3, int a4, int a5, int a6) +{ + static int (*fn)(); + int ret; + char pidstr[10]; + /* you can get /usr/bin/backtrace from + http://samba.org/ftp/unpacked/junkcode/backtrace */ + pstring cmd = "/usr/bin/backtrace %d"; + + slprintf(pidstr, sizeof(pidstr)-1, "%d", sys_getpid()); + pstring_sub(cmd, "%d", pidstr); + + if (!fn) { + static void *h; + h = dlopen("/usr/local/parasoft/insure++lite/lib.linux2/libinsure.so", RTLD_LAZY); + fn = dlsym(h, "_Insure_trap_error"); + + if (!h || h == _Insure_trap_error) { + h = dlopen("/usr/local/parasoft/lib.linux2/libinsure.so", RTLD_LAZY); + fn = dlsym(h, "_Insure_trap_error"); + } + } + ret = fn(a1, a2, a3, a4, a5, a6); + system(cmd); + return ret; +} +#endif diff --git a/source3/lib/util_file.c b/source3/lib/util_file.c new file mode 100644 index 0000000000..e80267f84b --- /dev/null +++ b/source3/lib/util_file.c @@ -0,0 +1,595 @@ +/* + * 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; + + gotalarm = 0; + 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); + ret = fcntl(fd, SMB_F_SETLKW, &lock); + alarm(0); + CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN); + + 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; +} + +/*************************************************************** + locks a file for enumeration / modification. + update to be set = True if modification is required. +****************************************************************/ + +void *startfilepwent(char *pfile, char *s_readbuf, int bufsize, + int *file_lock_depth, BOOL update) +{ + FILE *fp = NULL; + + if (!*pfile) + { + DEBUG(0, ("startfilepwent: No file set\n")); + return (NULL); + } + DEBUG(10, ("startfilepwent: opening file %s\n", pfile)); + + fp = sys_fopen(pfile, update ? "r+b" : "rb"); + + if (fp == NULL) { + DEBUG(0, ("startfilepwent: unable to open file %s\n", pfile)); + return NULL; + } + + /* Set a buffer to do more efficient reads */ + setvbuf(fp, s_readbuf, _IOFBF, bufsize); + + if (!file_lock(fileno(fp), (update ? F_WRLCK : F_RDLCK), 5, file_lock_depth)) + { + DEBUG(0, ("startfilepwent: unable to lock file %s\n", pfile)); + fclose(fp); + return NULL; + } + + /* Make sure it is only rw by the owner */ + chmod(pfile, 0600); + + /* We have a lock on the file. */ + return (void *)fp; +} + +/*************************************************************** + End enumeration of the file. +****************************************************************/ +void endfilepwent(void *vp, int *file_lock_depth) +{ + FILE *fp = (FILE *)vp; + + file_unlock(fileno(fp), file_lock_depth); + fclose(fp); + DEBUG(7, ("endfilepwent: closed file.\n")); +} + +/************************************************************************* + Return the current position in the file list as an SMB_BIG_UINT. + This must be treated as an opaque token. +*************************************************************************/ +SMB_BIG_UINT getfilepwpos(void *vp) +{ + return (SMB_BIG_UINT)sys_ftell((FILE *)vp); +} + +/************************************************************************* + Set the current position in the file list from an SMB_BIG_UINT. + This must be treated as an opaque token. +*************************************************************************/ +BOOL setfilepwpos(void *vp, SMB_BIG_UINT tok) +{ + return !sys_fseek((FILE *)vp, (SMB_OFF_T)tok, SEEK_SET); +} + +/************************************************************************* + 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 exand buffer!\n")); + close(fd); + SAFE_FREE(p); + return NULL; + } else p = tp; + memcpy(p+total, buf, n); + total += n; + } + if (p) p[total] = 0; + + 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 || 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) != length) { + return False; + } + close(fd); + return True; +} diff --git a/source3/lib/util_getent.c b/source3/lib/util_getent.c new file mode 100644 index 0000000000..02e4b932de --- /dev/null +++ b/source3/lib/util_getent.c @@ -0,0 +1,318 @@ +/* + 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" + +#if 0 +static void print_grent_list(struct sys_grent *glist) +{ + DEBUG(100, ("print_grent_list: %x\n", glist )); + while (glist) { + DEBUG(100,("glist: %x ", glist)); + if (glist->gr_name) + DEBUG(100,(": gr_name = (%x) %s ", glist->gr_name, glist->gr_name)); + if (glist->gr_passwd) + DEBUG(100,(": gr_passwd = (%x) %s ", glist->gr_passwd, glist->gr_passwd)); + if (glist->gr_mem) { + int i; + for (i = 0; glist->gr_mem[i]; i++) + DEBUG(100,(" : gr_mem[%d] = (%x) %s ", i, glist->gr_mem[i], glist->gr_mem[i])); + } + DEBUG(100,(": gr_next = %x\n", glist->next )); + glist = glist->next; + } + DEBUG(100,("FINISHED !\n\n")); +} +#endif + +/**************************************************************** + 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)); + size_t len = strlen(grp->gr_mem[i])+1; + if (entry == NULL) { + free_userlist(list_head); + return NULL; + } + entry->unix_name = (char *)malloc(len); + if (entry->unix_name == NULL) { + SAFE_FREE(entry); + free_userlist(list_head); + return NULL; + } + safe_strcpy(entry->unix_name, grp->gr_mem[i],len); + 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; + + (void) 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); + } + + 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; +} + +/**************************************************************** + 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/source3/lib/util_pw.c b/source3/lib/util_pw.c new file mode 100644 index 0000000000..259649a064 --- /dev/null +++ b/source3/lib/util_pw.c @@ -0,0 +1,133 @@ +/* + 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" + +struct passwd *make_modifyable_passwd(const struct passwd *from) +{ + struct passwd *ret = smb_xmalloc(sizeof(*ret)); +/* This is the assumed shape of the members by certain parts of the code... + fstring pw_name; + fstring pw_passwd; + fstring pw_gecos; + pstring pw_dir; + pstring pw_shell; +*/ + char *pw_name = smb_xmalloc(sizeof(fstring)); + char *pw_passwd = smb_xmalloc(sizeof(fstring)); + char *pw_gecos = smb_xmalloc(sizeof(fstring)); + char *pw_dir = smb_xmalloc(sizeof(pstring)); + char *pw_shell = smb_xmalloc(sizeof(pstring)); + + ZERO_STRUCTP(ret); + + /* + * Now point the struct's members as the + * newly allocated buffers: + */ + + ret->pw_name = pw_name; + fstrcpy(ret->pw_name, from->pw_name); + + ret->pw_passwd = pw_passwd; + fstrcpy(ret->pw_passwd, from->pw_passwd); + + ret->pw_uid = from->pw_uid; + ret->pw_gid = from->pw_gid; + + ret->pw_gecos = pw_gecos; + fstrcpy(ret->pw_gecos, from->pw_gecos); + + ret->pw_dir = pw_dir; + pstrcpy(ret->pw_dir, from->pw_dir); + + ret->pw_shell = pw_shell; + pstrcpy(ret->pw_shell, from->pw_shell); + + return ret; +} + +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 = 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 = getpwuid(uid); + + if (!temp) { +#if 0 + if (errno == ENOMEM) { + /* what now? */ + } +#endif + return NULL; + } + + return alloc_copy_passwd(temp); +} diff --git a/source3/lib/util_seaccess.c b/source3/lib/util_seaccess.c new file mode 100644 index 0000000000..8ed266aced --- /dev/null +++ b/source3/lib/util_seaccess.c @@ -0,0 +1,446 @@ +/* + 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" +#include "nterr.h" +#include "sids.h" + +/********************************************************************************** + 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, 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, 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(SEC_DESC *sd, 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 */ + + 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++) { + DEBUG(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]; + + DEBUG(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; + 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; +} diff --git a/source3/lib/util_sec.c b/source3/lib/util_sec.c new file mode 100644 index 0000000000..d59b1b0471 --- /dev/null +++ b/source3/lib/util_sec.c @@ -0,0 +1,422 @@ +/* + Unix SMB/CIFS implementation. + Copyright (C) Jeremy Allison 1998. + rewritten for version 2.0.6 by Tridge + + 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 AUTOCONF_TEST +#include "includes.h" +#else +/* we are running this code in autoconf test mode to see which type of setuid + function works */ +#if defined(HAVE_UNISTD_H) +#include <unistd.h> +#endif +#include <stdlib.h> +#include <stdio.h> +#include <sys/types.h> +#include <errno.h> + +#ifdef HAVE_SYS_PRIV_H +#include <sys/priv.h> +#endif +#ifdef HAVE_SYS_ID_H +#include <sys/id.h> +#endif + +#define DEBUG(x, y) printf y +#define smb_panic(x) exit(1) +#define BOOL int +#endif + +/* are we running as non-root? This is used by the regresison test code, + and potentially also for sites that want non-root smbd */ +static uid_t initial_uid; +static gid_t initial_gid; + +/**************************************************************************** +remember what uid we got started as - this allows us to run correctly +as non-root while catching trapdoor systems +****************************************************************************/ +void sec_init(void) +{ + initial_uid = geteuid(); + initial_gid = getegid(); +} + +/**************************************************************************** +some code (eg. winbindd) needs to know what uid we started as +****************************************************************************/ +uid_t sec_initial_uid(void) +{ + return initial_uid; +} + +/**************************************************************************** +some code (eg. winbindd, profiling shm) needs to know what gid we started as +****************************************************************************/ +gid_t sec_initial_gid(void) +{ + return initial_gid; +} + +/**************************************************************************** +are we running in non-root mode? +****************************************************************************/ +BOOL non_root_mode(void) +{ + return (initial_uid != (uid_t)0); +} + +/**************************************************************************** +abort if we haven't set the uid correctly +****************************************************************************/ +static void assert_uid(uid_t ruid, uid_t euid) +{ + if ((euid != (uid_t)-1 && geteuid() != euid) || + (ruid != (uid_t)-1 && getuid() != ruid)) { + if (!non_root_mode()) { + DEBUG(0,("Failed to set uid privileges to (%d,%d) now set to (%d,%d)\n", + (int)ruid, (int)euid, + (int)getuid(), (int)geteuid())); + smb_panic("failed to set uid\n"); + exit(1); + } + } +} + +/**************************************************************************** +abort if we haven't set the gid correctly +****************************************************************************/ +static void assert_gid(gid_t rgid, gid_t egid) +{ + if ((egid != (gid_t)-1 && getegid() != egid) || + (rgid != (gid_t)-1 && getgid() != rgid)) { + if (!non_root_mode()) { + DEBUG(0,("Failed to set gid privileges to (%d,%d) now set to (%d,%d) uid=(%d,%d)\n", + (int)rgid, (int)egid, + (int)getgid(), (int)getegid(), + (int)getuid(), (int)geteuid())); + smb_panic("failed to set gid\n"); + exit(1); + } + } +} + +/**************************************************************************** + Gain root privilege before doing something. + We want to end up with ruid==euid==0 +****************************************************************************/ +void gain_root_privilege(void) +{ +#if USE_SETRESUID + setresuid(0,0,0); +#endif + +#if USE_SETEUID + seteuid(0); +#endif + +#if USE_SETREUID + setreuid(0, 0); +#endif + +#if USE_SETUIDX + setuidx(ID_EFFECTIVE, 0); + setuidx(ID_REAL, 0); +#endif + + /* this is needed on some systems */ + setuid(0); + + assert_uid(0, 0); +} + + +/**************************************************************************** + Ensure our real and effective groups are zero. + we want to end up with rgid==egid==0 +****************************************************************************/ +void gain_root_group_privilege(void) +{ +#if USE_SETRESUID + setresgid(0,0,0); +#endif + +#if USE_SETREUID + setregid(0,0); +#endif + +#if USE_SETEUID + setegid(0); +#endif + +#if USE_SETUIDX + setgidx(ID_EFFECTIVE, 0); + setgidx(ID_REAL, 0); +#endif + + setgid(0); + + assert_gid(0, 0); +} + + +/**************************************************************************** + Set *only* the effective uid. + we want to end up with ruid==0 and euid==uid +****************************************************************************/ +void set_effective_uid(uid_t uid) +{ +#if USE_SETRESUID + setresuid(-1,uid,-1); +#endif + +#if USE_SETREUID + setreuid(-1,uid); +#endif + +#if USE_SETEUID + seteuid(uid); +#endif + +#if USE_SETUIDX + setuidx(ID_EFFECTIVE, uid); +#endif + + assert_uid(-1, uid); +} + +/**************************************************************************** + Set *only* the effective gid. + we want to end up with rgid==0 and egid==gid +****************************************************************************/ +void set_effective_gid(gid_t gid) +{ +#if USE_SETRESUID + setresgid(-1,gid,-1); +#endif + +#if USE_SETREUID + setregid(-1,gid); +#endif + +#if USE_SETEUID + setegid(gid); +#endif + +#if USE_SETUIDX + setgidx(ID_EFFECTIVE, gid); +#endif + + assert_gid(-1, gid); +} + +static uid_t saved_euid, saved_ruid; + +/**************************************************************************** + save the real and effective uid for later restoration. Used by the quotas + code +****************************************************************************/ +void save_re_uid(void) +{ + saved_ruid = getuid(); + saved_euid = geteuid(); +} + + +/**************************************************************************** + and restore them! +****************************************************************************/ +void restore_re_uid(void) +{ + set_effective_uid(0); + +#if USE_SETRESUID + setresuid(saved_ruid, saved_euid, -1); +#elif USE_SETREUID + setreuid(saved_ruid, -1); + setreuid(-1,saved_euid); +#elif USE_SETUIDX + setuidx(ID_REAL, saved_ruid); + setuidx(ID_EFFECTIVE, saved_euid); +#else + set_effective_uid(saved_euid); + if (getuid() != saved_ruid) + setuid(saved_ruid); + set_effective_uid(saved_euid); +#endif + + assert_uid(saved_ruid, saved_euid); +} + +/**************************************************************************** + set the real AND effective uid to the current effective uid in a way that + allows root to be regained. + This is only possible on some platforms. +****************************************************************************/ +int set_re_uid(void) +{ + uid_t uid = geteuid(); + +#if USE_SETRESUID + setresuid(geteuid(), -1, -1); +#endif + +#if USE_SETREUID + setreuid(0, 0); + setreuid(uid, -1); + setreuid(-1, uid); +#endif + +#if USE_SETEUID + /* can't be done */ + return -1; +#endif + +#if USE_SETUIDX + /* can't be done */ + return -1; +#endif + + assert_uid(uid, uid); + return 0; +} + + +/**************************************************************************** + Become the specified uid and gid - permanently ! + there should be no way back if possible +****************************************************************************/ +void become_user_permanently(uid_t uid, gid_t gid) +{ + /* + * First - gain root privilege. We do this to ensure + * we can lose it again. + */ + + gain_root_privilege(); + gain_root_group_privilege(); + +#if USE_SETRESUID + setresgid(gid,gid,gid); + setgid(gid); + setresuid(uid,uid,uid); + setuid(uid); +#endif + +#if USE_SETREUID + setregid(gid,gid); + setgid(gid); + setreuid(uid,uid); + setuid(uid); +#endif + +#if USE_SETEUID + setegid(gid); + setgid(gid); + setuid(uid); + seteuid(uid); + setuid(uid); +#endif + +#if USE_SETUIDX + setgidx(ID_REAL, gid); + setgidx(ID_EFFECTIVE, gid); + setgid(gid); + setuidx(ID_REAL, uid); + setuidx(ID_EFFECTIVE, uid); + setuid(uid); +#endif + + assert_uid(uid, uid); + assert_gid(gid, gid); +} + +#ifdef AUTOCONF_TEST + +/**************************************************************************** +this function just checks that we don't get ENOSYS back +****************************************************************************/ +static int have_syscall(void) +{ + errno = 0; + +#if USE_SETRESUID + setresuid(-1,-1,-1); +#endif + +#if USE_SETREUID + setreuid(-1,-1); +#endif + +#if USE_SETEUID + seteuid(-1); +#endif + +#if USE_SETUIDX + setuidx(ID_EFFECTIVE, -1); +#endif + + if (errno == ENOSYS) return -1; + + return 0; +} + +main() +{ + if (getuid() != 0) { +#if (defined(AIX) && defined(USE_SETREUID)) + /* setreuid is badly broken on AIX 4.1, we avoid it completely */ + fprintf(stderr,"avoiding possibly broken setreuid\n"); + exit(1); +#endif + + /* if not running as root then at least check to see if we get ENOSYS - this + handles Linux 2.0.x with glibc 2.1 */ + fprintf(stderr,"not running as root: checking for ENOSYS\n"); + exit(have_syscall()); + } + + gain_root_privilege(); + gain_root_group_privilege(); + set_effective_gid(1); + set_effective_uid(1); + save_re_uid(); + restore_re_uid(); + gain_root_privilege(); + gain_root_group_privilege(); + become_user_permanently(1, 1); + setuid(0); + if (getuid() == 0) { + fprintf(stderr,"uid not set permanently\n"); + exit(1); + } + + printf("OK\n"); + + exit(0); +} +#endif + +/**************************************************************************** +Check if we are setuid root. Used in libsmb and smbpasswd paranoia checks. +****************************************************************************/ +BOOL is_setuid_root(void) +{ + return (geteuid() == (uid_t)0) && (getuid() != (uid_t)0); +} diff --git a/source3/lib/util_sid.c b/source3/lib/util_sid.c new file mode 100644 index 0000000000..cd7b64bb70 --- /dev/null +++ b/source3/lib/util_sid.c @@ -0,0 +1,720 @@ +/* + 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 + + 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! the global_sam_sid is the SID of our local SAM. This is only + equal to the domain SID when we are a DC, otherwise its our + workstation SID */ +DOM_SID global_sam_sid; +extern pstring global_myname; +extern fstring global_myworkgroup; + +/* + * Some useful sids + */ + +DOM_SID global_sid_Builtin; /* Local well-known domain */ +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_Creator_Owner; /* Creator Owner */ +DOM_SID global_sid_Creator_Group; /* Creator Group */ +DOM_SID global_sid_NT_Authority; /* NT Authority */ +DOM_SID global_sid_NULL; /* NULL sid */ +DOM_SID global_sid_Builtin_Guests; /* Builtin guest users */ +DOM_SID global_sid_Authenticated_Users; /* All authenticated rids */ +DOM_SID global_sid_Network; /* Network rids */ +DOM_SID global_sid_Anonymous; /* Anonymous login */ + +const DOM_SID *global_sid_everyone = &global_sid_World; + +typedef struct _known_sid_users { + uint32 rid; + enum SID_NAME_USE sid_name_use; + char *known_user_name; +} known_sid_users; + +/* static known_sid_users no_users[] = {{0, 0, NULL}}; */ + +static known_sid_users everyone_users[] = { + { 0, SID_NAME_WKN_GRP, "Everyone" }, + {0, (enum SID_NAME_USE)0, NULL}}; + +static known_sid_users creator_owner_users[] = { + { 0, SID_NAME_ALIAS, "Creator Owner" }, + {0, (enum SID_NAME_USE)0, NULL}}; + +static known_sid_users nt_authority_users[] = { + { 1, SID_NAME_ALIAS, "Dialup" }, + { 2, SID_NAME_ALIAS, "Network"}, + { 3, SID_NAME_ALIAS, "Batch"}, + { 4, SID_NAME_ALIAS, "Interactive"}, + { 6, SID_NAME_ALIAS, "Service"}, + { 7, SID_NAME_ALIAS, "AnonymousLogon"}, + { 8, SID_NAME_ALIAS, "Proxy"}, + { 9, SID_NAME_ALIAS, "ServerLogon"}, + { 11, SID_NAME_ALIAS, "Authenticated Users"}, + { 18, SID_NAME_ALIAS, "SYSTEM"}, + { 0, (enum SID_NAME_USE)0, NULL}}; + +static known_sid_users builtin_groups[] = { + { BUILTIN_ALIAS_RID_ADMINS, SID_NAME_ALIAS, "Administrators" }, + { BUILTIN_ALIAS_RID_USERS, SID_NAME_ALIAS, "Users" }, + { BUILTIN_ALIAS_RID_GUESTS, SID_NAME_ALIAS, "Guests" }, + { BUILTIN_ALIAS_RID_ACCOUNT_OPS, SID_NAME_ALIAS, "Account Operators" }, + { BUILTIN_ALIAS_RID_SYSTEM_OPS, SID_NAME_ALIAS, "Server Operators" }, + { BUILTIN_ALIAS_RID_PRINT_OPS, SID_NAME_ALIAS, "Print Operators" }, + { BUILTIN_ALIAS_RID_BACKUP_OPS, SID_NAME_ALIAS, "Backup Operators" }, + { 0, (enum SID_NAME_USE)0, NULL}}; + +#define MAX_SID_NAMES 7 + +static struct sid_name_map_info +{ + DOM_SID *sid; + char *name; + known_sid_users *known_users; +} sid_name_map[MAX_SID_NAMES]; + +static BOOL sid_name_map_initialized = False; + +/* + * An NT compatible anonymous token. + */ + +static DOM_SID anon_sid_array[3]; + +NT_USER_TOKEN anonymous_token = { + 3, + anon_sid_array +}; + +/************************************************************************** + quick init function + *************************************************************************/ +static void init_sid_name_map (void) +{ + int i = 0; + + if (sid_name_map_initialized) return; + + + if ((lp_security() == SEC_USER) && lp_domain_logons()) { + sid_name_map[i].sid = &global_sam_sid; + sid_name_map[i].name = global_myworkgroup; + sid_name_map[i].known_users = NULL; + i++; + sid_name_map[i].sid = &global_sam_sid; + sid_name_map[i].name = global_myname; + sid_name_map[i].known_users = NULL; + i++; + } + else { + sid_name_map[i].sid = &global_sam_sid; + sid_name_map[i].name = global_myname; + sid_name_map[i].known_users = NULL; + i++; + } + + sid_name_map[i].sid = &global_sid_Builtin; + sid_name_map[i].name = "BUILTIN"; + sid_name_map[i].known_users = &builtin_groups[0]; + i++; + + sid_name_map[i].sid = &global_sid_World_Domain; + sid_name_map[i].name = ""; + sid_name_map[i].known_users = &everyone_users[0]; + i++; + + sid_name_map[i].sid = &global_sid_Creator_Owner_Domain; + sid_name_map[i].name = ""; + sid_name_map[i].known_users = &creator_owner_users[0]; + i++; + + sid_name_map[i].sid = &global_sid_NT_Authority; + sid_name_map[i].name = "NT Authority"; + sid_name_map[i].known_users = &nt_authority_users[0]; + i++; + + + /* end of array */ + sid_name_map[i].sid = NULL; + sid_name_map[i].name = NULL; + sid_name_map[i].known_users = NULL; + + sid_name_map_initialized = True; + + return; + +} + +/**************************************************************************** + Creates some useful well known sids +****************************************************************************/ + +void generate_wellknown_sids(void) +{ + string_to_sid(&global_sid_Builtin, "S-1-5-32"); + string_to_sid(&global_sid_Builtin_Guests, "S-1-5-32-546"); + string_to_sid(&global_sid_World_Domain, "S-1-1"); + string_to_sid(&global_sid_World, "S-1-1-0"); + 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"); + string_to_sid(&global_sid_NT_Authority, "S-1-5"); + string_to_sid(&global_sid_NULL, "S-1-0-0"); + string_to_sid(&global_sid_Authenticated_Users, "S-1-5-11"); + string_to_sid(&global_sid_Network, "S-1-5-2"); + string_to_sid(&global_sid_Anonymous, "S-1-5-7"); + + /* 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); +} + +/************************************************************************** + Turns a domain SID into a name, returned in the nt_domain argument. +***************************************************************************/ + +BOOL map_domain_sid_to_name(DOM_SID *sid, char *nt_domain) +{ + fstring sid_str; + int i = 0; + + sid_to_string(sid_str, sid); + + if (!sid_name_map_initialized) + init_sid_name_map(); + + DEBUG(5,("map_domain_sid_to_name: %s\n", sid_str)); + + if (nt_domain == NULL) + return False; + + while (sid_name_map[i].sid != NULL) { + sid_to_string(sid_str, sid_name_map[i].sid); + DEBUG(5,("map_domain_sid_to_name: compare: %s\n", sid_str)); + if (sid_equal(sid_name_map[i].sid, sid)) { + fstrcpy(nt_domain, sid_name_map[i].name); + DEBUG(5,("map_domain_sid_to_name: found '%s'\n", nt_domain)); + return True; + } + i++; + } + + DEBUG(5,("map_domain_sid_to_name: mapping for %s not found\n", sid_str)); + + return False; +} + +/************************************************************************** + Looks up a known username from one of the known domains. +***************************************************************************/ + +BOOL lookup_known_rid(DOM_SID *sid, uint32 rid, char *name, enum SID_NAME_USE *psid_name_use) +{ + int i = 0; + struct sid_name_map_info *psnm; + + if (!sid_name_map_initialized) + init_sid_name_map(); + + for(i = 0; sid_name_map[i].sid != NULL; i++) { + psnm = &sid_name_map[i]; + if(sid_equal(psnm->sid, sid)) { + int j; + for(j = 0; psnm->known_users && psnm->known_users[j].known_user_name != NULL; j++) { + if(rid == psnm->known_users[j].rid) { + DEBUG(5,("lookup_builtin_rid: rid = %u, domain = '%s', user = '%s'\n", + (unsigned int)rid, psnm->name, psnm->known_users[j].known_user_name )); + fstrcpy( name, psnm->known_users[j].known_user_name); + *psid_name_use = psnm->known_users[j].sid_name_use; + return True; + } + } + } + } + + return False; +} + +/************************************************************************** + Turns a domain name into a SID. + *** side-effect: if the domain name is NULL, it is set to our domain *** +***************************************************************************/ + +BOOL map_domain_name_to_sid(DOM_SID *sid, char *nt_domain) +{ + int i = 0; + + if (nt_domain == NULL) { + DEBUG(5,("map_domain_name_to_sid: mapping NULL domain to our SID.\n")); + sid_copy(sid, &global_sam_sid); + return True; + } + + if (nt_domain[0] == 0) { + fstrcpy(nt_domain, global_myname); + DEBUG(5,("map_domain_name_to_sid: overriding blank name to %s\n", nt_domain)); + sid_copy(sid, &global_sam_sid); + return True; + } + + DEBUG(5,("map_domain_name_to_sid: %s\n", nt_domain)); + + if (!sid_name_map_initialized) + init_sid_name_map(); + + while (sid_name_map[i].name != NULL) { + DEBUG(5,("map_domain_name_to_sid: compare: %s\n", sid_name_map[i].name)); + if (strequal(sid_name_map[i].name, nt_domain)) { + fstring sid_str; + sid_copy(sid, sid_name_map[i].sid); + sid_to_string(sid_str, sid_name_map[i].sid); + DEBUG(5,("map_domain_name_to_sid: found %s\n", sid_str)); + return True; + } + i++; + } + + DEBUG(0,("map_domain_name_to_sid: mapping to %s not found.\n", nt_domain)); + return False; +} + +/************************************************************************** + Splits a name of format \DOMAIN\name or name into its two components. + Sets the DOMAIN name to global_myname if it has not been specified. +***************************************************************************/ + +void split_domain_name(const char *fullname, char *domain, char *name) +{ + pstring full_name; + char *p, *sep; + + 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, global_myname); + fstrcpy(name, full_name); + } + + DEBUG(10,("split_domain_name:name '%s' split into domain :'%s' and user :'%s'\n", + fullname, domain, name)); +} + +/***************************************************************** + Convert a SID to an ascii string. +*****************************************************************/ + +char *sid_to_string(fstring sidstr_out, DOM_SID *sid) +{ + char subauth[16]; + int i; + /* BIG NOTE: this function only does SIDS where the identauth is not >= 2^32 */ + uint32 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_static(DOM_SID *sid) +{ + static fstring sid_str; + sid_to_string(sid_str, sid); + return sid_str; +} + +/***************************************************************** + 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 *p, *q; + /* 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)); + + q = p = 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(DOM_SID *sid, uint32 *rid) +{ + if (sid->num_auths > 0) { + *rid = sid->sub_auths[sid->num_auths - 1]; + return True; + } + return False; +} + +/***************************************************************** + Copies a sid +*****************************************************************/ + +void sid_copy(DOM_SID *dst, const DOM_SID *src) +{ + int i; + + memset((char *)dst, '\0', sizeof(DOM_SID)); + + 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]; +} + +/***************************************************************** + Duplicates a sid - mallocs the target. +*****************************************************************/ + +DOM_SID *sid_dup(DOM_SID *src) +{ + DOM_SID *dst; + + if(!src) + return NULL; + + if((dst = malloc(sizeof(DOM_SID))) != NULL) { + memset(dst, '\0', sizeof(DOM_SID)); + sid_copy( dst, src); + } + + return dst; +} + +/***************************************************************** + Write a sid out into on-the-wire format. +*****************************************************************/ +BOOL sid_linearize(char *outbuf, size_t len, 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(char *inbuf, size_t len, DOM_SID *sid) +{ + int i; + if (len < 8) return False; + 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. +*****************************************************************/ +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 our domain SID (S-1-5-21-x-y-z). +*****************************************************************/ +BOOL sid_check_is_domain(const DOM_SID *sid) +{ + return sid_equal(sid, &global_sam_sid); +} + + +/***************************************************************** + 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 our domain SID (S-1-5-21-x-y-z). +*****************************************************************/ +BOOL sid_check_is_in_our_domain(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_sam_sid); +} + +/***************************************************************** + Check if the SID is our domain SID (S-1-5-21-x-y-z). +*****************************************************************/ +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(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_Creator_Owner_Domain)) + 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(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; +} + diff --git a/source3/lib/util_sock.c b/source3/lib/util_sock.c new file mode 100644 index 0000000000..af3182264d --- /dev/null +++ b/source3/lib/util_sock.c @@ -0,0 +1,1340 @@ +/* + 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" + +#ifdef WITH_SSL +#include <openssl/ssl.h> +#undef Realloc /* SSLeay defines this and samba has a function of this name */ +extern SSL *ssl; +extern int sslFd; +#endif /* WITH_SSL */ + +/* the last IP received from */ +struct in_addr lastip; + +/* the last port received from */ +int lastport=0; + +int smb_read_error = 0; + +/**************************************************************************** + 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 +{ + char *name; + int level; + int option; + int value; + int opttype; +} smb_socket_option; + +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; + 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, 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) +{ + ssize_t ret; + struct sockaddr_in sock; + socklen_t socklen = sizeof(sock); + + memset((char *)&sock,'\0',socklen); + memset((char *)&lastip,'\0',sizeof(lastip)); + ret = (ssize_t)recvfrom(fd,buf,len,0,(struct sockaddr *)&sock,&socklen); + if (ret <= 0) { + DEBUG(2,("read socket failed. ERRNO=%s\n",strerror(errno))); + return(0); + } + + lastip = sock.sin_addr; + lastport = ntohs(sock.sin_port); + + DEBUG(10,("read_udp_socket: lastip %s lastport %d read: %d\n", + inet_ntoa(lastip), lastport, ret)); + + return(ret); +} + +/******************************************************************* + checks if read data is outstanding. + ********************************************************************/ +int read_data_outstanding(int fd, unsigned int time_out) +{ + int selrtn; + fd_set fds; + struct timeval timeout; + + FD_ZERO(&fds); + FD_SET(fd, &fds); + + timeout.tv_sec = (time_t) (time_out / 1000); + timeout.tv_usec = (long)(1000 * (time_out % 1000)); + + selrtn = sys_select_intr(fd + 1, &fds, NULL, NULL, &timeout); + + if (selrtn <= 0) + { + return selrtn; + } + return FD_ISSET(fd, &fds) ? 1 : 0; +} + +/**************************************************************************** + Read data from a socket with a timout in msec. + mincount = if timeout, minimum to read before returning + maxcount = number to be read. + time_out = timeout in milliseconds +****************************************************************************/ + +static ssize_t read_socket_with_timeout(int fd,char *buf,size_t mincnt,size_t maxcnt,unsigned int time_out) +{ + fd_set fds; + int selrtn; + ssize_t readret; + size_t nread = 0; + struct timeval timeout; + + /* just checking .... */ + if (maxcnt <= 0) + return(0); + + smb_read_error = 0; + + /* Blocking read */ + if (time_out <= 0) { + if (mincnt == 0) mincnt = maxcnt; + + while (nread < mincnt) { +#ifdef WITH_SSL + if (fd == sslFd) { + readret = SSL_read(ssl, buf + nread, maxcnt - nread); + } else { + readret = read(fd, buf + nread, maxcnt - nread); + } +#else /* WITH_SSL */ + readret = read(fd, buf + nread, maxcnt - nread); +#endif /* WITH_SSL */ + + if (readret == 0) { + DEBUG(5,("read_socket_with_timeout: blocking read. EOF from client.\n")); + smb_read_error = READ_EOF; + return -1; + } + + if (readret == -1) { + DEBUG(0,("read_socket_with_timeout: read error = %s.\n", strerror(errno) )); + smb_read_error = READ_ERROR; + return -1; + } + nread += readret; + } + return((ssize_t)nread); + } + + /* Most difficult - timeout read */ + /* If this is ever called on a disk file and + mincnt is greater then the filesize then + system performance will suffer severely as + select always returns true on disk files */ + + /* Set initial timeout */ + timeout.tv_sec = (time_t)(time_out / 1000); + timeout.tv_usec = (long)(1000 * (time_out % 1000)); + + for (nread=0; nread < mincnt; ) { + FD_ZERO(&fds); + FD_SET(fd,&fds); + + selrtn = sys_select_intr(fd+1,&fds,NULL,NULL,&timeout); + + /* Check if error */ + if (selrtn == -1) { + /* something is wrong. Maybe the socket is dead? */ + DEBUG(0,("read_socket_with_timeout: timeout read. select error = %s.\n", strerror(errno) )); + smb_read_error = READ_ERROR; + return -1; + } + + /* Did we timeout ? */ + if (selrtn == 0) { + DEBUG(10,("read_socket_with_timeout: timeout read. select timed out.\n")); + smb_read_error = READ_TIMEOUT; + return -1; + } + +#ifdef WITH_SSL + if (fd == sslFd) { + readret = SSL_read(ssl, buf + nread, maxcnt - nread); + }else{ + readret = read(fd, buf + nread, maxcnt - nread); + } +#else /* WITH_SSL */ + readret = read(fd, buf+nread, maxcnt-nread); +#endif /* WITH_SSL */ + + if (readret == 0) { + /* we got EOF on the file descriptor */ + DEBUG(5,("read_socket_with_timeout: timeout read. EOF from client.\n")); + smb_read_error = READ_EOF; + return -1; + } + + if (readret == -1) { + /* the descriptor is probably dead */ + DEBUG(0,("read_socket_with_timeout: timeout read. read error = %s.\n", strerror(errno) )); + smb_read_error = READ_ERROR; + return -1; + } + + nread += readret; + } + + /* Return the number we got */ + return (ssize_t)nread; +} + +/**************************************************************************** + Read data from a fd with a timout in msec. + mincount = if timeout, minimum to read before returning + maxcount = number to be read. + time_out = timeout in milliseconds +****************************************************************************/ + +ssize_t read_with_timeout(int fd, char *buf, size_t mincnt, size_t maxcnt, + unsigned int time_out) +{ + ssize_t readret; + size_t nread = 0; + + /* just checking .... */ + if (maxcnt <= 0) + return(0); + + /* Blocking read */ + if (time_out <= 0) { + if (mincnt == 0) mincnt = maxcnt; + + while (nread < mincnt) { +#ifdef WITH_SSL + if(fd == sslFd){ + readret = SSL_read(ssl, buf + nread, maxcnt - nread); + }else{ + readret = read(fd, buf + nread, maxcnt - nread); + } +#else /* WITH_SSL */ + readret = read(fd, buf + nread, maxcnt - nread); +#endif /* WITH_SSL */ + + if (readret <= 0) + return readret; + + nread += readret; + } + return((ssize_t)nread); + } + + /* Most difficult - timeout read */ + /* If this is ever called on a disk file and + mincnt is greater then the filesize then + system performance will suffer severely as + select always returns true on disk files */ + + for (nread=0; nread < mincnt; ) { + int selrtn = read_data_outstanding(fd, time_out); + + if(selrtn <= 0) + return selrtn; + +#ifdef WITH_SSL + if(fd == sslFd){ + readret = SSL_read(ssl, buf + nread, maxcnt - nread); + }else{ + readret = read(fd, buf + nread, maxcnt - nread); + } +#else /* WITH_SSL */ + readret = read(fd, buf+nread, maxcnt-nread); +#endif /* WITH_SSL */ + + if (readret <= 0) + return readret; + + nread += readret; + } + + /* Return the number we got */ + return((ssize_t)nread); +} + +/**************************************************************************** +send a keepalive packet (rfc1002) +****************************************************************************/ + +BOOL send_keepalive(int client) +{ + unsigned char buf[4]; + + buf[0] = SMBkeepalive; + buf[1] = buf[2] = buf[3] = 0; + + return(write_socket_data(client,(char *)buf,4) == 4); +} + +/**************************************************************************** + 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; + + smb_read_error = 0; + + while (total < N) + { +#ifdef WITH_SSL + if(fd == sslFd){ + ret = SSL_read(ssl, buffer + total, N - total); + }else{ + ret = read(fd,buffer + total,N - total); + } +#else /* WITH_SSL */ + ret = read(fd,buffer + total,N - total); +#endif /* WITH_SSL */ + + if (ret == 0) + { + DEBUG(10,("read_data: read of %d returned 0. Error = %s\n", (int)(N - total), strerror(errno) )); + smb_read_error = READ_EOF; + return 0; + } + if (ret == -1) + { + DEBUG(0,("read_data: read failure for %d. Error = %s\n", (int)(N - total), strerror(errno) )); + smb_read_error = READ_ERROR; + return -1; + } + total += ret; + } + return (ssize_t)total; +} + +/**************************************************************************** + Read data from a socket, reading exactly N bytes. +****************************************************************************/ + +static ssize_t read_socket_data(int fd,char *buffer,size_t N) +{ + ssize_t ret; + size_t total=0; + + smb_read_error = 0; + + while (total < N) + { +#ifdef WITH_SSL + if(fd == sslFd){ + ret = SSL_read(ssl, buffer + total, N - total); + }else{ + ret = read(fd,buffer + total,N - total); + } +#else /* WITH_SSL */ + ret = read(fd,buffer + total,N - total); +#endif /* WITH_SSL */ + + if (ret == 0) + { + DEBUG(10,("read_socket_data: recv of %d returned 0. Error = %s\n", (int)(N - total), strerror(errno) )); + smb_read_error = READ_EOF; + return 0; + } + if (ret == -1) + { + DEBUG(0,("read_socket_data: recv failure for %d. Error = %s\n", (int)(N - total), strerror(errno) )); + smb_read_error = READ_ERROR; + return -1; + } + total += ret; + } + return (ssize_t)total; +} + +/**************************************************************************** + Write data to a fd. +****************************************************************************/ + +ssize_t write_data(int fd,char *buffer,size_t N) +{ + size_t total=0; + ssize_t ret; + + while (total < N) + { +#ifdef WITH_SSL + if(fd == sslFd){ + ret = SSL_write(ssl,buffer + total,N - total); + }else{ + ret = write(fd,buffer + total,N - total); + } +#else /* WITH_SSL */ + ret = write(fd,buffer + total,N - total); +#endif /* WITH_SSL */ + + if (ret == -1) { + DEBUG(0,("write_data: write failure. Error = %s\n", strerror(errno) )); + return -1; + } + if (ret == 0) return total; + + total += ret; + } + return (ssize_t)total; +} + +/**************************************************************************** + Write data to a socket - use send rather than write. +****************************************************************************/ + +ssize_t write_socket_data(int fd,char *buffer,size_t N) +{ + size_t total=0; + ssize_t ret; + + while (total < N) + { +#ifdef WITH_SSL + if(fd == sslFd){ + ret = SSL_write(ssl,buffer + total,N - total); + }else{ + ret = send(fd,buffer + total,N - total, 0); + } +#else /* WITH_SSL */ + ret = send(fd,buffer + total,N - total,0); +#endif /* WITH_SSL */ + + if (ret == -1) { + DEBUG(0,("write_socket_data: write failure. Error = %s\n", strerror(errno) )); + return -1; + } + if (ret == 0) return total; + + total += ret; + } + return (ssize_t)total; +} + +/**************************************************************************** +write to a socket +****************************************************************************/ + +ssize_t write_socket(int fd,char *buf,size_t len) +{ + ssize_t ret=0; + + DEBUG(6,("write_socket(%d,%d)\n",fd,(int)len)); + ret = write_socket_data(fd,buf,len); + + DEBUG(6,("write_socket(%d,%d) wrote %d\n",fd,(int)len,(int)ret)); + if(ret <= 0) + DEBUG(0,("write_socket: Error writing %d bytes to socket %d: ERRNO = %s\n", + (int)len, fd, strerror(errno) )); + + return(ret); +} + +/**************************************************************************** +read 4 bytes of a smb packet and return the smb length of the packet +store the result in the buffer +This version of the function will return a length of zero on receiving +a keepalive packet. +timeout is in milliseconds. +****************************************************************************/ + +static ssize_t read_smb_length_return_keepalive(int fd,char *inbuf,unsigned int timeout) +{ + ssize_t len=0; + int msg_type; + BOOL ok = False; + + while (!ok) + { + if (timeout > 0) + ok = (read_socket_with_timeout(fd,inbuf,4,4,timeout) == 4); + else + ok = (read_socket_data(fd,inbuf,4) == 4); + + if (!ok) + return(-1); + + len = smb_len(inbuf); + msg_type = CVAL(inbuf,0); + + if (msg_type == SMBkeepalive) + DEBUG(5,("Got keepalive packet\n")); + } + + DEBUG(10,("got smb length of %d\n",len)); + + return(len); +} + +/**************************************************************************** +read 4 bytes of a smb packet and return the smb length of the packet +store the result in the buffer. This version of the function will +never return a session keepalive (length of zero). +timeout is in milliseconds. +****************************************************************************/ + +ssize_t read_smb_length(int fd,char *inbuf,unsigned int timeout) +{ + ssize_t len; + + for(;;) + { + len = read_smb_length_return_keepalive(fd, inbuf, timeout); + + if(len < 0) + return len; + + /* Ignore session keepalives. */ + if(CVAL(inbuf,0) != SMBkeepalive) + break; + } + + DEBUG(10,("read_smb_length: got smb length of %d\n",len)); + + return len; +} + +/**************************************************************************** + read an smb from a fd. Note that the buffer *MUST* be of size + BUFFER_SIZE+SAFETY_MARGIN. + The timeout is in milliseconds. + This function will return on a + receipt of a session keepalive packet. +****************************************************************************/ + +BOOL receive_smb(int fd,char *buffer, unsigned int timeout) +{ + ssize_t len,ret; + + smb_read_error = 0; + + memset(buffer,'\0',smb_size + 100); + + len = read_smb_length_return_keepalive(fd,buffer,timeout); + if (len < 0) { + DEBUG(10,("receive_smb: length < 0!\n")); + + /* + * Correct fix. smb_read_error may have already been + * set. Only set it here if not already set. Global + * variables still suck :-). JRA. + */ + + if (smb_read_error == 0) + smb_read_error = READ_ERROR; + return False; + } + + /* + * A WRITEX with CAP_LARGE_WRITEX can be 64k worth of data plus 65 bytes + * of header. Don't print the error if this fits.... JRA. + */ + + if (len > (BUFFER_SIZE + LARGE_WRITEX_HDR_SIZE)) { + DEBUG(0,("Invalid packet length! (%d bytes).\n",len)); + if (len > BUFFER_SIZE + (SAFETY_MARGIN/2)) { + + /* + * Correct fix. smb_read_error may have already been + * set. Only set it here if not already set. Global + * variables still suck :-). JRA. + */ + + if (smb_read_error == 0) + smb_read_error = READ_ERROR; + return False; + } + } + + if(len > 0) { + ret = read_socket_data(fd,buffer+4,len); + if (ret != len) { + if (smb_read_error == 0) + smb_read_error = READ_ERROR; + return False; + } + } + + return(True); +} + +/**************************************************************************** + read an smb from a fd ignoring all keepalive packets. Note that the buffer + *MUST* be of size BUFFER_SIZE+SAFETY_MARGIN. + The timeout is in milliseconds + + This is exactly the same as receive_smb except that it never returns + a session keepalive packet (just as receive_smb used to do). + receive_smb was changed to return keepalives as the oplock processing means this call + should never go into a blocking read. +****************************************************************************/ + +BOOL client_receive_smb(int fd,char *buffer, unsigned int timeout) +{ + BOOL ret; + + for(;;) + { + ret = receive_smb(fd, buffer, timeout); + + if (!ret) + { + DEBUG(10,("client_receive_smb failed\n")); + show_msg(buffer); + return ret; + } + + /* Ignore session keepalive packets. */ + if(CVAL(buffer,0) != SMBkeepalive) + break; + } + show_msg(buffer); + return ret; +} + +/**************************************************************************** + send an smb to a fd +****************************************************************************/ + +BOOL send_smb(int fd,char *buffer) +{ + size_t len; + size_t nwritten=0; + ssize_t ret; + len = smb_len(buffer) + 4; + + while (nwritten < len) { + ret = write_socket(fd,buffer+nwritten,len - nwritten); + if (ret <= 0) { + DEBUG(0,("Error writing %d bytes to client. %d. (%s)\n", + (int)len,(int)ret, strerror(errno) )); + return False; + } + nwritten += ret; + } + + return True; +} + +/**************************************************************************** +send a single packet to a port on another machine +****************************************************************************/ + +BOOL send_one_packet(char *buf,int len,struct in_addr ip,int port,int type) +{ + BOOL ret; + int out_fd; + struct sockaddr_in sock_out; + + /* create a socket to write to */ + out_fd = socket(AF_INET, type, 0); + if (out_fd == -1) + { + DEBUG(0,("socket failed")); + return False; + } + + /* set the address and port */ + memset((char *)&sock_out,'\0',sizeof(sock_out)); + putip((char *)&sock_out.sin_addr,(char *)&ip); + sock_out.sin_port = htons( port ); + sock_out.sin_family = AF_INET; + + if (DEBUGLEVEL > 0) + DEBUG(3,("sending a packet of len %d to (%s) on port %d of type %s\n", + len,inet_ntoa(ip),port,type==SOCK_DGRAM?"DGRAM":"STREAM")); + + /* send it */ + ret = (sendto(out_fd,buf,len,0,(struct sockaddr *)&sock_out,sizeof(sock_out)) >= 0); + + if (!ret) + DEBUG(0,("Packet send to %s(%d) failed ERRNO=%s\n", + inet_ntoa(ip),port,strerror(errno))); + + close(out_fd); + return(ret); +} + +/**************************************************************************** + 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 ) { + if( DEBUGLVL(0) ) { + dbgtext( "open_socket_in(): socket() call failed: " ); + dbgtext( "%s\n", strerror( errno ) ); + } + return -1; + } + + /* This block sets/clears the SO_REUSEADDR and possibly SO_REUSEPORT. */ + { + int val = rebind ? 1 : 0; + if( setsockopt(res,SOL_SOCKET,SO_REUSEADDR,(char *)&val,sizeof(val)) == -1 ) { + if( DEBUGLVL( dlevel ) ) { + dbgtext( "open_socket_in(): setsockopt: " ); + dbgtext( "SO_REUSEADDR = %s ", val?"True":"False" ); + dbgtext( "on port %d failed ", port ); + dbgtext( "with error = %s\n", strerror(errno) ); + } + } +#ifdef SO_REUSEPORT + if( setsockopt(res,SOL_SOCKET,SO_REUSEPORT,(char *)&val,sizeof(val)) == -1 ) { + if( DEBUGLVL( dlevel ) ) { + dbgtext( "open_socket_in(): setsockopt: "); + dbgtext( "SO_REUSEPORT = %s ", val?"True":"False" ); + dbgtext( "on port %d failed ", port ); + dbgtext( "with error = %s\n", strerror(errno) ); + } + } +#endif /* SO_REUSEPORT */ + } + + /* now we've got a socket - we need to bind it */ + if( bind( res, (struct sockaddr *)&sock, sizeof(sock) ) == -1 ) { + if( DEBUGLVL(dlevel) && (port == SMB_PORT || port == NMB_PORT) ) { + dbgtext( "bind failed on port %d ", port ); + dbgtext( "socket_addr = %s.\n", inet_ntoa( sock.sin_addr ) ); + dbgtext( "Error = %s\n", strerror(errno) ); + } + close( res ); + return( -1 ); + } + + DEBUG( 3, ( "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; + + addr = interpret_addr2(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; + + if (connect(res,(struct sockaddr *)&sock_out,sizeof(sock_out))) { + close(res); + return -1; + } + + return res; +} + + +/* the following 3 client_*() functions are nasty ways of allowing + some generic functions to get info that really should be hidden in + particular modules */ +static int client_fd = -1; + +void client_setfd(int fd) +{ + client_fd = fd; +} + +char *client_name(void) +{ + return get_socket_name(client_fd); +} + +char *client_addr(void) +{ + return get_socket_addr(client_fd); +} + +/******************************************************************* + 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], (caddr_t) & 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(int fd) +{ + static pstring name_buf; + static fstring 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()) { + return get_socket_addr(fd); + } + + p = get_socket_addr(fd); + + /* it might be the same as the last one - save some DNS work */ + if (strcmp(p, addr_buf) == 0) return name_buf; + + pstrcpy(name_buf,"UNKNOWN"); + if (fd == -1) return name_buf; + + fstrcpy(addr_buf, p); + + addr = *interpret_addr2(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)); + pstrcpy(name_buf, p); + } else { + pstrcpy(name_buf,(char *)hp->h_name); + if (!matchname(name_buf, addr)) { + DEBUG(0,("Matchname failed on %s %s\n",name_buf,p)); + pstrcpy(name_buf,"UNKNOWN"); + } + } + + alpha_strcpy(name_buf, name_buf, "_-.", sizeof(name_buf)); + if (strstr(name_buf,"..")) { + pstrcpy(name_buf, "UNKNOWN"); + } + + return name_buf; +} + +/******************************************************************* + return the IP addr of the remote end of a socket as a string + ******************************************************************/ +char *get_socket_addr(int fd) +{ + struct sockaddr sa; + struct sockaddr_in *sockin = (struct sockaddr_in *) (&sa); + int length = sizeof(sa); + static fstring addr_buf; + + fstrcpy(addr_buf,"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; + } + + fstrcpy(addr_buf,(char *)inet_ntoa(sockin->sin_addr)); + + return addr_buf; +} + +/******************************************************************* + opens and connects to a unix pipe socket + ******************************************************************/ +int open_pipe_sock(char *path) +{ + int sock; + struct sockaddr_un sa; + + sock = socket(AF_UNIX, SOCK_STREAM, 0); + + if (sock < 0) + { + DEBUG(0, ("unix socket open failed\n")); + return sock; + } + + ZERO_STRUCT(sa); + sa.sun_family = AF_UNIX; + safe_strcpy(sa.sun_path, path, sizeof(sa.sun_path)-1); + + DEBUG(10, ("socket open succeeded. file name: %s\n", sa.sun_path)); + + if (connect(sock, (struct sockaddr*) &sa, sizeof(sa)) < 0) + { + DEBUG(0,("socket connect to %s failed\n", sa.sun_path)); + close(sock); + return -1; + } + + return sock; +} + +/******************************************************************* + Create protected unix domain socket. + + some unixen cannot set permissions on a ux-dom-sock, so we + have to make sure that the directory contains the protection + permissions, instead. + ******************************************************************/ +int create_pipe_sock(const char *socket_dir, + const char *socket_name, + mode_t dir_perms) +{ + struct sockaddr_un sunaddr; + struct stat st; + int sock; + mode_t old_umask; + pstring path; + + /* Create the socket directory or reuse the existing one */ + + if (lstat(socket_dir, &st) == -1) { + + if (errno == ENOENT) { + + /* Create directory */ + + if (mkdir(socket_dir, dir_perms) == -1) { + DEBUG(0, ("error creating socket directory " + "%s: %s\n", socket_dir, + strerror(errno))); + return -1; + } + + } else { + + DEBUG(0, ("lstat failed on socket directory %s: %s\n", + socket_dir, strerror(errno))); + return -1; + } + + } else { + + /* Check ownership and permission on existing directory */ + + if (!S_ISDIR(st.st_mode)) { + DEBUG(0, ("socket directory %s isn't a directory\n", + socket_dir)); + return -1; + } + + if ((st.st_uid != sec_initial_uid()) || + ((st.st_mode & 0777) != dir_perms)) { + DEBUG(0, ("invalid permissions on socket directory " + "%s\n", socket_dir)); + return -1; + } + } + + /* Create the socket file */ + + old_umask = umask(0); + + sock = socket(AF_UNIX, SOCK_STREAM, 0); + + if (sock == -1) { + perror("socket"); + umask(old_umask); + return -1; + } + + snprintf(path, sizeof(path), "%s/%s", socket_dir, socket_name); + + unlink(path); + memset(&sunaddr, 0, sizeof(sunaddr)); + sunaddr.sun_family = AF_UNIX; + safe_strcpy(sunaddr.sun_path, path, sizeof(sunaddr.sun_path)-1); + + if (bind(sock, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) == -1) { + DEBUG(0, ("bind failed on pipe socket %s: %s\n", + path, + strerror(errno))); + close(sock); + umask(old_umask); + return -1; + } + + if (listen(sock, 5) == -1) { + DEBUG(0, ("listen failed on pipe socket %s: %s\n", + path, + strerror(errno))); + close(sock); + umask(old_umask); + return -1; + } + + umask(old_umask); + + /* Success! */ + + return sock; +} + +/******************************************************************* +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]; +} diff --git a/source3/lib/util_str.c b/source3/lib/util_str.c new file mode 100644 index 0000000000..6fdca658cd --- /dev/null +++ b/source3/lib/util_str.c @@ -0,0 +1,1003 @@ +/* + 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(char **ptr,char *buff,char *sep, size_t bufsize) +{ + 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(char **ptr,char *buff,char *sep, size_t bufsize) +{ + BOOL ret; + if (!ptr) ptr = &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, 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 +********************************************************************/ +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. + ******************************************************************/ +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 "normal" form +********************************************************************/ +void strnorm(char *s) +{ + extern int case_default; + if (case_default == CASE_UPPER) + strupper(s); + else + strlower(s); +} + +/******************************************************************* +check if a string is in "normal" case +********************************************************************/ +BOOL strisnormal(char *s) +{ + extern int case_default; + if (case_default == CASE_UPPER) + return(!strhaslower(s)); + + return(!strhasupper(s)); +} + + +/**************************************************************************** + string replace + NOTE: oldc and newc must be 7 bit characters +****************************************************************************/ +void string_replace(char *s,char oldc,char newc) +{ + push_ucs2(NULL, tmpbuf,s, sizeof(tmpbuf), STR_TERMINATE); + string_replace_w(tmpbuf, UCS2_CHAR(oldc), UCS2_CHAR(newc)); + pull_ucs2(NULL, s, tmpbuf, -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) +{ + push_ucs2(NULL, tmpbuf,s, sizeof(tmpbuf), STR_TERMINATE); + return strlen_w(tmpbuf); +} + +/******************************************************************* +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 (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; + } + + if (!src) { + *dest = 0; + return dest; + } + + len = strlen(src); + + if (len > maxlength) { + DEBUG(0,("ERROR: string overflow by %d in safe_strcpy [%.50s]\n", + (int)(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; + } + + 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)); + src_len = maxlength - dest_len; + } + + 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 (!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; + 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(char *s,char *list,BOOL casesensitive) +{ + pstring tok; + 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); +} + +/* this is used to prevent lots of mallocs of size 1 */ +static char *null_string = NULL; + +/**************************************************************************** +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); + + if (l == 0) + { + if (!null_string) { + if((null_string = (char *)malloc(1)) == NULL) { + DEBUG(0,("string_init: malloc fail for null_string.\n")); + return False; + } + *null_string = 0; + } + *dest = null_string; + } + else + { + (*dest) = (char *)malloc(l+1); + if ((*dest) == NULL) { + DEBUG(0,("Out of memory in string_init\n")); + return False; + } + + pstrcpy(*dest,src); + } + return(True); +} + +/**************************************************************************** +free a string value +****************************************************************************/ +void string_free(char **s) +{ + if (!s || !(*s)) return; + if (*s == null_string) + *s = NULL; + SAFE_FREE(*s); +} + +/**************************************************************************** +set a string value, allocing the space for the string, and deallocating any +existing space +****************************************************************************/ +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 no length check is performed +****************************************************************************/ +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 || !s) return; + + ls = (ssize_t)strlen(s); + lp = (ssize_t)strlen(pattern); + li = (ssize_t)strlen(insert); + + if (!*pattern) return; + + while (lp <= ls && (p = strstr(s,pattern))) { + if (len && (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 allows for any character to be substituted. +Use with caution! +if len==0 then no length check is performed +****************************************************************************/ +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; + + while (lp <= ls && (p = strstr(s,pattern))) { + if (len && (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 allocate unicode string. +len is the number of bytes, not chars + similar to string_sub() but allows for any character to be substituted. + Use with caution! + if len==0 then no length check is performed +****************************************************************************/ + +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, *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) { + 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 +****************************************************************************/ +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); + } + + 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); +} + +/******************************************************************* + 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); + } + + 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); +} + +/* + 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; + int i; + for (i=0;s[i] && i<n;i++) ; + + ret = malloc(i+1); + if (!ret) return NULL; + memcpy(ret, s, i); + ret[i] = 0; + return ret; +} +#endif diff --git a/source3/lib/util_unistr.c b/source3/lib/util_unistr.c new file mode 100644 index 0000000000..a1cff26169 --- /dev/null +++ b/source3/lib/util_unistr.c @@ -0,0 +1,777 @@ +/* + 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; + + if (initialised) return; + initialised = 1; + + upcase_table = map_file(lib_path("upcase.dat"), 0x20000); + lowcase_table = map_file(lib_path("lowcase.dat"), 0x20000); + + /* 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); + 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); + 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 +********************************************************************/ +void init_valid_table(void) +{ + static int initialised; + static int mapped_file; + int i; + const char *allowed = ".!#$%&'()_-@^`~"; + + if (initialised && mapped_file) return; + initialised = 1; + + valid_table = map_file(lib_path("valid.dat"), 0x10000); + if (valid_table) { + mapped_file = 1; + return; + } + + if (valid_table) free(valid_table); + + DEBUG(2,("creating default valid table\n")); + valid_table = malloc(0x10000); + 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(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); +} + +/******************************************************************* + Return a DOS codepage version of a little-endian unicode string. + len is the filename length (ignoring any terminating zero) in uin16 + units. Always null terminates. + Hack alert: uses fixed buffer(s). +********************************************************************/ +char *dos_unistrn2(const uint16 *src, int len) +{ + static char lbufs[8][MAXUNI]; + static int nexti; + char *lbuf = lbufs[nexti]; + nexti = (nexti+1)%8; + pull_ucs2(NULL, lbuf, src, MAXUNI-3, len*2, STR_NOALIGN); + return lbuf; +} + +/******************************************************************* + 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); +} + + +/******************************************************************* + 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; *src++; len++) ; + + 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; *src++ && (len < max); len++) ; + + return len; +} + +/******************************************************************* +wide strchr() +********************************************************************/ +smb_ucs2_t *strchr_w(const smb_ucs2_t *s, smb_ucs2_t c) +{ + while (*s != 0) { + if (c == *s) return (smb_ucs2_t *)s; + s++; + } + return NULL; +} + +smb_ucs2_t *strchr_wa(const smb_ucs2_t *s, char c) +{ + return strchr_w(s, UCS2_CHAR(c)); +} + +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 (smb_ucs2_t *)p; + } while (p-- != s); + return NULL; +} + +/******************************************************************* +wide strstr() +********************************************************************/ +smb_ucs2_t *strstr_w(const smb_ucs2_t *s, const smb_ucs2_t *ins) +{ + smb_ucs2_t *r; + size_t slen, inslen; + + if (!s || !*s || !ins || !*ins) return NULL; + slen = strlen_w(s); + inslen = strlen_w(ins); + r = (smb_ucs2_t *)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; +} + +/******************************************************************* + convert a string to "normal" form +********************************************************************/ +void strnorm_w(smb_ucs2_t *s) +{ + extern int case_default; + if (case_default == CASE_UPPER) + strupper_w(s); + else + strlower_w(s); +} + +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; +} + +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 (smb_ucs2_t *)s; + s++; + } + return NULL; +} + +smb_ucs2_t *strstr_wa(const smb_ucs2_t *s, const char *ins) +{ + smb_ucs2_t *r; + size_t slen, inslen; + + if (!s || !*s || !ins || !*ins) return NULL; + slen = strlen_w(s); + inslen = strlen(ins); + r = (smb_ucs2_t *)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; +} diff --git a/source3/lib/wins_srv.c b/source3/lib/wins_srv.c new file mode 100644 index 0000000000..0181ee0956 --- /dev/null +++ b/source3/lib/wins_srv.c @@ -0,0 +1,339 @@ +/* + Unix SMB/CIFS implementation. + Samba utility functions + Copyright (C) Andrew Tridgell 1992-1998 + Copyright (C) Christopher R. Hertel 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" + +/* -------------------------------------------------------------------------- ** + * Discussion... + * + * This module implements WINS failover. + * + * Microsoft's WINS servers provide a feature called WINS replication, + * which synchronises the WINS name databases between two or more servers. + * This means that the two servers can be used interchangably (more or + * less). WINS replication is particularly useful if you are trying to + * synchronise the WINS namespace between servers in remote locations, or + * if your WINS servers tend to crash a lot. + * + * WINS failover allows the client to 'switch' to a different WINS server + * if the current WINS server mysteriously disappears. On Windows + * systems, this is typically represented as 'primary' and 'secondary' + * WINS servers. + * + * Failover only works if the WINS servers are synced. If they are not, + * then + * a) if the primary WINS server never fails the client will never 'see' + * the secondary (or tertiary or...) WINS server name space. + * b) if the primary *does* fail, the client will be entering an + * unfamiliar namespace. The client itself will not be registered in + * that namespace and any names which match names in the previous + * space will likely resolve to different host IP addresses. + * + * One key thing to remember regarding WINS failover is that Samba does + * not (yet) implement WINS replication. For those interested, sniff port + * 42 (TCP? UDP? ...dunno off hand) and see what two MS WINS servers do. + * + * At this stage, only failover is implemented. The next thing is to add + * support for multi-WINS server registration and query (multi-membership). + * + * Multi-membership is a little wierd. The idea is that the client can + * register itself with multiple non-replicated WINS servers, and query + * all of those servers (in a prescribed sequence) to resolve a name. + * + * The implications of multi-membership are not quite clear. Worth + * trying, I suppose. Changes will be needed in the name query and + * registration code to accomodate this feature. Also, there will need to + * be some sort of syntax extension for the 'wins server' parameter in + * smb.conf. I'm thinking that a colon could be used as a separator. + * + * Of course, for each WINS namespace there might be multiple, synced WINS + * servers. The change to this module would likely be the addition of a + * linked list of linked lists. + * + * crh@samba.org + */ + +/* -------------------------------------------------------------------------- ** + * Defines... + * + * NECROMANCYCLE - The dead server retry period, in seconds. When a WINS + * server is declared dead, wait this many seconds before + * attempting to communicate with it. + */ + +#define NECROMANCYCLE 600 /* 600 seconds == 10 minutes. */ + +/* -------------------------------------------------------------------------- ** + * Typedefs... + */ + +typedef struct + { + ubi_slNode node; /* Linked list node structure. */ + time_t mourning; /* If > current time then server is dead, Jim. */ + char *server; /* DNS name or IP of NBNS server to query. */ + struct in_addr ip_addr; /* Cache translated IP. */ + } list_entry; + +/* -------------------------------------------------------------------------- ** + * Private, static variables. + */ + +static ubi_slNewList( wins_srv_list ); + +/* -------------------------------------------------------------------------- ** + * Functions... + */ + + +BOOL wins_srv_load_list( char *src ) + /* ------------------------------------------------------------------------ ** + * Create or recreate the linked list of failover WINS servers. + * + * Input: src - String of DNS names and/or IP addresses delimited by the + * characters listed in LIST_SEP (see include/local.h). + * + * Output: True if at least one name or IP could be parsed out of the + * list, else False. + * + * Notes: There is no syntax checking done on the names or IPs. We do + * check to see if the field is an IP, in which case we copy it + * to the ip_addr field of the entry. Don't bother to to a host + * name lookup on all names now. They're done as needed in + * wins_srv_ip(). + * ------------------------------------------------------------------------ ** + */ + { + list_entry *entry; + char *p = src; + pstring wins_id_bufr; + unsigned long count; + + /* Empty the list. */ + while( NULL != (entry =(list_entry *)ubi_slRemHead( wins_srv_list )) ) + { + SAFE_FREE( entry->server ); + SAFE_FREE( entry ); + } + (void)ubi_slInitList( wins_srv_list ); /* shouldn't be needed */ + + /* Parse out the DNS names or IP addresses of the WINS servers. */ + DEBUG( 4, ("wins_srv_load_list(): Building WINS server list:\n") ); + while( next_token( &p, wins_id_bufr, LIST_SEP, sizeof( wins_id_bufr ) ) ) + { + entry = (list_entry *)malloc( sizeof( list_entry ) ); + if( NULL == entry ) + { + DEBUG( 0, ("wins_srv_load_list(): malloc(list_entry) failed.\n") ); + } + else + { + entry->mourning = 0; + /* Create a copy of the server name and store it in the list. */ + if( NULL == (entry->server = strdup( wins_id_bufr )) ) + { + SAFE_FREE( entry ); + DEBUG( 0, + ("wins_srv_load_list(): strdup(\"%s\") failed.\n", wins_id_bufr) ); + } + else + { + /* Add server to list. + * If the server name was actually an IP address we will store that + * too. It it was a DNS name, we will wait until we need to use + * the WINS server before doing the DNS lookup. Since this may be + * a list, and since we will reload the list whenever smb.conf is + * reloaded, there's no point in trying to look up names until we + * need them. ...of course, once we do the lookup we will cache + * the result. See wins_srv_ip(). + */ + if( is_ipaddress( wins_id_bufr ) ) + entry->ip_addr = *interpret_addr2( wins_id_bufr ); + else + entry->ip_addr = *interpret_addr2( "0.0.0.0" ); + (void)ubi_slAddTail( wins_srv_list, entry ); + DEBUGADD( 4, ("%s,\n", wins_id_bufr) ); + } + } + } + + count = ubi_slCount( wins_srv_list ); + DEBUGADD( 4, + ( "%d WINS server%s listed.\n", (int)count, (1==count)?"":"s" ) ); + + return( (count > 0) ? True : False ); + } /* wins_srv_load_list */ + + +struct in_addr wins_srv_ip( void ) + /* ------------------------------------------------------------------------ ** + * Return the IP address of an NBNS (WINS) server thought to be active. + * + * Input: none. + * + * Output: An IP address in struct in_addr form. + * + * Notes: This function will return the IP address of the first available + * NBNS (WINS) server. The order of the list is determined in + * smb.conf. If all of the WINS servers have been marked 'dead' + * then the zero IP (0.0.0.0) is returned. The zero IP is not a + * valid Unicast address on any system. + * + * ------------------------------------------------------------------------ ** + */ + { + time_t now = time(NULL); + list_entry *entry = (list_entry *)ubi_slFirst( wins_srv_list ); + + while( NULL != entry ) + { + if( now >= entry->mourning ) /* Found a live one. */ + { + /* If we don't have the IP, look it up. */ + if( is_zero_ip( entry->ip_addr ) ) + entry->ip_addr = *interpret_addr2( entry->server ); + + /* If we still don't have the IP then kill it, else return it. */ + if( is_zero_ip( entry->ip_addr ) ) + entry->mourning = now + NECROMANCYCLE; + else + return( entry->ip_addr ); + } + entry = (list_entry *)ubi_slNext( entry ); + } + + /* If there are no live entries, return the zero IP. */ + return( *interpret_addr2( "0.0.0.0" ) ); + } /* wins_srv_ip */ + + +char *wins_srv_name( void ) + /* ------------------------------------------------------------------------ ** + * Return the name of first live WINS server in the list. + * + * Input: none. + * + * Output: A pointer to a string containing either the DNS name or IP + * address of the WINS server as given in the WINS SERVER + * parameter in smb.conf, or NULL if no (live) WINS servers are + * in the list. + * + * Notes: This function will return the name of the first available + * NBNS (WINS) server. The order of the list is determined in + * smb.conf. If all of the WINS servers have been marked 'dead' + * then NULL is returned. + * + * - This function does not verify that the name can be mapped to + * an IP address, or that the WINS server is running. + * + * ------------------------------------------------------------------------ ** + */ + { + time_t now = time(NULL); + list_entry *entry = (list_entry *)ubi_slFirst( wins_srv_list ); + + while( NULL != entry ) + { + if( now >= entry->mourning ) + return( entry->server ); /* Found a live one. */ + entry = (list_entry *)ubi_slNext( entry ); + } + + /* If there are no live entries, return NULL. */ + return( NULL ); + } /* wins_srv_name */ + + +void wins_srv_died( struct in_addr boothill_ip ) + /* ------------------------------------------------------------------------ ** + * Called to indicate that a specific WINS server has died. + * + * Input: boothill_ip - IP address of an NBNS (WINS) server that has + * failed. + * + * Notes: This function marks the record 'dead' for NECROMANCYCLE + * seconds. + * + * ------------------------------------------------------------------------ ** + */ + { + list_entry *entry; + + if( is_zero_ip( boothill_ip ) ) + { + DEBUG( 4, ("wins_srv_died(): Invalid request to mark zero IP down.\n") ); + return; + } + + entry = (list_entry *)ubi_slFirst( wins_srv_list ); + while( NULL != entry ) + { + /* Match based on IP. */ + if( ip_equal( boothill_ip, entry->ip_addr ) ) + { + entry->mourning = time(NULL) + NECROMANCYCLE; + entry->ip_addr.s_addr = 0; /* Force a re-lookup at re-birth. */ + DEBUG( 2, ( "wins_srv_died(): WINS server %s appears to be down.\n", + entry->server ) ); + return; + } + entry = (list_entry *)ubi_slNext( entry ); + } + + if( DEBUGLVL( 1 ) ) + { + dbgtext( "wins_srv_died(): Could not mark WINS server %s down.\n", + inet_ntoa( boothill_ip ) ); + dbgtext( "Address not found in server list.\n" ); + } + } /* wins_srv_died */ + + +unsigned long wins_srv_count( void ) + /* ------------------------------------------------------------------------ ** + * Return the total number of entries in the list, dead or alive. + * ------------------------------------------------------------------------ ** + */ + { + unsigned long count = ubi_slCount( wins_srv_list ); + + if( DEBUGLVL( 8 ) ) + { + list_entry *entry = (list_entry *)ubi_slFirst( wins_srv_list ); + time_t now = time(NULL); + + dbgtext( "wins_srv_count: WINS status: %ld servers.\n", count ); + while( NULL != entry ) + { + dbgtext( " %s <%s>: ", entry->server, inet_ntoa( entry->ip_addr ) ); + if( now >= entry->mourning ) + dbgtext( "alive\n" ); + else + dbgtext( "dead for %d more seconds\n", (int)(entry->mourning - now) ); + + entry = (list_entry *)ubi_slNext( entry ); + } + } + + return( count ); + } /* wins_srv_count */ + +/* ========================================================================== */ diff --git a/source3/lib/xfile.c b/source3/lib/xfile.c new file mode 100644 index 0000000000..00ea6e5cac --- /dev/null +++ b/source3/lib/xfile.c @@ -0,0 +1,339 @@ +/* + 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" + +static XFILE _x_stdin = { 0, NULL, NULL, 0, 0, O_RDONLY, X_IOFBF, 0 }; +static XFILE _x_stdout = { 1, NULL, NULL, 0, 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 XBUFSIZE BUFSIZ + +#define X_FLAG_EOF 1 +#define X_FLAG_ERROR 2 + +/* 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() */ +int x_fwrite(const void *p, size_t size, size_t nmemb, XFILE *f) +{ + int ret, 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) { + int 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-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; + len = vasprintf(&p, format, ap); + 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; +} |