1
0
mirror of https://github.com/samba-team/samba.git synced 2024-12-25 23:21:54 +03:00
samba-mirror/source3/rpc_server/svcctl/srv_svcctl_reg.c
Volker Lendecke d1ded271b6 s3: Wrap creating the svcctl keys in a transaction
This makes the startup of smbd in make test much quicker and thus more reliable
(cherry picked from commit f1aa38b414e97d8687d0bebf65baa384f75301b4)

Autobuild-User: Volker Lendecke <vlendec@samba.org>
Autobuild-Date: Mon Apr 11 22:09:58 CEST 2011 on sn-devel-104
2011-04-11 22:09:58 +02:00

709 lines
18 KiB
C

/*
* Unix SMB/CIFS implementation.
*
* SVCCTL RPC server keys initialization
*
* Copyright (c) 2005 Marcin Krzysztof Porwit
* Copyright (c) 2005 Gerald (Jerry) Carter
* Copyright (c) 2011 Andreas Schneider <asn@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 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"
#include "system/filesys.h"
#include "services/services.h"
#include "services/svc_winreg_glue.h"
#include "../librpc/gen_ndr/ndr_winreg_c.h"
#include "rpc_client/cli_winreg_int.h"
#include "rpc_client/cli_winreg.h"
#include "rpc_server/svcctl/srv_svcctl_reg.h"
#include "auth.h"
#include "registry/reg_backend_db.h"
#undef DBGC_CLASS
#define DBGC_CLASS DBGC_REGISTRY
#define TOP_LEVEL_SERVICES_KEY "SYSTEM\\CurrentControlSet\\Services"
struct rcinit_file_information {
char *description;
};
struct service_display_info {
const char *servicename;
const char *daemon;
const char *dispname;
const char *description;
};
static struct service_display_info builtin_svcs[] = {
{
"Spooler",
"smbd",
"Print Spooler",
"Internal service for spooling files to print devices"
},
{
"NETLOGON",
"smbd",
"Net Logon",
"File service providing access to policy and profile data (not"
"remotely manageable)"
},
{
"RemoteRegistry",
"smbd",
"Remote Registry Service",
"Internal service providing remote access to the Samba registry"
},
{
"WINS",
"nmbd",
"Windows Internet Name Service (WINS)",
"Internal service providing a NetBIOS point-to-point name server"
"(not remotely manageable)"
},
{ NULL, NULL, NULL, NULL }
};
static struct service_display_info common_unix_svcs[] = {
{ "cups", NULL, "Common Unix Printing System","Provides unified printing support for all operating systems" },
{ "postfix", NULL, "Internet Mail Service", "Provides support for sending and receiving electonic mail" },
{ "sendmail", NULL, "Internet Mail Service", "Provides support for sending and receiving electonic mail" },
{ "portmap", NULL, "TCP Port to RPC PortMapper",NULL },
{ "xinetd", NULL, "Internet Meta-Daemon", NULL },
{ "inet", NULL, "Internet Meta-Daemon", NULL },
{ "xntpd", NULL, "Network Time Service", NULL },
{ "ntpd", NULL, "Network Time Service", NULL },
{ "lpd", NULL, "BSD Print Spooler", NULL },
{ "nfsserver", NULL, "Network File Service", NULL },
{ "cron", NULL, "Scheduling Service", NULL },
{ "at", NULL, "Scheduling Service", NULL },
{ "nscd", NULL, "Name Service Cache Daemon", NULL },
{ "slapd", NULL, "LDAP Directory Service", NULL },
{ "ldap", NULL, "LDAP DIrectory Service", NULL },
{ "ypbind", NULL, "NIS Directory Service", NULL },
{ "courier-imap", NULL, "IMAP4 Mail Service", NULL },
{ "courier-pop3", NULL, "POP3 Mail Service", NULL },
{ "named", NULL, "Domain Name Service", NULL },
{ "bind", NULL, "Domain Name Service", NULL },
{ "httpd", NULL, "HTTP Server", NULL },
{ "apache", NULL, "HTTP Server", "Provides s highly scalable and flexible web server "
"capable of implementing various protocols incluing "
"but not limited to HTTP" },
{ "autofs", NULL, "Automounter", NULL },
{ "squid", NULL, "Web Cache Proxy ", NULL },
{ "perfcountd", NULL, "Performance Monitoring Daemon", NULL },
{ "pgsql", NULL, "PgSQL Database Server", "Provides service for SQL database from Postgresql.org" },
{ "arpwatch", NULL, "ARP Tables watcher", "Provides service for monitoring ARP tables for changes" },
{ "dhcpd", NULL, "DHCP Server", "Provides service for dynamic host configuration and IP assignment" },
{ "nwserv", NULL, "NetWare Server Emulator", "Provides service for emulating Novell NetWare 3.12 server" },
{ "proftpd", NULL, "Professional FTP Server", "Provides high configurable service for FTP connection and "
"file transferring" },
{ "ssh2", NULL, "SSH Secure Shell", "Provides service for secure connection for remote administration" },
{ "sshd", NULL, "SSH Secure Shell", "Provides service for secure connection for remote administration" },
{ NULL, NULL, NULL, NULL }
};
/********************************************************************
This is where we do the dirty work of filling in things like the
Display name, Description, etc...
********************************************************************/
static char *svcctl_get_common_service_dispname(TALLOC_CTX *mem_ctx,
const char *servicename)
{
uint32_t i;
for (i = 0; common_unix_svcs[i].servicename; i++) {
if (strequal(servicename, common_unix_svcs[i].servicename)) {
char *dispname;
dispname = talloc_asprintf(mem_ctx, "%s (%s)",
common_unix_svcs[i].dispname,
common_unix_svcs[i].servicename);
if (dispname == NULL) {
return NULL;
}
return dispname;
}
}
return talloc_strdup(mem_ctx, servicename);
}
/********************************************************************
********************************************************************/
static char *svcctl_cleanup_string(TALLOC_CTX *mem_ctx,
const char *string)
{
char *clean = NULL;
char *begin, *end;
clean = talloc_strdup(mem_ctx, string);
if (clean == NULL) {
return NULL;
}
begin = clean;
/* trim any beginning whilespace */
while (isspace(*begin)) {
begin++;
}
if (*begin == '\0') {
return NULL;
}
/* trim any trailing whitespace or carriage returns.
Start at the end and move backwards */
end = begin + strlen(begin) - 1;
while (isspace(*end) || *end=='\n' || *end=='\r') {
*end = '\0';
end--;
}
return begin;
}
/********************************************************************
********************************************************************/
static bool read_init_file(TALLOC_CTX *mem_ctx,
const char *servicename,
struct rcinit_file_information **service_info)
{
struct rcinit_file_information *info = NULL;
char *filepath = NULL;
char str[1024];
XFILE *f = NULL;
char *p = NULL;
info = talloc_zero(mem_ctx, struct rcinit_file_information);
if (info == NULL) {
return false;
}
/* attempt the file open */
filepath = talloc_asprintf(mem_ctx,
"%s/%s/%s",
get_dyn_MODULESDIR(),
SVCCTL_SCRIPT_DIR,
servicename);
if (filepath == NULL) {
return false;
}
f = x_fopen( filepath, O_RDONLY, 0 );
if (f == NULL) {
DEBUG(0,("read_init_file: failed to open [%s]\n", filepath));
return false;
}
while ((x_fgets(str, sizeof(str) - 1, f)) != NULL) {
/* ignore everything that is not a full line
comment starting with a '#' */
if (str[0] != '#') {
continue;
}
/* Look for a line like '^#.*Description:' */
p = strstr(str, "Description:");
if (p != NULL) {
char *desc;
p += strlen( "Description:" ) + 1;
if (p == NULL) {
break;
}
desc = svcctl_cleanup_string(mem_ctx, p);
if (desc != NULL) {
info->description = talloc_strdup(info, desc);
}
}
}
x_fclose(f);
if (info->description == NULL) {
info->description = talloc_strdup(info,
"External Unix Service");
if (info->description == NULL) {
return false;
}
}
*service_info = info;
return true;
}
static bool svcctl_add_service(TALLOC_CTX *mem_ctx,
struct dcerpc_binding_handle *h,
struct policy_handle *hive_hnd,
const char *key,
uint32_t access_mask,
const char *name)
{
enum winreg_CreateAction action = REG_ACTION_NONE;
struct security_descriptor *sd = NULL;
struct policy_handle key_hnd;
struct winreg_String wkey;
struct winreg_String wkeyclass;
char *description = NULL;
char *dname = NULL;
char *ipath = NULL;
bool ok = false;
uint32_t i;
NTSTATUS status;
WERROR result = WERR_OK;
ZERO_STRUCT(key_hnd);
ZERO_STRUCT(wkey);
wkey.name = talloc_asprintf(mem_ctx, "%s\\%s", key, name);
if (wkey.name == NULL) {
goto done;
}
ZERO_STRUCT(wkeyclass);
wkeyclass.name = "";
status = dcerpc_winreg_CreateKey(h,
mem_ctx,
hive_hnd,
wkey,
wkeyclass,
0,
access_mask,
NULL,
&key_hnd,
&action,
&result);
if (!NT_STATUS_IS_OK(status)) {
DEBUG(0, ("svcctl_init_winreg_keys: Could not create key %s: %s\n",
wkey.name, nt_errstr(status)));
goto done;
}
if (!W_ERROR_IS_OK(result)) {
DEBUG(0, ("svcctl_init_winreg_keys: Could not create key %s: %s\n",
wkey.name, win_errstr(result)));
goto done;
}
/* These values are hardcoded in all QueryServiceConfig() replies.
I'm just storing them here for cosmetic purposes */
status = dcerpc_winreg_set_dword(mem_ctx,
h,
&key_hnd,
"Start",
SVCCTL_AUTO_START,
&result);
if (!NT_STATUS_IS_OK(status)) {
DEBUG(0, ("svcctl_init_winreg_keys: Could not create value: %s\n",
nt_errstr(status)));
goto done;
}
if (!W_ERROR_IS_OK(result)) {
DEBUG(0, ("svcctl_init_winreg_keys: Could not create value: %s\n",
win_errstr(result)));
goto done;
}
status = dcerpc_winreg_set_dword(mem_ctx,
h,
&key_hnd,
"Type",
SERVICE_TYPE_WIN32_OWN_PROCESS,
&result);
if (!NT_STATUS_IS_OK(status)) {
DEBUG(0, ("svcctl_init_winreg_keys: Could not create value: %s\n",
nt_errstr(status)));
goto done;
}
if (!W_ERROR_IS_OK(result)) {
DEBUG(0, ("svcctl_init_winreg_keys: Could not create value: %s\n",
win_errstr(result)));
goto done;
}
status = dcerpc_winreg_set_dword(mem_ctx,
h,
&key_hnd,
"ErrorControl",
SVCCTL_SVC_ERROR_NORMAL,
&result);
if (!NT_STATUS_IS_OK(status)) {
DEBUG(0, ("svcctl_init_winreg_keys: Could not create value: %s\n",
nt_errstr(status)));
goto done;
}
if (!W_ERROR_IS_OK(result)) {
DEBUG(0, ("svcctl_init_winreg_keys: Could not create value: %s\n",
win_errstr(result)));
goto done;
}
status = dcerpc_winreg_set_sz(mem_ctx,
h,
&key_hnd,
"ObjectName",
"LocalSystem",
&result);
if (!NT_STATUS_IS_OK(status)) {
DEBUG(0, ("svcctl_init_winreg_keys: Could not create value: %s\n",
nt_errstr(status)));
goto done;
}
if (!W_ERROR_IS_OK(result)) {
DEBUG(0, ("svcctl_init_winreg_keys: Could not create value: %s\n",
win_errstr(result)));
goto done;
}
/*
* Special considerations for internal services and the DisplayName
* value.
*/
for (i = 0; builtin_svcs[i].servicename; i++) {
if (strequal(name, builtin_svcs[i].servicename)) {
ipath = talloc_asprintf(mem_ctx,
"%s/%s/%s",
get_dyn_MODULESDIR(),
SVCCTL_SCRIPT_DIR,
builtin_svcs[i].daemon);
description = talloc_strdup(mem_ctx, builtin_svcs[i].description);
dname = talloc_strdup(mem_ctx, builtin_svcs[i].dispname);
break;
}
}
if (ipath == NULL || dname == NULL || description == NULL) {
goto done;
}
/* Default to an external service if we haven't found a match */
if (builtin_svcs[i].servicename == NULL) {
struct rcinit_file_information *init_info = NULL;
char *dispname = NULL;
ipath = talloc_asprintf(mem_ctx,
"%s/%s/%s",
get_dyn_MODULESDIR(),
SVCCTL_SCRIPT_DIR,
name);
/* lookup common unix display names */
dispname = svcctl_get_common_service_dispname(mem_ctx, name);
dname = talloc_strdup(mem_ctx, dispname ? dispname : "");
/* get info from init file itself */
if (read_init_file(mem_ctx, name, &init_info)) {
description = talloc_strdup(mem_ctx,
init_info->description);
} else {
description = talloc_strdup(mem_ctx,
"External Unix Service");
}
}
if (ipath == NULL || dname == NULL || description == NULL) {
goto done;
}
status = dcerpc_winreg_set_sz(mem_ctx,
h,
&key_hnd,
"DisplayName",
dname,
&result);
if (!NT_STATUS_IS_OK(status)) {
DEBUG(0, ("svcctl_init_winreg_keys: Could not create value: %s\n",
nt_errstr(status)));
goto done;
}
if (!W_ERROR_IS_OK(result)) {
DEBUG(0, ("svcctl_init_winreg_keys: Could not create value: %s\n",
win_errstr(result)));
goto done;
}
status = dcerpc_winreg_set_sz(mem_ctx,
h,
&key_hnd,
"ImagePath",
ipath,
&result);
if (!NT_STATUS_IS_OK(status)) {
DEBUG(0, ("svcctl_init_winreg_keys: Could not create value: %s\n",
nt_errstr(status)));
goto done;
}
if (!W_ERROR_IS_OK(result)) {
DEBUG(0, ("svcctl_init_winreg_keys: Could not create value: %s\n",
win_errstr(result)));
goto done;
}
status = dcerpc_winreg_set_sz(mem_ctx,
h,
&key_hnd,
"Description",
description,
&result);
if (!NT_STATUS_IS_OK(status)) {
DEBUG(0, ("svcctl_init_winreg_keys: Could not create value: %s\n",
nt_errstr(status)));
goto done;
}
if (!W_ERROR_IS_OK(result)) {
DEBUG(0, ("svcctl_init_winreg_keys: Could not create value: %s\n",
win_errstr(result)));
goto done;
}
sd = svcctl_gen_service_sd(mem_ctx);
if (sd == NULL) {
DEBUG(0, ("add_new_svc_name: Failed to create default "
"sec_desc!\n"));
goto done;
}
if (is_valid_policy_hnd(&key_hnd)) {
dcerpc_winreg_CloseKey(h, mem_ctx, &key_hnd, &result);
}
ZERO_STRUCT(key_hnd);
ZERO_STRUCT(wkey);
wkey.name = talloc_asprintf(mem_ctx, "%s\\%s\\Security", key, name);
if (wkey.name == NULL) {
result = WERR_NOMEM;
goto done;
}
ZERO_STRUCT(wkeyclass);
wkeyclass.name = "";
status = dcerpc_winreg_CreateKey(h,
mem_ctx,
hive_hnd,
wkey,
wkeyclass,
0,
access_mask,
NULL,
&key_hnd,
&action,
&result);
if (!NT_STATUS_IS_OK(status)) {
DEBUG(0, ("eventlog_init_winreg_keys: Could not create key %s: %s\n",
wkey.name, nt_errstr(status)));
goto done;
}
if (!W_ERROR_IS_OK(result)) {
DEBUG(0, ("eventlog_init_winreg_keys: Could not create key %s: %s\n",
wkey.name, win_errstr(result)));
goto done;
}
status = dcerpc_winreg_set_sd(mem_ctx,
h,
&key_hnd,
"Security",
sd,
&result);
if (!NT_STATUS_IS_OK(status)) {
DEBUG(0, ("svcctl_init_winreg_keys: Could not create value: %s\n",
nt_errstr(status)));
goto done;
}
if (!W_ERROR_IS_OK(result)) {
DEBUG(0, ("svcctl_init_winreg_keys: Could not create value: %s\n",
win_errstr(result)));
goto done;
}
ok = true;
done:
if (is_valid_policy_hnd(&key_hnd)) {
dcerpc_winreg_CloseKey(h, mem_ctx, &key_hnd, &result);
}
return ok;
}
bool svcctl_init_winreg(struct messaging_context *msg_ctx)
{
struct dcerpc_binding_handle *h = NULL;
uint32_t access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
struct policy_handle hive_hnd, key_hnd;
const char **service_list = lp_svcctl_list();
const char **subkeys = NULL;
uint32_t num_subkeys = 0;
char *key = NULL;
uint32_t i;
NTSTATUS status;
WERROR result = WERR_OK;
bool ok = false;
TALLOC_CTX *tmp_ctx;
tmp_ctx = talloc_stackframe();
if (tmp_ctx == NULL) {
return false;
}
DEBUG(3, ("Initialise the svcctl registry keys if needed.\n"));
ZERO_STRUCT(hive_hnd);
ZERO_STRUCT(key_hnd);
key = talloc_strdup(tmp_ctx, TOP_LEVEL_SERVICES_KEY);
if (key == NULL) {
goto done;
}
result = regdb_open();
if (!W_ERROR_IS_OK(result)) {
DEBUG(10, ("regdb_open failed: %s\n",
win_errstr(result)));
goto done;
}
result = regdb_transaction_start();
if (!W_ERROR_IS_OK(result)) {
DEBUG(10, ("regdb_transaction_start failed: %s\n",
win_errstr(result)));
goto done;
}
status = dcerpc_winreg_int_hklm_openkey(tmp_ctx,
get_session_info_system(),
msg_ctx,
&h,
key,
false,
access_mask,
&hive_hnd,
&key_hnd,
&result);
if (!NT_STATUS_IS_OK(status)) {
DEBUG(0, ("svcctl_init_winreg: Could not open %s - %s\n",
key, nt_errstr(status)));
goto done;
}
if (!W_ERROR_IS_OK(result)) {
DEBUG(0, ("svcctl_init_winreg: Could not open %s - %s\n",
key, win_errstr(result)));
goto done;
}
/* get all subkeys */
status = dcerpc_winreg_enum_keys(tmp_ctx,
h,
&key_hnd,
&num_subkeys,
&subkeys,
&result);
if (!NT_STATUS_IS_OK(status)) {
DEBUG(0, ("svcctl_init_winreg: Could enum keys at %s - %s\n",
key, nt_errstr(status)));
goto done;
}
if (!W_ERROR_IS_OK(result)) {
DEBUG(0, ("svcctl_init_winreg: Could enum keys at %s - %s\n",
key, win_errstr(result)));
goto done;
}
for (i = 0; builtin_svcs[i].servicename != NULL; i++) {
uint32_t j;
bool skip = false;
for (j = 0; j < num_subkeys; j++) {
if (strequal(subkeys[i], builtin_svcs[i].servicename)) {
skip = true;
}
}
if (skip) {
continue;
}
ok = svcctl_add_service(tmp_ctx,
h,
&hive_hnd,
key,
access_mask,
builtin_svcs[i].servicename);
if (!ok) {
goto done;
}
}
for (i = 0; service_list && service_list[i]; i++) {
uint32_t j;
bool skip = false;
for (j = 0; j < num_subkeys; j++) {
if (strequal(subkeys[i], service_list[i])) {
skip = true;
}
}
if (skip) {
continue;
}
ok = svcctl_add_service(tmp_ctx,
h,
&hive_hnd,
key,
access_mask,
service_list[i]);
if (is_valid_policy_hnd(&key_hnd)) {
dcerpc_winreg_CloseKey(h, tmp_ctx, &key_hnd, &result);
}
ZERO_STRUCT(key_hnd);
if (!ok) {
goto done;
}
}
done:
if (is_valid_policy_hnd(&key_hnd)) {
dcerpc_winreg_CloseKey(h, tmp_ctx, &key_hnd, &result);
}
if (ok) {
result = regdb_transaction_commit();
if (!W_ERROR_IS_OK(result)) {
DEBUG(10, ("regdb_transaction_commit failed: %s\n",
win_errstr(result)));
}
} else {
result = regdb_transaction_cancel();
if (!W_ERROR_IS_OK(result)) {
DEBUG(10, ("regdb_transaction_cancel failed: %s\n",
win_errstr(result)));
}
}
regdb_close();
return ok;
}
/* vim: set ts=8 sw=8 noet cindent syntax=c.doxygen: */