diff options
Diffstat (limited to 'source3/lib/substitute.c')
-rw-r--r-- | source3/lib/substitute.c | 915 |
1 files changed, 915 insertions, 0 deletions
diff --git a/source3/lib/substitute.c b/source3/lib/substitute.c new file mode 100644 index 0000000000..acfe55d761 --- /dev/null +++ b/source3/lib/substitute.c @@ -0,0 +1,915 @@ +/* + Unix SMB/CIFS implementation. + string substitution functions + Copyright (C) Andrew Tridgell 1992-2000 + Copyright (C) Gerald Carter 2006 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + + +#include "includes.h" + +userdom_struct current_user_info; +fstring remote_proto="UNKNOWN"; + +/** + * Set the 'local' machine name + * @param local_name the name we are being called + * @param if this is the 'final' name for us, not be be changed again + */ + +static char *local_machine; + +void free_local_machine_name(void) +{ + SAFE_FREE(local_machine); +} + +bool set_local_machine_name(const char *local_name, bool perm) +{ + static bool already_perm = false; + char *tmp_local_machine = NULL; + char addr[INET6_ADDRSTRLEN]; + size_t len; + + tmp_local_machine = SMB_STRDUP(local_name); + if (!tmp_local_machine) { + return false; + } + trim_char(tmp_local_machine,' ',' '); + + /* + * Windows NT/2k uses "*SMBSERVER" and XP uses "*SMBSERV" + * arrggg!!! + */ + + if (strequal(tmp_local_machine, "*SMBSERVER") || + strequal(tmp_local_machine, "*SMBSERV") ) { + SAFE_FREE(local_machine); + local_machine = SMB_STRDUP(client_socket_addr(get_client_fd(), + addr, sizeof(addr)) ); + SAFE_FREE(tmp_local_machine); + return local_machine ? true : false; + } + + if (already_perm) { + return true; + } + + SAFE_FREE(local_machine); + len = strlen(tmp_local_machine); + local_machine = SMB_CALLOC_ARRAY(char, len+1); + if (!local_machine) { + SAFE_FREE(tmp_local_machine); + return false; + } + /* alpha_strcpy includes the space for the terminating nul. */ + alpha_strcpy(local_machine,tmp_local_machine, + SAFE_NETBIOS_CHARS,len+1); + strlower_m(local_machine); + SAFE_FREE(tmp_local_machine); + + already_perm = perm; + + return true; +} + +const char *get_local_machine_name(void) +{ + if (!local_machine || !*local_machine) { + return global_myname(); + } + + return local_machine; +} + +/** + * Set the 'remote' machine name + * @param remote_name the name our client wants to be called by + * @param if this is the 'final' name for them, not be be changed again + */ + +static char *remote_machine; + +bool set_remote_machine_name(const char *remote_name, bool perm) +{ + static bool already_perm = False; + char *tmp_remote_machine; + size_t len; + + if (already_perm) { + return true; + } + + tmp_remote_machine = SMB_STRDUP(remote_name); + if (!tmp_remote_machine) { + return false; + } + trim_char(tmp_remote_machine,' ',' '); + + SAFE_FREE(remote_machine); + len = strlen(tmp_remote_machine); + remote_machine = SMB_CALLOC_ARRAY(char, len+1); + if (!remote_machine) { + SAFE_FREE(tmp_remote_machine); + return false; + } + + /* alpha_strcpy includes the space for the terminating nul. */ + alpha_strcpy(remote_machine,tmp_remote_machine, + SAFE_NETBIOS_CHARS,len+1); + strlower_m(remote_machine); + SAFE_FREE(tmp_remote_machine); + + already_perm = perm; + + return true; +} + +const char *get_remote_machine_name(void) +{ + return remote_machine ? remote_machine : ""; +} + +/******************************************************************* + Setup the string used by %U substitution. +********************************************************************/ + +static char *smb_user_name; + +void sub_set_smb_name(const char *name) +{ + char *tmp; + size_t len; + bool is_machine_account = false; + + /* don't let anonymous logins override the name */ + if (!name || !*name) { + return; + } + + tmp = SMB_STRDUP(name); + if (!tmp) { + return; + } + trim_char(tmp, ' ', ' '); + strlower_m(tmp); + + len = strlen(tmp); + + if (len == 0) { + SAFE_FREE(tmp); + return; + } + + /* long story but here goes....we have to allow usernames + ending in '$' as they are valid machine account names. + So check for a machine account and re-add the '$' + at the end after the call to alpha_strcpy(). --jerry */ + + if (tmp[len-1] == '$') { + is_machine_account = True; + } + + SAFE_FREE(smb_user_name); + smb_user_name = SMB_CALLOC_ARRAY(char, len+1); + if (!smb_user_name) { + SAFE_FREE(tmp); + return; + } + + /* alpha_strcpy includes the space for the terminating nul. */ + alpha_strcpy(smb_user_name, tmp, + SAFE_NETBIOS_CHARS, + len+1); + + SAFE_FREE(tmp); + + if (is_machine_account) { + len = strlen(smb_user_name); + smb_user_name[len-1] = '$'; + } +} + +static const char *get_smb_user_name(void) +{ + return smb_user_name ? smb_user_name : ""; +} + +/******************************************************************* + Setup the strings used by substitutions. Called per packet. Ensure + %U name is set correctly also. + + smb_name must be sanitized by alpha_strcpy +********************************************************************/ + +void set_current_user_info(const char *smb_name, const char *unix_name, + const char *full_name, const char *domain) +{ + fstrcpy(current_user_info.smb_name, smb_name); + fstrcpy(current_user_info.unix_name, unix_name); + fstrcpy(current_user_info.full_name, full_name); + fstrcpy(current_user_info.domain, domain); + + /* The following is safe as current_user_info.smb_name + * has already been sanitised in register_existing_vuid. */ + + sub_set_smb_name(current_user_info.smb_name); +} + +/******************************************************************* + Return the current active user name. +*******************************************************************/ + +const char *get_current_username(void) +{ + if (current_user_info.smb_name[0] == '\0' ) { + return get_smb_user_name(); + } + + return current_user_info.smb_name; +} + +/******************************************************************* + Given a pointer to a %$(NAME) in p and the whole string in str + expand it as an environment variable. + Return a new allocated and expanded string. + Based on code by Branko Cibej <branko.cibej@hermes.si> + When this is called p points at the '%' character. + May substitute multiple occurrencies of the same env var. +********************************************************************/ + +static char * realloc_expand_env_var(char *str, char *p) +{ + char *envname; + char *envval; + char *q, *r; + int copylen; + + if (p[0] != '%' || p[1] != '$' || p[2] != '(') { + return str; + } + + /* + * Look for the terminating ')'. + */ + + if ((q = strchr_m(p,')')) == NULL) { + DEBUG(0,("expand_env_var: Unterminated environment variable [%s]\n", p)); + return str; + } + + /* + * Extract the name from within the %$(NAME) string. + */ + + r = p + 3; + copylen = q - r; + + /* reserve space for use later add %$() chars */ + if ( (envname = (char *)SMB_MALLOC(copylen + 1 + 4)) == NULL ) { + return NULL; + } + + strncpy(envname,r,copylen); + envname[copylen] = '\0'; + + if ((envval = getenv(envname)) == NULL) { + DEBUG(0,("expand_env_var: Environment variable [%s] not set\n", envname)); + SAFE_FREE(envname); + return str; + } + + /* + * Copy the full %$(NAME) into envname so it + * can be replaced. + */ + + copylen = q + 1 - p; + strncpy(envname,p,copylen); + envname[copylen] = '\0'; + r = realloc_string_sub(str, envname, envval); + SAFE_FREE(envname); + + return r; +} + +/******************************************************************* +*******************************************************************/ + +static char *longvar_domainsid( void ) +{ + DOM_SID sid; + fstring tmp; + char *sid_string; + + if ( !secrets_fetch_domain_sid( lp_workgroup(), &sid ) ) { + return NULL; + } + + sid_string = SMB_STRDUP( sid_to_fstring( tmp, &sid ) ); + + if ( !sid_string ) { + DEBUG(0,("longvar_domainsid: failed to dup SID string!\n")); + } + + return sid_string; +} + +/******************************************************************* +*******************************************************************/ + +struct api_longvar { + const char *name; + char* (*fn)( void ); +}; + +static struct api_longvar longvar_table[] = { + { "DomainSID", longvar_domainsid }, + { NULL, NULL } +}; + +static char *get_longvar_val( const char *varname ) +{ + int i; + + DEBUG(7,("get_longvar_val: expanding variable [%s]\n", varname)); + + for ( i=0; longvar_table[i].name; i++ ) { + if ( strequal( longvar_table[i].name, varname ) ) { + return longvar_table[i].fn(); + } + } + + return NULL; +} + +/******************************************************************* + Expand the long smb.conf variable names given a pointer to a %(NAME). + Return the number of characters by which the pointer should be advanced. + When this is called p points at the '%' character. +********************************************************************/ + +static char *realloc_expand_longvar(char *str, char *p) +{ + fstring varname; + char *value; + char *q, *r; + int copylen; + + if ( p[0] != '%' || p[1] != '(' ) { + return str; + } + + /* Look for the terminating ')'.*/ + + if ((q = strchr_m(p,')')) == NULL) { + DEBUG(0,("realloc_expand_longvar: Unterminated environment variable [%s]\n", p)); + return str; + } + + /* Extract the name from within the %(NAME) string.*/ + + r = p+2; + copylen = MIN( (q-r), (sizeof(varname)-1) ); + strncpy(varname, r, copylen); + varname[copylen] = '\0'; + + if ((value = get_longvar_val(varname)) == NULL) { + DEBUG(0,("realloc_expand_longvar: Variable [%s] not set. Skipping\n", varname)); + return str; + } + + /* Copy the full %(NAME) into envname so it can be replaced.*/ + + copylen = MIN( (q+1-p),(sizeof(varname)-1) ); + strncpy( varname, p, copylen ); + varname[copylen] = '\0'; + r = realloc_string_sub(str, varname, value); + SAFE_FREE( value ); + + /* skip over the %(varname) */ + + return r; +} + +/******************************************************************* + Patch from jkf@soton.ac.uk + Added this to implement %p (NIS auto-map version of %H) +*******************************************************************/ + +static const char *automount_path(const char *user_name) +{ + TALLOC_CTX *ctx = talloc_tos(); + const char *server_path; + + /* use the passwd entry as the default */ + /* this will be the default if WITH_AUTOMOUNT is not used or fails */ + + server_path = talloc_strdup(ctx, get_user_home_dir(ctx, user_name)); + if (!server_path) { + return ""; + } + +#if (defined(HAVE_NETGROUP) && defined (WITH_AUTOMOUNT)) + + if (lp_nis_home_map()) { + const char *home_path_start; + char *automount_value = automount_lookup(ctx, user_name); + + if(automount_value && 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):"")); + server_path = talloc_strdup(ctx, + home_path_start+1); + if (!server_path) { + server_path = ""; + } + } + } 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 const char *automount_server(const char *user_name) +{ + TALLOC_CTX *ctx = talloc_tos(); + const char *server_name; + const char *local_machine_name = get_local_machine_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_name && *local_machine_name) { + server_name = talloc_strdup(ctx, local_machine_name); + } else { + server_name = talloc_strdup(ctx, global_myname()); + } + + if (!server_name) { + return ""; + } + +#if (defined(HAVE_NETGROUP) && defined (WITH_AUTOMOUNT)) + if (lp_nis_home_map()) { + char *p; + char *srv; + char *automount_value = automount_lookup(ctx, user_name); + if (!automount_value) { + return ""; + } + srv = talloc_strdup(ctx, automount_value); + if (!srv) { + return ""; + } + p = strchr_m(srv, ':'); + if (!p) { + return ""; + } + *p = '\0'; + server_name = srv; + DEBUG(5, ("NIS lookup succeeded. Home server %s\n", + server_name)); + } +#endif + + DEBUG(4,("Home server: %s\n", server_name)); + return server_name; +} + +/**************************************************************************** + Do some standard substitutions in a string. + len is the length in bytes of the space allowed in string str. If zero means + don't allow expansions. +****************************************************************************/ + +void standard_sub_basic(const char *smb_name, const char *domain_name, + char *str, size_t len) +{ + char *s; + + if ( (s = alloc_sub_basic( smb_name, domain_name, str )) != NULL ) { + strncpy( str, s, len ); + } + + SAFE_FREE( s ); + +} + +/**************************************************************************** + Do some standard substitutions in a string. + This function will return an allocated string that have to be freed. +****************************************************************************/ + +char *talloc_sub_basic(TALLOC_CTX *mem_ctx, const char *smb_name, + const char *domain_name, const char *str) +{ + char *a, *t; + + if ( (a = alloc_sub_basic(smb_name, domain_name, str)) == NULL ) { + return NULL; + } + t = talloc_strdup(mem_ctx, a); + SAFE_FREE(a); + return t; +} + +/**************************************************************************** +****************************************************************************/ + +char *alloc_sub_basic(const char *smb_name, const char *domain_name, + const char *str) +{ + char *b, *p, *s, *r, *a_string; + fstring pidstr, vnnstr; + char addr[INET6_ADDRSTRLEN]; + const char *local_machine_name = get_local_machine_name(); + TALLOC_CTX *tmp_ctx = NULL; + + /* workaround to prevent a crash while looking at bug #687 */ + + if (!str) { + DEBUG(0,("alloc_sub_basic: NULL source string! This should not happen\n")); + return NULL; + } + + a_string = SMB_STRDUP(str); + if (a_string == NULL) { + DEBUG(0, ("alloc_sub_basic: Out of memory!\n")); + return NULL; + } + + tmp_ctx = talloc_stackframe(); + + for (b = s = a_string; (p = strchr_m(s, '%')); s = a_string + (p - b)) { + + r = NULL; + b = a_string; + + switch (*(p+1)) { + case 'U' : + r = strdup_lower(smb_name); + if (r == NULL) { + goto error; + } + a_string = realloc_string_sub(a_string, "%U", r); + break; + case 'G' : { + struct passwd *pass; + r = SMB_STRDUP(smb_name); + if (r == NULL) { + goto error; + } + pass = Get_Pwnam_alloc(tmp_ctx, r); + if (pass != NULL) { + a_string = realloc_string_sub( + a_string, "%G", + gidtoname(pass->pw_gid)); + } + TALLOC_FREE(pass); + break; + } + case 'D' : + r = strdup_upper(domain_name); + if (r == NULL) { + goto error; + } + a_string = realloc_string_sub(a_string, "%D", r); + break; + case 'I' : { + int offset = 0; + client_addr(get_client_fd(), addr, sizeof(addr)); + if (strnequal(addr,"::ffff:",7)) { + offset = 7; + } + a_string = realloc_string_sub(a_string, "%I", + addr + offset); + break; + } + case 'i': + a_string = realloc_string_sub( a_string, "%i", + client_socket_addr(get_client_fd(), addr, sizeof(addr)) ); + break; + case 'L' : + if ( StrnCaseCmp(p, "%LOGONSERVER%", strlen("%LOGONSERVER%")) == 0 ) { + break; + } + if (local_machine_name && *local_machine_name) { + a_string = realloc_string_sub(a_string, "%L", local_machine_name); + } else { + a_string = realloc_string_sub(a_string, "%L", global_myname()); + } + break; + case 'N': + a_string = realloc_string_sub(a_string, "%N", automount_server(smb_name)); + break; + case 'M' : + a_string = realloc_string_sub(a_string, "%M", client_name(get_client_fd())); + break; + case 'R' : + a_string = realloc_string_sub(a_string, "%R", remote_proto); + break; + case 'T' : + a_string = realloc_string_sub(a_string, "%T", current_timestring(tmp_ctx, False)); + break; + case 'a' : + a_string = realloc_string_sub(a_string, "%a", + get_remote_arch_str()); + break; + case 'd' : + slprintf(pidstr,sizeof(pidstr)-1, "%d",(int)sys_getpid()); + a_string = realloc_string_sub(a_string, "%d", pidstr); + break; + case 'h' : + a_string = realloc_string_sub(a_string, "%h", myhostname()); + break; + case 'm' : + a_string = realloc_string_sub(a_string, "%m", + remote_machine + ? remote_machine + : ""); + break; + case 'v' : + a_string = realloc_string_sub(a_string, "%v", SAMBA_VERSION_STRING); + break; + case 'w' : + a_string = realloc_string_sub(a_string, "%w", lp_winbind_separator()); + break; + case '$' : + a_string = realloc_expand_env_var(a_string, p); /* Expand environment variables */ + break; + case '(': + a_string = realloc_expand_longvar( a_string, p ); + break; + case 'V' : + slprintf(vnnstr,sizeof(vnnstr)-1, "%u", get_my_vnn()); + a_string = realloc_string_sub(a_string, "%V", vnnstr); + break; + default: + break; + } + + p++; + SAFE_FREE(r); + + if (a_string == NULL) { + goto done; + } + } + + goto done; + +error: + SAFE_FREE(a_string); + +done: + TALLOC_FREE(tmp_ctx); + return a_string; +} + +/**************************************************************************** + Do some specific substitutions in a string. + This function will return an allocated string that have to be freed. +****************************************************************************/ + +char *talloc_sub_specified(TALLOC_CTX *mem_ctx, + const char *input_string, + const char *username, + const char *domain, + uid_t uid, + gid_t gid) +{ + char *a_string; + char *ret_string = NULL; + char *b, *p, *s; + TALLOC_CTX *tmp_ctx; + + if (!(tmp_ctx = talloc_new(mem_ctx))) { + DEBUG(0, ("talloc_new failed\n")); + return NULL; + } + + a_string = talloc_strdup(tmp_ctx, input_string); + if (a_string == NULL) { + DEBUG(0, ("talloc_sub_specified: Out of memory!\n")); + goto done; + } + + for (b = s = a_string; (p = strchr_m(s, '%')); s = a_string + (p - b)) { + + b = a_string; + + switch (*(p+1)) { + case 'U' : + a_string = talloc_string_sub( + tmp_ctx, a_string, "%U", username); + break; + case 'u' : + a_string = talloc_string_sub( + tmp_ctx, a_string, "%u", username); + break; + case 'G' : + if (gid != -1) { + a_string = talloc_string_sub( + tmp_ctx, a_string, "%G", + gidtoname(gid)); + } else { + a_string = talloc_string_sub( + tmp_ctx, a_string, + "%G", "NO_GROUP"); + } + break; + case 'g' : + if (gid != -1) { + a_string = talloc_string_sub( + tmp_ctx, a_string, "%g", + gidtoname(gid)); + } else { + a_string = talloc_string_sub( + tmp_ctx, a_string, "%g", "NO_GROUP"); + } + break; + case 'D' : + a_string = talloc_string_sub(tmp_ctx, a_string, + "%D", domain); + break; + case 'N' : + a_string = talloc_string_sub( + tmp_ctx, a_string, "%N", + automount_server(username)); + break; + default: + break; + } + + p++; + if (a_string == NULL) { + goto done; + } + } + + /* Watch out, using "mem_ctx" here, so all intermediate stuff goes + * away with the TALLOC_FREE(tmp_ctx) further down. */ + + ret_string = talloc_sub_basic(mem_ctx, username, domain, a_string); + + done: + TALLOC_FREE(tmp_ctx); + return ret_string; +} + +/**************************************************************************** +****************************************************************************/ + +static char *alloc_sub_advanced(const char *servicename, const char *user, + const char *connectpath, gid_t gid, + const char *smb_name, const char *domain_name, + const char *str) +{ + char *a_string, *ret_string; + char *b, *p, *s; + + a_string = SMB_STRDUP(str); + if (a_string == NULL) { + DEBUG(0, ("alloc_sub_advanced: Out of memory!\n")); + return NULL; + } + + for (b = s = a_string; (p = strchr_m(s, '%')); s = a_string + (p - b)) { + + b = a_string; + + switch (*(p+1)) { + case 'N' : + a_string = realloc_string_sub(a_string, "%N", automount_server(user)); + break; + case 'H': { + char *h; + if ((h = get_user_home_dir(talloc_tos(), user))) + a_string = realloc_string_sub(a_string, "%H", h); + TALLOC_FREE(h); + break; + } + case 'P': + a_string = realloc_string_sub(a_string, "%P", connectpath); + break; + case 'S': + a_string = realloc_string_sub(a_string, "%S", servicename); + break; + case 'g': + a_string = realloc_string_sub(a_string, "%g", gidtoname(gid)); + break; + case 'u': + a_string = realloc_string_sub(a_string, "%u", user); + 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': + a_string = realloc_string_sub(a_string, "%p", + automount_path(servicename)); + break; + + default: + break; + } + + p++; + if (a_string == NULL) { + return NULL; + } + } + + ret_string = alloc_sub_basic(smb_name, domain_name, a_string); + SAFE_FREE(a_string); + return ret_string; +} + +/* + * This obviously is inefficient and needs to be merged into + * alloc_sub_advanced... + */ + +char *talloc_sub_advanced(TALLOC_CTX *mem_ctx, + const char *servicename, const char *user, + const char *connectpath, gid_t gid, + const char *smb_name, const char *domain_name, + const char *str) +{ + char *a, *t; + + if (!(a = alloc_sub_advanced(servicename, user, connectpath, gid, + smb_name, domain_name, str))) { + return NULL; + } + t = talloc_strdup(mem_ctx, a); + SAFE_FREE(a); + return t; +} + + +void standard_sub_advanced(const char *servicename, const char *user, + const char *connectpath, gid_t gid, + const char *smb_name, const char *domain_name, + char *str, size_t len) +{ + char *s; + + s = alloc_sub_advanced(servicename, user, connectpath, + gid, smb_name, domain_name, str); + + if ( s ) { + strncpy( str, s, len ); + SAFE_FREE( s ); + } +} + +/**************************************************************************** + Do some standard substitutions in a string. +****************************************************************************/ + +char *standard_sub_conn(TALLOC_CTX *ctx, connection_struct *conn, const char *str) +{ + return talloc_sub_advanced(ctx, + lp_servicename(SNUM(conn)), + conn->server_info->unix_name, + conn->connectpath, + conn->server_info->utok.gid, + get_smb_user_name(), + "", + str); +} |