/* * Samba Unix/Linux SMB client library * Distributed SMB/CIFS Server Management Utility * Local configuration interface * Copyright (C) Michael Adam 2007 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ /* * This is an interface to the configuration stored inside the * samba registry. In the future there might be support for other * configuration backends as well. */ #include "includes.h" #include "utils/net.h" #include "libnet/libnet.h" /* * usage functions */ static int net_conf_list_usage(int argc, const char **argv) { d_printf("USAGE: net conf list\n"); return -1; } static int net_conf_import_usage(int argc, const char**argv) { d_printf("USAGE: net conf import [--test|-T] " "[]\n" "\t[--test|-T] testmode - do not act, just print " "what would be done\n" "\t only import service , " "ignore the rest\n"); return -1; } static int net_conf_listshares_usage(int argc, const char **argv) { d_printf("USAGE: net conf listshares\n"); return -1; } static int net_conf_drop_usage(int argc, const char **argv) { d_printf("USAGE: net conf drop\n"); return -1; } static int net_conf_showshare_usage(int argc, const char **argv) { d_printf("USAGE: net conf showshare \n"); return -1; } static int net_conf_addshare_usage(int argc, const char **argv) { d_printf("USAGE: net conf addshare " "[writeable={y|N} [guest_ok={y|N} []]\n" "\t the new share name.\n" "\t the path on the filesystem to export.\n" "\twriteable={y|N} set \"writeable to \"yes\" or " "\"no\" (default) on this share.\n" "\tguest_ok={y|N} set \"guest ok\" to \"yes\" or " "\"no\" (default) on this share.\n" "\t optional comment for the new share.\n"); return -1; } static int net_conf_delshare_usage(int argc, const char **argv) { d_printf("USAGE: net conf delshare \n"); return -1; } static int net_conf_setparm_usage(int argc, const char **argv) { d_printf("USAGE: net conf setparm
\n"); return -1; } static int net_conf_getparm_usage(int argc, const char **argv) { d_printf("USAGE: net conf getparm
\n"); return -1; } static int net_conf_delparm_usage(int argc, const char **argv) { d_printf("USAGE: net conf delparm
\n"); return -1; } /* * Helper functions */ static char *format_value(TALLOC_CTX *mem_ctx, struct registry_value *value) { char *result = NULL; /* what if mem_ctx = NULL? */ switch (value->type) { case REG_DWORD: result = talloc_asprintf(mem_ctx, "%d", value->v.dword); break; case REG_SZ: case REG_EXPAND_SZ: result = talloc_asprintf(mem_ctx, "%s", value->v.sz.str); break; case REG_MULTI_SZ: { uint32 j; for (j = 0; j < value->v.multi_sz.num_strings; j++) { result = talloc_asprintf(mem_ctx, "\"%s\" ", value->v.multi_sz.strings[j]); } break; } case REG_BINARY: result = talloc_asprintf(mem_ctx, "binary (%d bytes)", (int)value->v.binary.length); break; default: result = talloc_asprintf(mem_ctx, ""); break; } return result; } /* * delete a subkey of KEY_SMBCONF */ static WERROR reg_delkey_internal(TALLOC_CTX *ctx, const char *keyname) { WERROR werr = WERR_OK; struct registry_key *key = NULL; werr = libnet_smbconf_open_basepath(ctx, REG_KEY_WRITE, &key); if (!W_ERROR_IS_OK(werr)) { goto done; } werr = reg_deletekey_recursive(key, key, keyname); if (!W_ERROR_IS_OK(werr)) { d_fprintf(stderr, "Error deleting registry key %s\\%s: %s\n", KEY_SMBCONF, keyname, dos_errstr(werr)); } done: TALLOC_FREE(key); return werr; } static WERROR list_values(TALLOC_CTX *ctx, struct registry_key *key) { WERROR werr = WERR_OK; uint32 idx = 0; struct registry_value *valvalue = NULL; char *valname = NULL; for (idx = 0; W_ERROR_IS_OK(werr = reg_enumvalue(ctx, key, idx, &valname, &valvalue)); idx++) { d_printf("\t%s = %s\n", valname, format_value(ctx, valvalue)); } if (!W_ERROR_EQUAL(WERR_NO_MORE_ITEMS, werr)) { d_fprintf(stderr, "Error enumerating values: %s\n", dos_errstr(werr)); goto done; } werr = WERR_OK; done: return werr; } static WERROR drop_smbconf_internal(TALLOC_CTX *ctx) { char *path, *p; WERROR werr = WERR_OK; NT_USER_TOKEN *token; struct registry_key *parent_key = NULL; struct registry_key *new_key = NULL; TALLOC_CTX* tmp_ctx = NULL; enum winreg_CreateAction action; tmp_ctx = talloc_new(ctx); if (tmp_ctx == NULL) { werr = WERR_NOMEM; goto done; } if (!(token = registry_create_admin_token(tmp_ctx))) { /* what is the appropriate error code here? */ werr = WERR_CAN_NOT_COMPLETE; goto done; } path = talloc_strdup(tmp_ctx, KEY_SMBCONF); if (path == NULL) { d_fprintf(stderr, "ERROR: out of memory!\n"); werr = WERR_NOMEM; goto done; } p = strrchr(path, '\\'); *p = '\0'; werr = reg_open_path(tmp_ctx, path, REG_KEY_WRITE, token, &parent_key); if (!W_ERROR_IS_OK(werr)) { goto done; } werr = reg_deletekey_recursive(tmp_ctx, parent_key, p+1); if (!W_ERROR_IS_OK(werr)) { goto done; } werr = reg_createkey(tmp_ctx, parent_key, p+1, REG_KEY_WRITE, &new_key, &action); done: TALLOC_FREE(tmp_ctx); return werr; } static char *parm_valstr(TALLOC_CTX *ctx, struct parm_struct *parm, struct share_params *share) { char *valstr = NULL; int i = 0; void *ptr = parm->ptr; if (parm->p_class == P_LOCAL && share->service >= 0) { ptr = lp_local_ptr(share->service, ptr); } switch (parm->type) { case P_CHAR: valstr = talloc_asprintf(ctx, "%c", *(char *)ptr); break; case P_STRING: case P_USTRING: valstr = talloc_asprintf(ctx, "%s", *(char **)ptr); break; case P_BOOL: valstr = talloc_asprintf(ctx, "%s", BOOLSTR(*(bool *)ptr)); break; case P_BOOLREV: valstr = talloc_asprintf(ctx, "%s", BOOLSTR(!*(bool *)ptr)); break; case P_ENUM: for (i = 0; parm->enum_list[i].name; i++) { if (*(int *)ptr == parm->enum_list[i].value) { valstr = talloc_asprintf(ctx, "%s", parm->enum_list[i].name); break; } } break; case P_OCTAL: { char *o = octal_string(*(int *)ptr); valstr = talloc_move(ctx, &o); break; } case P_LIST: valstr = talloc_strdup(ctx, ""); if ((char ***)ptr && *(char ***)ptr) { char **list = *(char ***)ptr; for (; *list; list++) { /* surround strings with whitespace * in double quotes */ if (strchr_m(*list, ' ')) { valstr = talloc_asprintf_append( valstr, "\"%s\"%s", *list, ((*(list+1))?", ":"")); } else { valstr = talloc_asprintf_append( valstr, "%s%s", *list, ((*(list+1))?", ":"")); } } } break; case P_INTEGER: valstr = talloc_asprintf(ctx, "%d", *(int *)ptr); break; case P_SEP: break; default: valstr = talloc_asprintf(ctx, "\n"); break; } return valstr; } static int import_process_service(TALLOC_CTX *ctx, struct share_params *share) { int ret = -1; struct parm_struct *parm; int pnum = 0; const char *servicename; struct registry_key *key; WERROR werr; char *valstr = NULL; TALLOC_CTX *tmp_ctx = NULL; tmp_ctx = talloc_new(ctx); if (tmp_ctx == NULL) { werr = WERR_NOMEM; goto done; } servicename = (share->service == GLOBAL_SECTION_SNUM)? GLOBAL_NAME : lp_servicename(share->service); if (opt_testmode) { d_printf("[%s]\n", servicename); } else { if (libnet_smbconf_key_exists(tmp_ctx, servicename)) { werr = reg_delkey_internal(tmp_ctx, servicename); if (!W_ERROR_IS_OK(werr)) { goto done; } } werr = libnet_reg_createkey_internal(tmp_ctx, servicename, &key); if (!W_ERROR_IS_OK(werr)) { goto done; } } while ((parm = lp_next_parameter(share->service, &pnum, 0))) { if ((share->service < 0 && parm->p_class == P_LOCAL) && !(parm->flags & FLAG_GLOBAL)) continue; valstr = parm_valstr(tmp_ctx, parm, share); if (parm->type != P_SEP) { if (opt_testmode) { d_printf("\t%s = %s\n", parm->label, valstr); } else { werr = libnet_smbconf_reg_setvalue_internal(key, parm->label, valstr); if (!W_ERROR_IS_OK(werr)) { goto done; } } } } if (opt_testmode) { d_printf("\n"); } ret = 0; done: TALLOC_FREE(tmp_ctx); return ret; } /* return True iff there are nondefault globals */ static bool globals_exist(void) { int i = 0; struct parm_struct *parm; while ((parm = lp_next_parameter(GLOBAL_SECTION_SNUM, &i, 0)) != NULL) { if (parm->type != P_SEP) { return True; } } return False; } /* * the conf functions */ int net_conf_list(int argc, const char **argv) { WERROR werr = WERR_OK; int ret = -1; TALLOC_CTX *ctx; struct registry_key *base_key = NULL; struct registry_key *sub_key = NULL; uint32 idx_key = 0; char *subkey_name = NULL; ctx = talloc_init("list"); if (argc != 0) { net_conf_list_usage(argc, argv); goto done; } werr = libnet_smbconf_open_basepath(ctx, REG_KEY_READ, &base_key); if (!W_ERROR_IS_OK(werr)) { goto done; } if (libnet_smbconf_key_exists(ctx, GLOBAL_NAME)) { werr = reg_openkey(ctx, base_key, GLOBAL_NAME, REG_KEY_READ, &sub_key); if (!W_ERROR_IS_OK(werr)) { d_fprintf(stderr, "Error opening subkey '%s' : %s\n", subkey_name, dos_errstr(werr)); goto done; } d_printf("[%s]\n", GLOBAL_NAME); if (!W_ERROR_IS_OK(list_values(ctx, sub_key))) { goto done; } d_printf("\n"); } for (idx_key = 0; W_ERROR_IS_OK(werr = reg_enumkey(ctx, base_key, idx_key, &subkey_name, NULL)); idx_key++) { if (strequal(subkey_name, GLOBAL_NAME)) { continue; } d_printf("[%s]\n", subkey_name); werr = reg_openkey(ctx, base_key, subkey_name, REG_KEY_READ, &sub_key); if (!W_ERROR_IS_OK(werr)) { d_fprintf(stderr, "Error opening subkey '%s': %s\n", subkey_name, dos_errstr(werr)); goto done; } if (!W_ERROR_IS_OK(list_values(ctx, sub_key))) { goto done; } d_printf("\n"); } if (!W_ERROR_EQUAL(WERR_NO_MORE_ITEMS, werr)) { d_fprintf(stderr, "Error enumerating subkeys: %s\n", dos_errstr(werr)); goto done; } ret = 0; done: TALLOC_FREE(ctx); return ret; } int net_conf_import(int argc, const char **argv) { int ret = -1; const char *filename = NULL; const char *servicename = NULL; bool service_found = False; TALLOC_CTX *ctx; struct share_iterator *shares; struct share_params *share; struct share_params global_share = { GLOBAL_SECTION_SNUM }; ctx = talloc_init("net_conf_import"); switch (argc) { case 0: default: net_conf_import_usage(argc, argv); goto done; case 2: servicename = argv[1]; case 1: filename = argv[0]; break; } DEBUG(3,("net_conf_import: reading configuration from file %s.\n", filename)); if (!lp_load(filename, False, /* global_only */ True, /* save_defaults */ False, /* add_ipc */ True)) /* initialize_globals */ { d_fprintf(stderr, "Error parsing configuration file.\n"); goto done; } if (opt_testmode) { d_printf("\nTEST MODE - " "would import the following configuration:\n\n"); } if (((servicename == NULL) && globals_exist()) || strequal(servicename, GLOBAL_NAME)) { service_found = True; if (import_process_service(ctx, &global_share) != 0) { goto done; } } if (service_found && (servicename != NULL)) { ret = 0; goto done; } if (!(shares = share_list_all(ctx))) { d_fprintf(stderr, "Could not list shares...\n"); goto done; } while ((share = next_share(shares)) != NULL) { if ((servicename == NULL) || strequal(servicename, lp_servicename(share->service))) { service_found = True; if (import_process_service(ctx, share)!= 0) { goto done; } } } if ((servicename != NULL) && !service_found) { d_printf("Share %s not found in file %s\n", servicename, filename); goto done; } ret = 0; done: TALLOC_FREE(ctx); return ret; } int net_conf_listshares(int argc, const char **argv) { WERROR werr = WERR_OK; int ret = -1; struct registry_key *key; uint32 idx = 0; char *subkey_name = NULL; TALLOC_CTX *ctx; ctx = talloc_init("listshares"); if (argc != 0) { net_conf_listshares_usage(argc, argv); goto done; } werr = libnet_smbconf_open_basepath(ctx, SEC_RIGHTS_ENUM_SUBKEYS, &key); if (!W_ERROR_IS_OK(werr)) { goto done; } for (idx = 0; W_ERROR_IS_OK(werr = reg_enumkey(ctx, key, idx, &subkey_name, NULL)); idx++) { d_printf("%s\n", subkey_name); } if (! W_ERROR_EQUAL(WERR_NO_MORE_ITEMS, werr)) { d_fprintf(stderr, "Error enumerating subkeys: %s\n", dos_errstr(werr)); goto done; } ret = 0; done: TALLOC_FREE(ctx); return ret; } int net_conf_drop(int argc, const char **argv) { int ret = -1; WERROR werr; if (argc != 0) { net_conf_drop_usage(argc, argv); goto done; } werr = drop_smbconf_internal(NULL); if (!W_ERROR_IS_OK(werr)) { d_fprintf(stderr, "Error deleting configuration: %s\n", dos_errstr(werr)); goto done; } ret = 0; done: return ret; } int net_conf_showshare(int argc, const char **argv) { int ret = -1; WERROR werr = WERR_OK; struct registry_key *key = NULL; TALLOC_CTX *ctx; ctx = talloc_init("showshare"); if (argc != 1) { net_conf_showshare_usage(argc, argv); goto done; } werr = libnet_smbconf_open_path(ctx, argv[0], REG_KEY_READ, &key); if (!W_ERROR_IS_OK(werr)) { goto done; } d_printf("[%s]\n", argv[0]); if (!W_ERROR_IS_OK(list_values(ctx, key))) { goto done; } ret = 0; done: TALLOC_FREE(ctx); return ret; } int net_conf_addshare(int argc, const char **argv) { int ret = -1; WERROR werr = WERR_OK; struct registry_key *newkey = NULL; char *sharename = NULL; const char *path = NULL; const char *comment = NULL; const char *guest_ok = "no"; const char *writeable = "no"; SMB_STRUCT_STAT sbuf; switch (argc) { case 0: case 1: default: net_conf_addshare_usage(argc, argv); goto done; case 5: comment = argv[4]; case 4: if (!strnequal(argv[3], "guest_ok=", 9)) { net_conf_addshare_usage(argc, argv); goto done; } switch (argv[3][9]) { case 'y': case 'Y': guest_ok = "yes"; break; case 'n': case 'N': guest_ok = "no"; break; default: net_conf_addshare_usage(argc, argv); goto done; } case 3: if (!strnequal(argv[2], "writeable=", 10)) { net_conf_addshare_usage(argc, argv); goto done; } switch (argv[2][10]) { case 'y': case 'Y': writeable = "yes"; break; case 'n': case 'N': writeable = "no"; break; default: net_conf_addshare_usage(argc, argv); goto done; } case 2: path = argv[1]; sharename = strdup_lower(argv[0]); break; } /* * validate arguments */ /* validate share name */ if (!validate_net_name(sharename, INVALID_SHARENAME_CHARS, strlen(sharename))) { d_fprintf(stderr, "ERROR: share name %s contains " "invalid characters (any of %s)\n", sharename, INVALID_SHARENAME_CHARS); goto done; } if (getpwnam(sharename)) { d_fprintf(stderr, "ERROR: share name %s is already a valid " "system user name.\n", sharename); goto done; } if (strequal(sharename, GLOBAL_NAME)) { d_fprintf(stderr, "ERROR: 'global' is not a valid share name.\n"); goto done; } /* validate path */ if (path[0] != '/') { d_fprintf(stderr, "Error: path '%s' is not an absolute path.\n", path); goto done; } if (sys_stat(path, &sbuf) != 0) { d_fprintf(stderr, "ERROR: cannot stat path '%s' to ensure " "this is a directory.\n" "Error was '%s'.\n", path, strerror(errno)); goto done; } if (!S_ISDIR(sbuf.st_mode)) { d_fprintf(stderr, "ERROR: path '%s' is not a directory.\n", path); goto done; } /* * create the share */ werr = libnet_reg_createkey_internal(NULL, argv[0], &newkey); if (!W_ERROR_IS_OK(werr)) { goto done; } /* add config params as values */ werr = libnet_smbconf_reg_setvalue_internal(newkey, "path", path); if (!W_ERROR_IS_OK(werr)) goto done; if (comment != NULL) { werr = libnet_smbconf_reg_setvalue_internal(newkey, "comment", comment); if (!W_ERROR_IS_OK(werr)) goto done; } werr = libnet_smbconf_reg_setvalue_internal(newkey, "guest ok", guest_ok); if (!W_ERROR_IS_OK(werr)) goto done; werr = libnet_smbconf_reg_setvalue_internal(newkey, "writeable", writeable); if (!W_ERROR_IS_OK(werr)) goto done; ret = 0; done: TALLOC_FREE(newkey); SAFE_FREE(sharename); return ret; } int net_conf_delshare(int argc, const char **argv) { int ret = -1; const char *sharename = NULL; if (argc != 1) { net_conf_delshare_usage(argc, argv); goto done; } sharename = argv[0]; if (W_ERROR_IS_OK(reg_delkey_internal(NULL, sharename))) { ret = 0; } done: return ret; } static int net_conf_setparm(int argc, const char **argv) { int ret = -1; WERROR werr = WERR_OK; char *service = NULL; char *param = NULL; const char *value_str = NULL; TALLOC_CTX *ctx; ctx = talloc_init("setparm"); if (argc != 3) { net_conf_setparm_usage(argc, argv); goto done; } service = strdup_lower(argv[0]); param = strdup_lower(argv[1]); value_str = argv[2]; werr = libnet_smbconf_setparm(ctx, service, param, value_str); if (!W_ERROR_IS_OK(werr)) { d_fprintf(stderr, "Error setting value '%s': %s\n", param, dos_errstr(werr)); goto done; } ret = 0; done: SAFE_FREE(service); TALLOC_FREE(ctx); return ret; } static int net_conf_getparm(int argc, const char **argv) { int ret = -1; WERROR werr = WERR_OK; struct registry_key *key = NULL; char *service = NULL; char *param = NULL; struct registry_value *value = NULL; TALLOC_CTX *ctx; ctx = talloc_init("getparm"); if (argc != 2) { net_conf_getparm_usage(argc, argv); goto done; } service = strdup_lower(argv[0]); param = strdup_lower(argv[1]); if (!libnet_smbconf_key_exists(ctx, service)) { d_fprintf(stderr, "ERROR: given service '%s' does not exist.\n", service); goto done; } werr = libnet_smbconf_open_path(ctx, service, REG_KEY_READ, &key); if (!W_ERROR_IS_OK(werr)) { goto done; } werr = reg_queryvalue(ctx, key, param, &value); if (!W_ERROR_IS_OK(werr)) { d_fprintf(stderr, "Error querying value '%s': %s.\n", param, dos_errstr(werr)); goto done; } d_printf("%s\n", format_value(ctx, value)); ret = 0; done: SAFE_FREE(service); SAFE_FREE(param); TALLOC_FREE(ctx); return ret; } static int net_conf_delparm(int argc, const char **argv) { int ret = -1; WERROR werr = WERR_OK; char *service = NULL; char *param = NULL; TALLOC_CTX *ctx; ctx = talloc_init("delparm"); if (argc != 2) { net_conf_delparm_usage(argc, argv); goto done; } service = strdup_lower(argv[0]); param = strdup_lower(argv[1]); werr = libnet_smbconf_delparm(ctx, service, param); if (W_ERROR_EQUAL(werr, WERR_NO_SUCH_SERVICE)) { d_fprintf(stderr, "Error: given service '%s' does not exist.\n", service); goto done; } else if (W_ERROR_EQUAL(werr, WERR_INVALID_PARAM)) { d_fprintf(stderr, "Error: given parameter '%s' is not set.\n", param); goto done; } else if (!W_ERROR_IS_OK(werr)) { d_fprintf(stderr, "Error deleting value '%s': %s.\n", param, dos_errstr(werr)); goto done; } ret = 0; done: return ret; } /* * Entry-point for all the CONF functions. */ int net_conf(int argc, const char **argv) { int ret = -1; struct functable2 func[] = { {"list", net_conf_list, "Dump the complete configuration in smb.conf like format."}, {"import", net_conf_import, "Import configuration from file in smb.conf format."}, {"listshares", net_conf_listshares, "List the registry shares."}, {"drop", net_conf_drop, "Delete the complete configuration from registry."}, {"showshare", net_conf_showshare, "Show the definition of a registry share."}, {"addshare", net_conf_addshare, "Create a new registry share."}, {"delshare", net_conf_delshare, "Delete a registry share."}, {"setparm", net_conf_setparm, "Store a parameter."}, {"getparm", net_conf_getparm, "Retrieve the value of a parameter."}, {"delparm", net_conf_delparm, "Delete a parameter."}, {NULL, NULL, NULL} }; if (!registry_init_regdb()) { d_fprintf(stderr, "Error initializing the registry!\n"); goto done; } ret = net_run_function2(argc, argv, "net conf", func); regdb_close(); done: return ret; }