Index: samba/source/param/loadparm.c =================================================================== --- samba/source/param/loadparm.c.orig +++ samba/source/param/loadparm.c @@ -9,6 +9,7 @@ Copyright (C) Alexander Bokovoy 2002 Copyright (C) Stefan (metze) Metzmacher 2002 Copyright (C) Jim McDonough 2003 + Copyright (C) 2007 Apple Inc. All rights reserved. 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 @@ -307,6 +308,7 @@ typedef struct { BOOL bASUSupport; BOOL bUsershareOwnerOnly; BOOL bUsershareAllowGuests; + BOOL bUsershareAllowFullConfig; int restrict_anonymous; int name_cache_timeout; int client_signing; @@ -1249,6 +1251,7 @@ static struct parm_struct parm_table[] = {"root postexec", P_STRING, P_LOCAL, &sDefault.szRootPostExec, NULL, NULL, FLAG_ADVANCED | FLAG_SHARE | FLAG_PRINT}, {"available", P_BOOL, P_LOCAL, &sDefault.bAvailable, NULL, NULL, FLAG_BASIC | FLAG_ADVANCED | FLAG_SHARE | FLAG_PRINT}, {"usershare allow guests", P_BOOL, P_GLOBAL, &Globals.bUsershareAllowGuests, NULL, NULL, FLAG_ADVANCED}, + {"usershare allow full config", P_BOOL, P_GLOBAL, &Globals.bUsershareAllowFullConfig, NULL, NULL, FLAG_ADVANCED}, {"usershare max shares", P_INTEGER, P_GLOBAL, &Globals.iUsershareMaxShares, NULL, NULL, FLAG_ADVANCED}, {"usershare owner only", P_BOOL, P_GLOBAL, &Globals.bUsershareOwnerOnly, NULL, NULL, FLAG_ADVANCED}, {"usershare path", P_STRING, P_GLOBAL, &Globals.szUsersharePath, NULL, NULL, FLAG_ADVANCED}, @@ -1686,6 +1689,8 @@ static void init_globals(BOOL first_time Globals.bUsershareOwnerOnly = True; /* By default disallow guest access to usershares. */ Globals.bUsershareAllowGuests = False; + /* By default don't allow arbitrary usershare configurations. */ + Globals.bUsershareAllowFullConfig = False; } static TALLOC_CTX *lp_talloc; @@ -1943,6 +1948,7 @@ FN_GLOBAL_LIST(lp_usershare_prefix_deny_ FN_GLOBAL_LIST(lp_eventlog_list, &Globals.szEventLogs) FN_GLOBAL_BOOL(lp_usershare_allow_guests, &Globals.bUsershareAllowGuests) +FN_GLOBAL_BOOL(lp_usershare_allow_full_config, &Globals.bUsershareAllowFullConfig) FN_GLOBAL_BOOL(lp_usershare_owner_only, &Globals.bUsershareOwnerOnly) FN_GLOBAL_BOOL(lp_disable_netbios, &Globals.bDisableNetbios) FN_GLOBAL_BOOL(lp_reset_on_zero_vc, &Globals.bResetOnZeroVC) @@ -4412,84 +4418,33 @@ static BOOL check_usershare_stat(const c } /*************************************************************************** - Parse the contents of a usershare file. + Check whether the usershare configuration allows a particular path to be + shared. ***************************************************************************/ -enum usershare_err parse_usershare_file(TALLOC_CTX *ctx, - SMB_STRUCT_STAT *psbuf, - const char *servicename, - int snum, - char **lines, - int numlines, - pstring sharepath, - pstring comment, - SEC_DESC **ppsd, - BOOL *pallow_guest) +static enum usershare_err usershare_validate_path(int snum, + const char *servicename, + const SMB_STRUCT_STAT *svcstat, + const char *sharepath) { + static const char const fn[] = "usershare_validate_path"; + const char **prefixallowlist = lp_usershare_prefix_allow_list(); const char **prefixdenylist = lp_usershare_prefix_deny_list(); - int us_vers; + SMB_STRUCT_DIR *dp; SMB_STRUCT_STAT sbuf; - *pallow_guest = False; - - if (numlines < 4) { - return USERSHARE_MALFORMED_FILE; - } - - if (strcmp(lines[0], "#VERSION 1") == 0) { - us_vers = 1; - } else if (strcmp(lines[0], "#VERSION 2") == 0) { - us_vers = 2; - if (numlines < 5) { - return USERSHARE_MALFORMED_FILE; - } - } else { - return USERSHARE_BAD_VERSION; - } - - if (strncmp(lines[1], "path=", 5) != 0) { - return USERSHARE_MALFORMED_PATH; - } - - pstrcpy(sharepath, &lines[1][5]); - trim_string(sharepath, " ", " "); - - if (strncmp(lines[2], "comment=", 8) != 0) { - return USERSHARE_MALFORMED_COMMENT_DEF; - } - - pstrcpy(comment, &lines[2][8]); - trim_string(comment, " ", " "); - trim_char(comment, '"', '"'); - - if (strncmp(lines[3], "usershare_acl=", 14) != 0) { - return USERSHARE_MALFORMED_ACL_DEF; - } - - if (!parse_usershare_acl(ctx, &lines[3][14], ppsd)) { - return USERSHARE_ACL_ERR; - } - - if (us_vers == 2) { - if (strncmp(lines[4], "guest_ok=", 9) != 0) { - return USERSHARE_MALFORMED_ACL_DEF; - } - if (lines[4][9] == 'y') { - *pallow_guest = True; - } - } - - if (snum != -1 && (strcmp(sharepath, ServicePtrs[snum]->szPath) == 0)) { + if (snum != GLOBAL_SECTION_SNUM && + (strcmp(sharepath, ServicePtrs[snum]->szPath) == 0)) { /* Path didn't change, no checks needed. */ return USERSHARE_OK; } /* The path *must* be absolute. */ if (sharepath[0] != '/') { - DEBUG(2,("parse_usershare_file: share %s: path %s is not an absolute path.\n", - servicename, sharepath)); + DEBUG(2,("%s: share %s: path %s is not an absolute path.\n", + fn, servicename, sharepath)); return USERSHARE_PATH_NOT_ABSOLUTE; } @@ -4498,12 +4453,12 @@ enum usershare_err parse_usershare_file( if (prefixdenylist) { int i; for ( i=0; prefixdenylist[i]; i++ ) { - DEBUG(10,("parse_usershare_file: share %s : checking prefixdenylist[%d]='%s' against %s\n", - servicename, i, prefixdenylist[i], sharepath )); + DEBUG(10,("%s: share %s : checking prefixdenylist[%d]='%s' against %s\n", + fn, servicename, i, prefixdenylist[i], sharepath )); if (memcmp( sharepath, prefixdenylist[i], strlen(prefixdenylist[i])) == 0) { - DEBUG(2,("parse_usershare_file: share %s path %s starts with one of the " + DEBUG(2,("%s: share %s path %s starts with one of the " "usershare prefix deny list entries.\n", - servicename, sharepath)); + fn, servicename, sharepath)); return USERSHARE_PATH_IS_DENIED; } } @@ -4515,16 +4470,16 @@ enum usershare_err parse_usershare_file( if (prefixallowlist) { int i; for ( i=0; prefixallowlist[i]; i++ ) { - DEBUG(10,("parse_usershare_file: share %s checking prefixallowlist[%d]='%s' against %s\n", - servicename, i, prefixallowlist[i], sharepath )); + DEBUG(10,("%s: share %s checking prefixallowlist[%d]='%s' against %s\n", + fn, servicename, i, prefixallowlist[i], sharepath )); if (memcmp( sharepath, prefixallowlist[i], strlen(prefixallowlist[i])) == 0) { break; } } if (prefixallowlist[i] == NULL) { - DEBUG(2,("parse_usershare_file: share %s path %s doesn't start with one of the " + DEBUG(2,("%s: share %s path %s doesn't start with one of the " "usershare prefix allow list entries.\n", - servicename, sharepath)); + fn, servicename, sharepath)); return USERSHARE_PATH_NOT_ALLOWED; } } @@ -4533,8 +4488,8 @@ enum usershare_err parse_usershare_file( dp = sys_opendir(sharepath); if (!dp) { - DEBUG(2,("parse_usershare_file: share %s path %s is not a directory.\n", - servicename, sharepath)); + DEBUG(2,("%s: share %s path %s is not a directory.\n", + fn, servicename, sharepath)); return USERSHARE_PATH_NOT_DIRECTORY; } @@ -4542,8 +4497,8 @@ enum usershare_err parse_usershare_file( this directory. */ if (sys_stat(sharepath, &sbuf) == -1) { - DEBUG(2,("parse_usershare_file: share %s : stat failed on path %s. %s\n", - servicename, sharepath, strerror(errno) )); + DEBUG(2,("%s: share %s : stat failed on path %s. %s\n", + fn, servicename, sharepath, strerror(errno) )); sys_closedir(dp); return USERSHARE_POSIX_ERR; } @@ -4551,8 +4506,8 @@ enum usershare_err parse_usershare_file( sys_closedir(dp); if (!S_ISDIR(sbuf.st_mode)) { - DEBUG(2,("parse_usershare_file: share %s path %s is not a directory.\n", - servicename, sharepath )); + DEBUG(2,("%s: share %s path %s is not a directory.\n", + fn, servicename, sharepath )); return USERSHARE_PATH_NOT_DIRECTORY; } @@ -4562,7 +4517,8 @@ enum usershare_err parse_usershare_file( if (lp_usershare_owner_only()) { /* root can share anything. */ - if ((psbuf->st_uid != 0) && (sbuf.st_uid != psbuf->st_uid)) { + if ((svcstat->st_uid != 0) && + (sbuf.st_uid != svcstat->st_uid)) { return USERSHARE_PATH_NOT_ALLOWED; } } @@ -4570,6 +4526,282 @@ enum usershare_err parse_usershare_file( return USERSHARE_OK; } +/* Handle versions 1 and 2. */ +static enum usershare_err parse_usershare_file_vers2(TALLOC_CTX *ctx, + const SMB_STRUCT_STAT *svcstat, + const char *servicename, + int snum, + int us_vers, + const char **lines, + int numlines) +{ + pstring sharepath; + pstring comment; + SEC_DESC *psd = NULL; + BOOL guest_ok = False; + BOOL applied_share_acl = False; + enum usershare_err ret = USERSHARE_MALFORMED_FILE; + + if (us_vers != 1 && us_vers != 2) { + return USERSHARE_BAD_VERSION; + } + + if (numlines < 4) { + return USERSHARE_MALFORMED_FILE; + } + + if (us_vers == 2 && numlines < 5) { + return USERSHARE_MALFORMED_FILE; + } + + if (strncmp(lines[1], "path=", 5) != 0) { + return USERSHARE_MALFORMED_PATH; + } + pstrcpy(sharepath, &lines[1][5]); + trim_string(sharepath, " ", " "); + + ret = usershare_validate_path(snum, servicename, svcstat, sharepath); + if (ret != USERSHARE_OK) { + goto done; + } + + if (snum != GLOBAL_SECTION_SNUM && + !lp_do_parameter(snum, "path", sharepath)) { + ret = USERSHARE_MALFORMED_PATH; + goto done; + } + + if (strncmp(lines[2], "comment=", 8) != 0) { + ret = USERSHARE_MALFORMED_COMMENT_DEF; + goto done; + } + + pstrcpy(comment, &lines[2][8]); + trim_string(comment, " ", " "); + trim_char(comment, '"', '"'); + + if (snum != GLOBAL_SECTION_SNUM) { + lp_do_parameter(snum, "comment", comment); + } + + if (strncmp(lines[3], "usershare_acl=", 14) != 0) { + ret = USERSHARE_MALFORMED_ACL_DEF; + goto done; + } + + if (!parse_usershare_acl(ctx, &lines[3][14], &psd)) { + ret = USERSHARE_ACL_ERR; + goto done; + } + + /* Write the ACL of the new/modified share. */ + if (snum != GLOBAL_SECTION_SNUM) { + if (!set_share_security(servicename, psd)) { + DEBUG(0, ("Failed to set share " + "security for user share %s\n", + servicename )); + ret = USERSHARE_ACL_ERR; + goto done; + } else { + applied_share_acl = True; + } + } + + if (us_vers == 2) { + if (strncmp(lines[4], "guest_ok=", 9) != 0) { + ret = USERSHARE_MALFORMED_ACL_DEF; + goto done; + } + if (lines[4][9] == 'y' && lp_usershare_allow_guests()) { + guest_ok = True; + } + } + + if (snum != GLOBAL_SECTION_SNUM) { + lp_do_parameter(snum, "guest ok", guest_ok ? "yes" : "no"); + } + + ret = USERSHARE_OK; + +done: + /* Make sure we don't remove any existing share ACL just because we + * found a bogus usershare file. + */ + if (applied_share_acl && ret != USERSHARE_OK) { + delete_share_security(snum2params_static(snum)); + } + + return ret; +} + +/* Handle version 3. */ +static enum usershare_err parse_usershare_file_vers3(TALLOC_CTX *ctx, + const SMB_STRUCT_STAT *svcstat, + const char *servicename, + int snum, + int us_vers, + const char **lines, + int numlines) +{ + fstring param; + fstring value; + + BOOL applied_share_acl = False; + enum usershare_err ret = USERSHARE_MALFORMED_FILE; + int i; + + if (us_vers != 3) { + return USERSHARE_BAD_VERSION; + } + + if (!lp_usershare_allow_full_config()) { + return USERSHARE_BAD_VERSION; + } + + if (snum != GLOBAL_SECTION_SNUM) { + lp_do_parameter(snum, "guest ok", "no"); + } + + for (i = 1; i < numlines; ++i) { + const char *sep = strchr(lines[i], '='); + if (sep == NULL || /* no separator */ + sep == lines[i] || /* no param */ + *(sep + 1) == '\0') /* no value */ { + ret = USERSHARE_MALFORMED_FILE; + goto done; + } + + /* More than 20 options is just silly - someone is messing with + * us if this is the case. + */ + if (i > 20) { + ret = USERSHARE_MALFORMED_FILE; + goto done; + } + + /* Make sure that both the param and the value will fit in an + * fstring. Note that we don't support comments in ths file + * format, so no need to trim them. + */ + if (PTR_DIFF(sep, lines[i]) >= sizeof(fstring) || + strlen(sep) >= sizeof(fstring)) { + ret = USERSHARE_MALFORMED_FILE; + goto done; + } + + strncpy(param, lines[i], PTR_DIFF(sep, lines[i])); + param[PTR_DIFF(sep, lines[i])] = '\0'; + trim_string(param, " ", " "); + trim_char(param, '"', '"'); + + strncpy(value, sep + 1, sizeof(fstring)); + value[sizeof(fstring) - 1] = '\0'; + trim_string(value, " ", " "); + trim_char(value, '"', '"'); + + if (strcmp(param, "path") == 0) { + ret = usershare_validate_path(snum, servicename, + svcstat, value); + if (ret != USERSHARE_OK) { + goto done; + } + } else if (strcmp(param, "guest ok") == 0) { + if (!lp_usershare_allow_guests()) { + continue; + } + } else if (strcmp(param, "usershare_acl") == 0) { + /* There's not standard config option for the + * usershare share ACL syntax, so we roll it by hand. + */ + SEC_DESC *psd = NULL; + + if (!parse_usershare_acl(ctx, value, &psd)) { + ret = USERSHARE_ACL_ERR; + goto done; + } + + if (snum == GLOBAL_SECTION_SNUM) { + continue; + } + + if (!set_share_security(servicename, psd)) { + DEBUG(0, ("Failed to set share " + "security for user share %s\n", + servicename )); + ret = USERSHARE_ACL_ERR; + goto done; + } + + applied_share_acl = True; + continue; + } + + if (snum == GLOBAL_SECTION_SNUM) { + continue; + } + + if (!lp_do_parameter(snum, param, value)) { + DEBUG(0, ("Malformed parameter '%s' " + "on user share %s\n", param, servicename)); + ret = USERSHARE_MALFORMED_FILE; + goto done; + } + } + + ret = USERSHARE_OK; + +done: + /* Make sure we don't remove any existing share ACL just because we + * found a bogus usershare file. + */ + if (applied_share_acl && ret != USERSHARE_OK) { + delete_share_security(snum2params_static(snum)); + } + + return ret; +} + +/*************************************************************************** + Parse the contents of a usershare file. Passing GLOBAL_SECTION_SNUM as the + service number means to to as much parsing and validation as possible without + actually applying the configuration changes. +***************************************************************************/ + +enum usershare_err parse_usershare_file(TALLOC_CTX *ctx, + const SMB_STRUCT_STAT *svcstat, + const char *servicename, + int snum, + const char **lines, + int numlines) +{ + int us_vers = -1; + + if (strcmp(lines[0], "#VERSION 1") == 0) { + us_vers = 1; + } else if (strcmp(lines[0], "#VERSION 2") == 0) { + us_vers = 2; + } else if (strcmp(lines[0], "#VERSION 3") == 0) { + /* Version 3 adds the ability to specify arbitrary smb.conf + * parameters in a usershare file. We only allow this if + * permission is granted by the admin. + */ + us_vers = 3; + } + + switch (us_vers) { + case 1: + case 2: + return parse_usershare_file_vers2(ctx, svcstat, servicename, + snum, us_vers, lines, numlines); + case 3: + return parse_usershare_file_vers3(ctx, svcstat, servicename, + snum, us_vers, lines, numlines); + default: + return USERSHARE_BAD_VERSION; + } + +} + /*************************************************************************** Deal with a usershare file. Returns: @@ -4579,21 +4811,20 @@ enum usershare_err parse_usershare_file( with permissions to share directory etc. ***************************************************************************/ -static int process_usershare_file(const char *dir_name, const char *file_name, int snum_template) +static int process_usershare_file(const char *dir_name, + const char *file_name, + int snum_template) { SMB_STRUCT_STAT sbuf; SMB_STRUCT_STAT lsbuf; pstring fname; - pstring sharepath; - pstring comment; fstring service_name; char **lines = NULL; int numlines = 0; int fd = -1; int iService = -1; TALLOC_CTX *ctx = NULL; - SEC_DESC *psd = NULL; - BOOL guest_ok = False; + service *saved_service = NULL; /* Ensure share name doesn't contain invalid characters. */ if (!validate_net_name(file_name, INVALID_SHARENAME_CHARS, strlen(file_name))) { @@ -4689,16 +4920,6 @@ static int process_usershare_file(const return 1; } - if (parse_usershare_file(ctx, &sbuf, service_name, - iService, lines, numlines, sharepath, - comment, &psd, &guest_ok) != USERSHARE_OK) { - talloc_destroy(ctx); - file_lines_free(lines); - return -1; - } - - file_lines_free(lines); - /* Everything ok - add the service possibly using a template. */ if (iService < 0) { const service *sp = &sDefault; @@ -4709,24 +4930,50 @@ static int process_usershare_file(const if ((iService = add_a_service(sp, service_name)) < 0) { DEBUG(0, ("process_usershare_file: Failed to add " "new service %s\n", service_name)); + file_lines_free(lines); talloc_destroy(ctx); return -1; } + /* Don't let this be valid until we have parsed the usershare + * file correctly. + */ + ServicePtrs[iService]->valid = False; + /* Read only is controlled by usershare ACL below. */ ServicePtrs[iService]->bRead_only = False; + } else if (ServicePtrs[iService]->usershare == 0) { + /* We are modifying an existing service this is not a + * usershare. This is OK, but we do not want a corrupt or + * malicious usershare file to damage it. We save a copy of the + * service so that we can restore it if necessary. + */ + saved_service = talloc_zero(ctx, service); + if (saved_service == NULL) { + file_lines_free(lines); + talloc_destroy(ctx); + return -1; + } + + copy_service(saved_service, ServicePtrs[iService], NULL); } - /* Write the ACL of the new/modified share. */ - if (!set_share_security(service_name, psd)) { - DEBUG(0, ("process_usershare_file: Failed to set share " - "security for user share %s\n", - service_name )); - lp_remove_service(iService); + if (parse_usershare_file(ctx, &sbuf, service_name, iService, + (const char **)lines, numlines) != USERSHARE_OK) { + if (saved_service) { + free_service(ServicePtrs[iService]); + copy_service(ServicePtrs[iService], + saved_service, NULL); + free_service(saved_service); + } else { + lp_remove_service(iService); + } talloc_destroy(ctx); + file_lines_free(lines); return -1; } + file_lines_free(lines); talloc_destroy(ctx); /* If from a template it may be marked invalid. */ @@ -4735,15 +4982,8 @@ static int process_usershare_file(const /* Set the service as a valid usershare. */ ServicePtrs[iService]->usershare = USERSHARE_VALID; - /* Set guest access. */ - if (lp_usershare_allow_guests()) { - ServicePtrs[iService]->bGuest_ok = guest_ok; - } - /* And note when it was loaded. */ ServicePtrs[iService]->usershare_last_mod = sbuf.st_mtime; - string_set(&ServicePtrs[iService]->szPath, sharepath); - string_set(&ServicePtrs[iService]->comment, comment); return iService; } Index: samba/source/utils/net_usershare.c =================================================================== --- samba/source/utils/net_usershare.c.orig +++ samba/source/utils/net_usershare.c @@ -3,6 +3,7 @@ Distributed SMB/CIFS Server Management Utility Copyright (C) Jeremy Allison (jra@samba.org) 2005 + Copyright (C) 2007 Apple Inc. All rights reserved. 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 @@ -289,6 +290,79 @@ static int process_share_list(int (*fn)( Info function. ***************************************************************************/ +/* Parse the usershare_ok line from a version 1 - 3 usershare file and return a + * string containing the resolved ACL. + */ +static char * resolve_usershare_acl(void * ctx, const char * usershare_line) +{ + char *acl_str; + const char *acl_sep; + fstring acl_value; + + int num_aces; + char sep_str[2]; + SEC_DESC *psd = NULL; + + sep_str[0] = *lp_winbind_separator(); + sep_str[1] = '\0'; + + acl_sep = strchr(usershare_line, '='); + if (acl_sep == NULL || /* no separator */ + acl_sep == usershare_line || /* no param */ + *(acl_sep + 1) == '\0') /* no value */ { + /* Shouldn't happen because we already checked the syntax. */ + DEBUG(0, ("invalid usershare_acl syntax: '%s'\n", + usershare_line)); + return NULL; + } + + strncpy(acl_value, acl_sep + 1, sizeof(fstring)); + trim_string(acl_value, " ", " "); + trim_char(acl_value, '"', '"'); + + if (!parse_usershare_acl(ctx, acl_value, &psd)) { + /* Shouldn't happen because we already checked the syntax. */ + DEBUG(0, ("invalid usershare_acl: %s\n", acl_value)); + return NULL; + } + + acl_str = talloc_strdup(ctx, ""); + + for (num_aces = 0; num_aces < psd->dacl->num_aces; num_aces++) { + const char *domain; + const char *name; + NTSTATUS ntstatus; + + ntstatus = net_lookup_name_from_sid(ctx, &psd->dacl->aces[num_aces].trustee, &domain, &name); + + if (NT_STATUS_IS_OK(ntstatus)) { + if (domain && *domain) { + acl_str = talloc_asprintf_append(acl_str, + "%s%s", domain, sep_str); + } + acl_str = talloc_asprintf_append(acl_str, "%s", name); + } else { + fstring sidstr; + sid_to_string(sidstr, &psd->dacl->aces[num_aces].trustee); + acl_str = talloc_asprintf_append(acl_str, "%s", sidstr); + } + + acl_str = talloc_asprintf_append(acl_str, ":"); + + if (psd->dacl->aces[num_aces].type == SEC_ACE_TYPE_ACCESS_DENIED) { + acl_str = talloc_asprintf_append(acl_str, "D"); + } else { + if (psd->dacl->aces[num_aces].access_mask & GENERIC_ALL_ACCESS) { + acl_str = talloc_asprintf_append(acl_str, "F"); + } else { + acl_str = talloc_asprintf_append(acl_str, "R"); + } + } + } + + return acl_str; +} + static int info_fn(struct file_list *fl, void *priv) { SMB_STRUCT_STAT sbuf; @@ -297,18 +371,8 @@ static int info_fn(struct file_list *fl, TALLOC_CTX *ctx = pi->ctx; int fd = -1; int numlines = 0; - SEC_DESC *psd = NULL; pstring basepath; - pstring sharepath; - pstring comment; - pstring acl_str; - int num_aces; - char sep_str[2]; enum usershare_err us_err; - BOOL guest_ok = False; - - sep_str[0] = *lp_winbind_separator(); - sep_str[1] = '\0'; get_basepath(basepath); pstrcat(basepath, "/"); @@ -349,67 +413,45 @@ static int info_fn(struct file_list *fl, } /* Ensure it's well formed. */ - us_err = parse_usershare_file(ctx, &sbuf, fl->pathname, -1, lines, numlines, - sharepath, - comment, - &psd, - &guest_ok); - - file_lines_free(lines); + us_err = parse_usershare_file(ctx, &sbuf, + fl->pathname, GLOBAL_SECTION_SNUM, + (const char **)lines, numlines); if (us_err != USERSHARE_OK) { d_fprintf(stderr, "info_fn: file %s is not a well formed usershare file.\n", basepath ); d_fprintf(stderr, "info_fn: Error was %s.\n", get_us_error_code(us_err) ); + file_lines_free(lines); return -1; } - pstrcpy(acl_str, "usershare_acl="); - - for (num_aces = 0; num_aces < psd->dacl->num_aces; num_aces++) { - const char *domain; - const char *name; - NTSTATUS ntstatus; - - ntstatus = net_lookup_name_from_sid(ctx, &psd->dacl->aces[num_aces].trustee, &domain, &name); + if (pi->op == US_LIST_OP) { + d_printf("%s\n", fl->pathname); + } else if (pi->op == US_INFO_OP) { + int i; + d_printf("[%s]\n", fl->pathname ); - if (NT_STATUS_IS_OK(ntstatus)) { - if (domain && *domain) { - pstrcat(acl_str, domain); - pstrcat(acl_str, sep_str); + for (i = 0; i < numlines; ++i) { + if (*lines[i] == '#') { + continue; } - pstrcat(acl_str,name); - } else { - fstring sidstr; - sid_to_string(sidstr, &psd->dacl->aces[num_aces].trustee); - pstrcat(acl_str,sidstr); - } - pstrcat(acl_str, ":"); - if (psd->dacl->aces[num_aces].type == SEC_ACE_TYPE_ACCESS_DENIED) { - pstrcat(acl_str, "D,"); - } else { - if (psd->dacl->aces[num_aces].access_mask & GENERIC_ALL_ACCESS) { - pstrcat(acl_str, "F,"); + if (strncmp(lines[i], "usershare_acl", 13) == 0) { + char *acl_str; + + acl_str = resolve_usershare_acl(ctx, lines[i]); + if (acl_str) { + d_printf("usershare_acl=%s\n", + acl_str); + } } else { - pstrcat(acl_str, "R,"); + d_printf("%s\n", lines[i]); } } } - acl_str[strlen(acl_str)-1] = '\0'; - - if (pi->op == US_INFO_OP) { - d_printf("[%s]\n", fl->pathname ); - d_printf("path=%s\n", sharepath ); - d_printf("comment=%s\n", comment); - d_printf("%s\n", acl_str); - d_printf("guest_ok=%c\n\n", guest_ok ? 'y' : 'n'); - } else if (pi->op == US_LIST_OP) { - d_printf("%s\n", fl->pathname); - } - + file_lines_free(lines); return 0; } @@ -545,6 +587,144 @@ static BOOL usershare_name_is_valid(cons return True; } + +/* Go through and validate the ACL string. Convert names to SID's as + * needed. Then run it through parse_usershare_acl to ensure it's valid. + */ +static char * usershare_parse_acl_from_arg(void * ctx, const char *arg_acl) +{ + char *us_acl; + const char *pacl; + int num_aces; + int i; + SEC_DESC *psd = NULL; + + /* Start off the string we'll append to. */ + us_acl = talloc_strdup(ctx, ""); + + pacl = arg_acl; + num_aces = 1; + + /* Add the number of ',' characters to get the number of aces. */ + num_aces += count_chars(pacl,','); + + for (i = 0; i < num_aces; i++) { + DOM_SID sid; + const char *pcolon = strchr_m(pacl, ':'); + const char *name; + + if (pcolon == NULL) { + d_fprintf(stderr, "net usershare add: malformed acl %s (missing ':').\n", + pacl ); + return NULL; + } + + switch(pcolon[1]) { + case 'f': + case 'F': + case 'd': + case 'r': + case 'R': + break; + default: + d_fprintf(stderr, "net usershare add: malformed acl %s " + "(access control must be 'r', 'f', or 'd')\n", + pacl ); + return NULL; + } + + if (pcolon[2] != ',' && pcolon[2] != '\0') { + d_fprintf(stderr, "net usershare add: malformed terminating character for acl %s\n", + pacl ); + return NULL; + } + + /* Get the name */ + if ((name = talloc_strndup(ctx, pacl, pcolon - pacl)) == NULL) { + d_fprintf(stderr, + "net usershare add: memory allocation failure\n"); + return NULL; + } + if (!string_to_sid(&sid, name)) { + /* Convert to a SID */ + NTSTATUS ntstatus = net_lookup_sid_from_name(ctx, name, &sid); + if (!NT_STATUS_IS_OK(ntstatus)) { + d_fprintf(stderr, "net usershare add: cannot convert name \"%s\" to a SID. %s.", + name, get_friendly_nt_error_msg(ntstatus) ); + if (NT_STATUS_EQUAL(ntstatus, NT_STATUS_CONNECTION_REFUSED)) { + d_fprintf(stderr, " Maybe smbd is not running.\n"); + } else { + d_fprintf(stderr, "\n"); + } + + return NULL; + } + } + us_acl = talloc_asprintf_append(us_acl, "%s:%c,", sid_string_static(&sid), pcolon[1]); + + /* Move to the next ACL entry. */ + if (pcolon[2] == ',') { + pacl = &pcolon[3]; + } + } + + /* Remove the last ',' */ + us_acl[strlen(us_acl)-1] = '\0'; + + if (!parse_usershare_acl(ctx, us_acl, &psd)) { + d_fprintf(stderr, + "net usershare add: malformed share acl %s\n", us_acl); + return NULL; + } + + return us_acl; +} + +static char * usershare_mkfile_vers2(void *ctx, + const char * us_path, + const char * us_comment, + const char * us_acl, + BOOL guest_ok) +{ + char * file_img; + + file_img = talloc_strdup(ctx, "#VERSION 2\npath="); + file_img = talloc_asprintf_append(file_img, + "%s\ncomment=%s\nusershare_acl=%s\nguest_ok=%c\n", + us_path, us_comment, us_acl, guest_ok ? 'y' : 'n'); + + return file_img; +} + +static char * usershare_mkfile_vers3(void *ctx, + const char * us_path, + const char * us_comment, + const char * us_acl, + BOOL guest_ok, + const char ** options, + int num_options) +{ + int i; + char *file_img; + + /* More than 20 options is just silly. */ + if (num_options > 20) { + return NULL; + } + + file_img = talloc_strdup(ctx, "#VERSION 3\n"); + file_img = talloc_asprintf_append(file_img, + "path=%s\ncomment=%s\nusershare_acl=%s\nguest ok=%s\n", + us_path, us_comment, us_acl, + guest_ok ? "yes" : "no"); + + for (i = 0; i < num_options; ++i) { + file_img = talloc_asprintf_append(file_img, "%s\n", options[i]); + } + + return file_img; +} + static int net_usershare_add(int argc, const char **argv) { TALLOC_CTX *ctx = NULL; @@ -558,14 +738,12 @@ static int net_usershare_add(int argc, c const char *arg_acl; char *us_acl; char *file_img; - int num_aces = 0; - int i; int tmpfd; - const char *pacl; size_t to_write; uid_t myeuid = geteuid(); BOOL guest_ok = False; int num_usershares; + BOOL full_config = False; us_comment = ""; arg_acl = "S-1-1-0:R"; @@ -573,7 +751,6 @@ static int net_usershare_add(int argc, c switch (argc) { case 0: case 1: - default: return net_usershare_add_usage(argc, argv); case 2: sharename = strdup_lower(argv[0]); @@ -590,6 +767,9 @@ static int net_usershare_add(int argc, c us_comment = argv[2]; arg_acl = argv[3]; break; + default: + full_config = True; + /* FALLTHRU */ case 5: sharename = strdup_lower(argv[0]); us_path = argv[1]; @@ -614,6 +794,14 @@ static int net_usershare_add(int argc, c return net_usershare_add_usage(argc, argv); } break; + + } + + if (full_config && !lp_usershare_allow_full_config()) { + d_fprintf(stderr, "net usershare add: full usershare control " + "is administratively disabled\n"); + SAFE_FREE(sharename); + return -1; } /* Ensure we're under the "usershare max shares" number. Advisory only. */ @@ -674,92 +862,16 @@ static int net_usershare_add(int argc, c return -1; } - /* No validation needed on comment. Now go through and validate the - acl string. Convert names to SID's as needed. Then run it through - parse_usershare_acl to ensure it's valid. */ + /* No validation needed on comment. */ ctx = talloc_init("share_info"); - - /* Start off the string we'll append to. */ - us_acl = talloc_strdup(ctx, ""); - - pacl = arg_acl; - num_aces = 1; - - /* Add the number of ',' characters to get the number of aces. */ - num_aces += count_chars(pacl,','); - - for (i = 0; i < num_aces; i++) { - DOM_SID sid; - const char *pcolon = strchr_m(pacl, ':'); - const char *name; - - if (pcolon == NULL) { - d_fprintf(stderr, "net usershare add: malformed acl %s (missing ':').\n", - pacl ); - talloc_destroy(ctx); - SAFE_FREE(sharename); - return -1; - } - - switch(pcolon[1]) { - case 'f': - case 'F': - case 'd': - case 'r': - case 'R': - break; - default: - d_fprintf(stderr, "net usershare add: malformed acl %s " - "(access control must be 'r', 'f', or 'd')\n", - pacl ); - talloc_destroy(ctx); - SAFE_FREE(sharename); - return -1; - } - - if (pcolon[2] != ',' && pcolon[2] != '\0') { - d_fprintf(stderr, "net usershare add: malformed terminating character for acl %s\n", - pacl ); - talloc_destroy(ctx); - SAFE_FREE(sharename); - return -1; - } - - /* Get the name */ - if ((name = talloc_strndup(ctx, pacl, pcolon - pacl)) == NULL) { - d_fprintf(stderr, "talloc_strndup failed\n"); - talloc_destroy(ctx); - SAFE_FREE(sharename); - return -1; - } - if (!string_to_sid(&sid, name)) { - /* Convert to a SID */ - NTSTATUS ntstatus = net_lookup_sid_from_name(ctx, name, &sid); - if (!NT_STATUS_IS_OK(ntstatus)) { - d_fprintf(stderr, "net usershare add: cannot convert name \"%s\" to a SID. %s.", - name, get_friendly_nt_error_msg(ntstatus) ); - if (NT_STATUS_EQUAL(ntstatus, NT_STATUS_CONNECTION_REFUSED)) { - d_fprintf(stderr, " Maybe smbd is not running.\n"); - } else { - d_fprintf(stderr, "\n"); - } - talloc_destroy(ctx); - SAFE_FREE(sharename); - return -1; - } - } - us_acl = talloc_asprintf_append(us_acl, "%s:%c,", sid_string_static(&sid), pcolon[1]); - - /* Move to the next ACL entry. */ - if (pcolon[2] == ',') { - pacl = &pcolon[3]; - } + us_acl = usershare_parse_acl_from_arg(ctx, arg_acl); + if (us_acl == NULL) { + talloc_destroy(ctx); + SAFE_FREE(sharename); + return -1; } - /* Remove the last ',' */ - us_acl[strlen(us_acl)-1] = '\0'; - if (guest_ok && !lp_usershare_allow_guests()) { d_fprintf(stderr, "net usershare add: guest_ok=y requested " "but the \"usershare allow guests\" parameter is not enabled " @@ -815,9 +927,14 @@ static int net_usershare_add(int argc, c } /* Create the in-memory image of the file. */ - file_img = talloc_strdup(ctx, "#VERSION 2\npath="); - file_img = talloc_asprintf_append(file_img, "%s\ncomment=%s\nusershare_acl=%s\nguest_ok=%c\n", - us_path, us_comment, us_acl, guest_ok ? 'y' : 'n'); + + if (full_config) { + file_img = usershare_mkfile_vers3(ctx, us_path, us_comment, + us_acl, guest_ok, &argv[5], argc - 5); + } else { + file_img = usershare_mkfile_vers2(ctx, us_path, us_comment, + us_acl, guest_ok); + } to_write = strlen(file_img);