diff options
Diffstat (limited to 'source3/rpc_server/svcctl')
-rw-r--r-- | source3/rpc_server/svcctl/srv_svcctl_nt.c | 1201 | ||||
-rw-r--r-- | source3/rpc_server/svcctl/srv_svcctl_reg.c | 678 | ||||
-rw-r--r-- | source3/rpc_server/svcctl/srv_svcctl_reg.h | 29 |
3 files changed, 1908 insertions, 0 deletions
diff --git a/source3/rpc_server/svcctl/srv_svcctl_nt.c b/source3/rpc_server/svcctl/srv_svcctl_nt.c new file mode 100644 index 0000000000..a7af089e7e --- /dev/null +++ b/source3/rpc_server/svcctl/srv_svcctl_nt.c @@ -0,0 +1,1201 @@ +/* + * Unix SMB/CIFS implementation. + * RPC Pipe client / server routines + * + * Copyright (C) Marcin Krzysztof Porwit 2005. + * + * Largely Rewritten (Again) by: + * Copyright (C) Gerald (Jerry) Carter 2005. + * Copyright (C) Guenther Deschner 2008,2009. + * + * 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 "../librpc/gen_ndr/srv_svcctl.h" +#include "../libcli/security/security.h" +#include "../librpc/gen_ndr/ndr_security.h" +#include "services/services.h" +#include "services/svc_winreg_glue.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_RPC_SRV + +struct service_control_op { + const char *name; + SERVICE_CONTROL_OPS *ops; +}; + +/* handle external services */ +extern SERVICE_CONTROL_OPS rcinit_svc_ops; + +/* builtin services (see service_db.c and services/svc_*.c */ +extern SERVICE_CONTROL_OPS spoolss_svc_ops; +extern SERVICE_CONTROL_OPS netlogon_svc_ops; +extern SERVICE_CONTROL_OPS winreg_svc_ops; +extern SERVICE_CONTROL_OPS wins_svc_ops; + +/* make sure this number patches the number of builtin + SERVICE_CONTROL_OPS structure listed above */ + +#define SVCCTL_NUM_INTERNAL_SERVICES 4 + +struct service_control_op *svcctl_ops; + +static const struct generic_mapping scm_generic_map = + { SC_MANAGER_READ_ACCESS, SC_MANAGER_WRITE_ACCESS, SC_MANAGER_EXECUTE_ACCESS, SC_MANAGER_ALL_ACCESS }; +static const struct generic_mapping svc_generic_map = + { SERVICE_READ_ACCESS, SERVICE_WRITE_ACCESS, SERVICE_EXECUTE_ACCESS, SERVICE_ALL_ACCESS }; + + +/******************************************************************** +********************************************************************/ + +bool init_service_op_table( void ) +{ + const char **service_list = lp_svcctl_list(); + int num_services = SVCCTL_NUM_INTERNAL_SERVICES + str_list_length( service_list ); + int i; + + if ( !(svcctl_ops = TALLOC_ARRAY( NULL, struct service_control_op, num_services+1)) ) { + DEBUG(0,("init_service_op_table: talloc() failed!\n")); + return False; + } + + /* services listed in smb.conf get the rc.init interface */ + + for ( i=0; service_list && service_list[i]; i++ ) { + svcctl_ops[i].name = talloc_strdup( svcctl_ops, service_list[i] ); + svcctl_ops[i].ops = &rcinit_svc_ops; + } + + /* add builtin services */ + + svcctl_ops[i].name = talloc_strdup( svcctl_ops, "Spooler" ); + svcctl_ops[i].ops = &spoolss_svc_ops; + i++; + + svcctl_ops[i].name = talloc_strdup( svcctl_ops, "NETLOGON" ); + svcctl_ops[i].ops = &netlogon_svc_ops; + i++; + + svcctl_ops[i].name = talloc_strdup( svcctl_ops, "RemoteRegistry" ); + svcctl_ops[i].ops = &winreg_svc_ops; + i++; + + svcctl_ops[i].name = talloc_strdup( svcctl_ops, "WINS" ); + svcctl_ops[i].ops = &wins_svc_ops; + i++; + + /* NULL terminate the array */ + + svcctl_ops[i].name = NULL; + svcctl_ops[i].ops = NULL; + + return True; +} + +bool shutdown_service_op_table(void) +{ + TALLOC_FREE(svcctl_ops); + + return true; +} + +/******************************************************************** +********************************************************************/ + +static struct service_control_op* find_service_by_name( const char *name ) +{ + int i; + + for ( i=0; svcctl_ops[i].name; i++ ) { + if ( strequal( name, svcctl_ops[i].name ) ) + return &svcctl_ops[i]; + } + + return NULL; +} +/******************************************************************** +********************************************************************/ + +static NTSTATUS svcctl_access_check( struct security_descriptor *sec_desc, struct security_token *token, + uint32 access_desired, uint32 *access_granted ) +{ + if ( geteuid() == sec_initial_uid() ) { + DEBUG(5,("svcctl_access_check: using root's token\n")); + token = get_root_nt_token(); + } + + return se_access_check( sec_desc, token, access_desired, access_granted); +} + +/******************************************************************** +********************************************************************/ + +static struct security_descriptor* construct_scm_sd( TALLOC_CTX *ctx ) +{ + struct security_ace ace[2]; + size_t i = 0; + struct security_descriptor *sd; + struct security_acl *theacl; + size_t sd_size; + + /* basic access for Everyone */ + + init_sec_ace(&ace[i++], &global_sid_World, + SEC_ACE_TYPE_ACCESS_ALLOWED, SC_MANAGER_READ_ACCESS, 0); + + /* Full Access 'BUILTIN\Administrators' */ + + init_sec_ace(&ace[i++], &global_sid_Builtin_Administrators, + SEC_ACE_TYPE_ACCESS_ALLOWED, SC_MANAGER_ALL_ACCESS, 0); + + + /* create the security descriptor */ + + if ( !(theacl = make_sec_acl(ctx, NT4_ACL_REVISION, i, ace)) ) + return NULL; + + if ( !(sd = make_sec_desc(ctx, SECURITY_DESCRIPTOR_REVISION_1, + SEC_DESC_SELF_RELATIVE, NULL, NULL, NULL, + theacl, &sd_size)) ) + return NULL; + + return sd; +} + +/****************************************************************** + Find a registry key handle and return a SERVICE_INFO + *****************************************************************/ + +static SERVICE_INFO *find_service_info_by_hnd(struct pipes_struct *p, + struct policy_handle *hnd) +{ + SERVICE_INFO *service_info = NULL; + + if( !find_policy_by_hnd( p, hnd, (void **)(void *)&service_info) ) { + DEBUG(2,("find_service_info_by_hnd: handle not found\n")); + return NULL; + } + + return service_info; +} + +/****************************************************************** + *****************************************************************/ + +static WERROR create_open_service_handle(struct pipes_struct *p, + struct policy_handle *handle, + uint32_t type, + const char *service, + uint32_t access_granted) +{ + SERVICE_INFO *info = NULL; + WERROR result = WERR_OK; + struct service_control_op *s_op; + + if ( !(info = TALLOC_ZERO_P( NULL, SERVICE_INFO )) ) + return WERR_NOMEM; + + /* the Service Manager has a NULL name */ + + info->type = SVC_HANDLE_IS_SCM; + + switch ( type ) { + case SVC_HANDLE_IS_SCM: + info->type = SVC_HANDLE_IS_SCM; + break; + + case SVC_HANDLE_IS_DBLOCK: + info->type = SVC_HANDLE_IS_DBLOCK; + break; + + case SVC_HANDLE_IS_SERVICE: + info->type = SVC_HANDLE_IS_SERVICE; + + /* lookup the SERVICE_CONTROL_OPS */ + + if ( !(s_op = find_service_by_name( service )) ) { + result = WERR_NO_SUCH_SERVICE; + goto done; + } + + info->ops = s_op->ops; + + if ( !(info->name = talloc_strdup( info, s_op->name )) ) { + result = WERR_NOMEM; + goto done; + } + break; + + default: + result = WERR_NO_SUCH_SERVICE; + goto done; + } + + info->access_granted = access_granted; + + /* store the SERVICE_INFO and create an open handle */ + + if ( !create_policy_hnd( p, handle, info ) ) { + result = WERR_ACCESS_DENIED; + goto done; + } + +done: + if ( !W_ERROR_IS_OK(result) ) + TALLOC_FREE(info); + + return result; +} + +/******************************************************************** + _svcctl_OpenSCManagerW +********************************************************************/ + +WERROR _svcctl_OpenSCManagerW(struct pipes_struct *p, + struct svcctl_OpenSCManagerW *r) +{ + struct security_descriptor *sec_desc; + uint32 access_granted = 0; + NTSTATUS status; + + /* perform access checks */ + + if ( !(sec_desc = construct_scm_sd( p->mem_ctx )) ) + return WERR_NOMEM; + + se_map_generic( &r->in.access_mask, &scm_generic_map ); + status = svcctl_access_check( sec_desc, p->server_info->security_token, + r->in.access_mask, &access_granted ); + if ( !NT_STATUS_IS_OK(status) ) + return ntstatus_to_werror( status ); + + return create_open_service_handle( p, r->out.handle, SVC_HANDLE_IS_SCM, NULL, access_granted ); +} + +/******************************************************************** + _svcctl_OpenServiceW +********************************************************************/ + +WERROR _svcctl_OpenServiceW(struct pipes_struct *p, + struct svcctl_OpenServiceW *r) +{ + struct security_descriptor *sec_desc; + uint32 access_granted = 0; + NTSTATUS status; + const char *service = NULL; + + service = r->in.ServiceName; + if (!service) { + return WERR_NOMEM; + } + DEBUG(5, ("_svcctl_OpenServiceW: Attempting to open Service [%s], \n", service)); + + /* based on my tests you can open a service if you have a valid scm handle */ + + if ( !find_service_info_by_hnd( p, r->in.scmanager_handle) ) + return WERR_BADFID; + + /* + * Perform access checks. Use the system server_info in order to ensure + * that we retrieve the security descriptor + */ + sec_desc = svcctl_get_secdesc(p->mem_ctx, + p->msg_ctx, + get_server_info_system(), + service); + if (sec_desc == NULL) { + DEBUG(0, ("_svcctl_OpenServiceW: Failed to get a valid security " + "descriptor")); + return WERR_NOMEM; + } + + se_map_generic( &r->in.access_mask, &svc_generic_map ); + status = svcctl_access_check( sec_desc, p->server_info->security_token, + r->in.access_mask, &access_granted ); + if ( !NT_STATUS_IS_OK(status) ) + return ntstatus_to_werror( status ); + + return create_open_service_handle( p, r->out.handle, SVC_HANDLE_IS_SERVICE, service, access_granted ); +} + +/******************************************************************** + _svcctl_CloseServiceHandle +********************************************************************/ + +WERROR _svcctl_CloseServiceHandle(struct pipes_struct *p, + struct svcctl_CloseServiceHandle *r) +{ + if ( !close_policy_hnd( p, r->in.handle ) ) + return WERR_BADFID; + + ZERO_STRUCTP(r->out.handle); + + return WERR_OK; +} + +/******************************************************************** + _svcctl_GetServiceDisplayNameW +********************************************************************/ + +WERROR _svcctl_GetServiceDisplayNameW(struct pipes_struct *p, + struct svcctl_GetServiceDisplayNameW *r) +{ + const char *service; + const char *display_name; + SERVICE_INFO *info = find_service_info_by_hnd( p, r->in.handle ); + + /* can only use an SCM handle here */ + + if ( !info || (info->type != SVC_HANDLE_IS_SCM) ) + return WERR_BADFID; + + service = r->in.service_name; + + display_name = svcctl_lookup_dispname(p->mem_ctx, + p->msg_ctx, + p->server_info, + service); + if (!display_name) { + display_name = ""; + } + + *r->out.display_name = display_name; + *r->out.display_name_length = strlen(display_name); + + return WERR_OK; +} + +/******************************************************************** + _svcctl_QueryServiceStatus +********************************************************************/ + +WERROR _svcctl_QueryServiceStatus(struct pipes_struct *p, + struct svcctl_QueryServiceStatus *r) +{ + SERVICE_INFO *info = find_service_info_by_hnd( p, r->in.handle ); + + /* perform access checks */ + + if ( !info || (info->type != SVC_HANDLE_IS_SERVICE) ) + return WERR_BADFID; + + if ( !(info->access_granted & SC_RIGHT_SVC_QUERY_STATUS) ) + return WERR_ACCESS_DENIED; + + /* try the service specific status call */ + + return info->ops->service_status( info->name, r->out.service_status ); +} + +/******************************************************************** +********************************************************************/ + +static int enumerate_status(TALLOC_CTX *ctx, + struct messaging_context *msg_ctx, + struct auth_serversupplied_info *server_info, + struct ENUM_SERVICE_STATUSW **status) +{ + int num_services = 0; + int i; + struct ENUM_SERVICE_STATUSW *st; + const char *display_name; + + /* just count */ + while ( svcctl_ops[num_services].name ) + num_services++; + + if ( !(st = TALLOC_ARRAY( ctx, struct ENUM_SERVICE_STATUSW, num_services )) ) { + DEBUG(0,("enumerate_status: talloc() failed!\n")); + return -1; + } + + for ( i=0; i<num_services; i++ ) { + st[i].service_name = talloc_strdup(st, svcctl_ops[i].name ); + + display_name = svcctl_lookup_dispname(ctx, + msg_ctx, + server_info, + svcctl_ops[i].name); + st[i].display_name = talloc_strdup(st, display_name ? display_name : ""); + + svcctl_ops[i].ops->service_status( svcctl_ops[i].name, &st[i].status ); + } + + *status = st; + + return num_services; +} + +/******************************************************************** + _svcctl_EnumServicesStatusW +********************************************************************/ + +WERROR _svcctl_EnumServicesStatusW(struct pipes_struct *p, + struct svcctl_EnumServicesStatusW *r) +{ + struct ENUM_SERVICE_STATUSW *services = NULL; + int num_services; + int i = 0; + size_t buffer_size = 0; + WERROR result = WERR_OK; + SERVICE_INFO *info = find_service_info_by_hnd( p, r->in.handle ); + DATA_BLOB blob = data_blob_null; + + /* perform access checks */ + + if ( !info || (info->type != SVC_HANDLE_IS_SCM) ) + return WERR_BADFID; + + if ( !(info->access_granted & SC_RIGHT_MGR_ENUMERATE_SERVICE) ) { + return WERR_ACCESS_DENIED; + } + + num_services = enumerate_status(p->mem_ctx, + p->msg_ctx, + p->server_info, + &services); + if (num_services == -1 ) { + return WERR_NOMEM; + } + + for ( i=0; i<num_services; i++ ) { + buffer_size += ndr_size_ENUM_SERVICE_STATUSW(&services[i], 0); + } + + buffer_size += buffer_size % 4; + + if (buffer_size > r->in.offered) { + num_services = 0; + result = WERR_MORE_DATA; + } + + if ( W_ERROR_IS_OK(result) ) { + + enum ndr_err_code ndr_err; + struct ndr_push *ndr; + + ndr = ndr_push_init_ctx(p->mem_ctx); + if (ndr == NULL) { + return WERR_INVALID_PARAM; + } + + ndr_err = ndr_push_ENUM_SERVICE_STATUSW_array( + ndr, num_services, services); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + return ntstatus_to_werror(ndr_map_error2ntstatus(ndr_err)); + } + blob = ndr_push_blob(ndr); + memcpy(r->out.service, blob.data, MIN(blob.length, r->in.offered)); + } + + *r->out.needed = (buffer_size > r->in.offered) ? buffer_size : r->in.offered; + *r->out.services_returned = (uint32)num_services; + if (r->out.resume_handle) { + *r->out.resume_handle = 0; + } + + return result; +} + +/******************************************************************** + _svcctl_StartServiceW +********************************************************************/ + +WERROR _svcctl_StartServiceW(struct pipes_struct *p, + struct svcctl_StartServiceW *r) +{ + SERVICE_INFO *info = find_service_info_by_hnd( p, r->in.handle ); + + /* perform access checks */ + + if ( !info || (info->type != SVC_HANDLE_IS_SERVICE) ) + return WERR_BADFID; + + if ( !(info->access_granted & SC_RIGHT_SVC_START) ) + return WERR_ACCESS_DENIED; + + return info->ops->start_service( info->name ); +} + +/******************************************************************** + _svcctl_ControlService +********************************************************************/ + +WERROR _svcctl_ControlService(struct pipes_struct *p, + struct svcctl_ControlService *r) +{ + SERVICE_INFO *info = find_service_info_by_hnd( p, r->in.handle ); + + /* perform access checks */ + + if ( !info || (info->type != SVC_HANDLE_IS_SERVICE) ) + return WERR_BADFID; + + switch ( r->in.control ) { + case SVCCTL_CONTROL_STOP: + if ( !(info->access_granted & SC_RIGHT_SVC_STOP) ) + return WERR_ACCESS_DENIED; + + return info->ops->stop_service( info->name, + r->out.service_status ); + + case SVCCTL_CONTROL_INTERROGATE: + if ( !(info->access_granted & SC_RIGHT_SVC_QUERY_STATUS) ) + return WERR_ACCESS_DENIED; + + return info->ops->service_status( info->name, + r->out.service_status ); + default: + return WERR_INVALID_PARAM; + } +} + +/******************************************************************** + _svcctl_EnumDependentServicesW +********************************************************************/ + +WERROR _svcctl_EnumDependentServicesW(struct pipes_struct *p, + struct svcctl_EnumDependentServicesW *r) +{ + SERVICE_INFO *info = find_service_info_by_hnd( p, r->in.service ); + + /* perform access checks */ + + if ( !info || (info->type != SVC_HANDLE_IS_SERVICE) ) + return WERR_BADFID; + + if ( !(info->access_granted & SC_RIGHT_SVC_ENUMERATE_DEPENDENTS) ) + return WERR_ACCESS_DENIED; + + switch (r->in.state) { + case SERVICE_STATE_ACTIVE: + case SERVICE_STATE_INACTIVE: + case SERVICE_STATE_ALL: + break; + default: + return WERR_INVALID_PARAM; + } + + /* we have to set the outgoing buffer size to the same as the + incoming buffer size (even in the case of failure */ + /* this is done in the autogenerated server already - gd */ + + *r->out.needed = r->in.offered; + + /* no dependent services...basically a stub function */ + *r->out.services_returned = 0; + + return WERR_OK; +} + +/******************************************************************** + _svcctl_QueryServiceStatusEx +********************************************************************/ + +WERROR _svcctl_QueryServiceStatusEx(struct pipes_struct *p, + struct svcctl_QueryServiceStatusEx *r) +{ + SERVICE_INFO *info = find_service_info_by_hnd( p, r->in.handle ); + uint32 buffer_size; + + /* perform access checks */ + + if ( !info || (info->type != SVC_HANDLE_IS_SERVICE) ) + return WERR_BADFID; + + if ( !(info->access_granted & SC_RIGHT_SVC_QUERY_STATUS) ) + return WERR_ACCESS_DENIED; + + /* we have to set the outgoing buffer size to the same as the + incoming buffer size (even in the case of failure) */ + *r->out.needed = r->in.offered; + + switch ( r->in.info_level ) { + case SVC_STATUS_PROCESS_INFO: + { + struct SERVICE_STATUS_PROCESS svc_stat_proc; + enum ndr_err_code ndr_err; + DATA_BLOB blob; + + /* Get the status of the service.. */ + info->ops->service_status( info->name, &svc_stat_proc.status ); + svc_stat_proc.process_id = sys_getpid(); + svc_stat_proc.service_flags = 0x0; + + ndr_err = ndr_push_struct_blob(&blob, p->mem_ctx, &svc_stat_proc, + (ndr_push_flags_fn_t)ndr_push_SERVICE_STATUS_PROCESS); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + return WERR_INVALID_PARAM; + } + + r->out.buffer = blob.data; + buffer_size = sizeof(struct SERVICE_STATUS_PROCESS); + break; + } + + default: + return WERR_UNKNOWN_LEVEL; + } + + + buffer_size += buffer_size % 4; + *r->out.needed = (buffer_size > r->in.offered) ? buffer_size : r->in.offered; + + if (buffer_size > r->in.offered ) { + return WERR_INSUFFICIENT_BUFFER; + } + + return WERR_OK; +} + +/******************************************************************** +********************************************************************/ + +static WERROR fill_svc_config(TALLOC_CTX *ctx, + struct messaging_context *msg_ctx, + struct auth_serversupplied_info *server_info, + const char *name, + struct QUERY_SERVICE_CONFIG *config) +{ + TALLOC_CTX *mem_ctx = talloc_stackframe(); + const char *result = NULL; + + /* now fill in the individual values */ + + config->displayname = svcctl_lookup_dispname(mem_ctx, + msg_ctx, + server_info, + name); + + result = svcctl_get_string_value(mem_ctx, + msg_ctx, + server_info, + name, + "ObjectName"); + if (result != NULL) { + config->startname = result; + } + + result = svcctl_get_string_value(mem_ctx, + msg_ctx, + server_info, + name, + "ImagePath"); + if (result != NULL) { + config->executablepath = result; + } + + /* a few hard coded values */ + /* loadordergroup and dependencies are empty */ + + config->tag_id = 0x00000000; /* unassigned loadorder group */ + config->service_type = SERVICE_TYPE_WIN32_OWN_PROCESS; + config->error_control = SVCCTL_SVC_ERROR_NORMAL; + + /* set the start type. NetLogon and WINS are disabled to prevent + the client from showing the "Start" button (if of course the services + are not running */ + + if ( strequal( name, "NETLOGON" ) && ( lp_servicenumber(name) == -1 ) ) + config->start_type = SVCCTL_DISABLED; + else if ( strequal( name, "WINS" ) && ( !lp_wins_support() )) + config->start_type = SVCCTL_DISABLED; + else + config->start_type = SVCCTL_DEMAND_START; + + + talloc_free(mem_ctx); + + return WERR_OK; +} + +/******************************************************************** + _svcctl_QueryServiceConfigW +********************************************************************/ + +WERROR _svcctl_QueryServiceConfigW(struct pipes_struct *p, + struct svcctl_QueryServiceConfigW *r) +{ + SERVICE_INFO *info = find_service_info_by_hnd( p, r->in.handle ); + uint32 buffer_size; + WERROR wresult; + + /* perform access checks */ + + if ( !info || (info->type != SVC_HANDLE_IS_SERVICE) ) + return WERR_BADFID; + + if ( !(info->access_granted & SC_RIGHT_SVC_QUERY_CONFIG) ) + return WERR_ACCESS_DENIED; + + /* we have to set the outgoing buffer size to the same as the + incoming buffer size (even in the case of failure */ + + *r->out.needed = r->in.offered; + + wresult = fill_svc_config(p->mem_ctx, + p->msg_ctx, + p->server_info, + info->name, + r->out.query); + if ( !W_ERROR_IS_OK(wresult) ) + return wresult; + + buffer_size = ndr_size_QUERY_SERVICE_CONFIG(r->out.query, 0); + *r->out.needed = (buffer_size > r->in.offered) ? buffer_size : r->in.offered; + + if (buffer_size > r->in.offered ) { + ZERO_STRUCTP(r->out.query); + return WERR_INSUFFICIENT_BUFFER; + } + + return WERR_OK; +} + +/******************************************************************** + _svcctl_QueryServiceConfig2W +********************************************************************/ + +WERROR _svcctl_QueryServiceConfig2W(struct pipes_struct *p, + struct svcctl_QueryServiceConfig2W *r) +{ + SERVICE_INFO *info = find_service_info_by_hnd( p, r->in.handle ); + uint32 buffer_size; + + /* perform access checks */ + + if ( !info || (info->type != SVC_HANDLE_IS_SERVICE) ) + return WERR_BADFID; + + if ( !(info->access_granted & SC_RIGHT_SVC_QUERY_CONFIG) ) + return WERR_ACCESS_DENIED; + + /* we have to set the outgoing buffer size to the same as the + incoming buffer size (even in the case of failure */ + *r->out.needed = r->in.offered; + + switch ( r->in.info_level ) { + case SERVICE_CONFIG_DESCRIPTION: + { + struct SERVICE_DESCRIPTION desc_buf; + const char *description; + enum ndr_err_code ndr_err; + DATA_BLOB blob; + + description = svcctl_lookup_description(p->mem_ctx, + p->msg_ctx, + p->server_info, + info->name); + + desc_buf.description = description; + + ndr_err = ndr_push_struct_blob(&blob, p->mem_ctx, &desc_buf, + (ndr_push_flags_fn_t)ndr_push_SERVICE_DESCRIPTION); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + return WERR_INVALID_PARAM; + } + + buffer_size = ndr_size_SERVICE_DESCRIPTION(&desc_buf, 0); + r->out.buffer = blob.data; + + break; + } + break; + case SERVICE_CONFIG_FAILURE_ACTIONS: + { + struct SERVICE_FAILURE_ACTIONS actions; + enum ndr_err_code ndr_err; + DATA_BLOB blob; + + /* nothing to say...just service the request */ + + ZERO_STRUCT( actions ); + + ndr_err = ndr_push_struct_blob(&blob, p->mem_ctx, &actions, + (ndr_push_flags_fn_t)ndr_push_SERVICE_FAILURE_ACTIONS); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + return WERR_INVALID_PARAM; + } + + buffer_size = ndr_size_SERVICE_FAILURE_ACTIONS(&actions, 0); + r->out.buffer = blob.data; + + break; + } + break; + + default: + return WERR_UNKNOWN_LEVEL; + } + + buffer_size += buffer_size % 4; + *r->out.needed = (buffer_size > r->in.offered) ? buffer_size : r->in.offered; + + if (buffer_size > r->in.offered) + return WERR_INSUFFICIENT_BUFFER; + + return WERR_OK; +} + +/******************************************************************** + _svcctl_LockServiceDatabase +********************************************************************/ + +WERROR _svcctl_LockServiceDatabase(struct pipes_struct *p, + struct svcctl_LockServiceDatabase *r) +{ + SERVICE_INFO *info = find_service_info_by_hnd( p, r->in.handle ); + + /* perform access checks */ + + if ( !info || (info->type != SVC_HANDLE_IS_SCM) ) + return WERR_BADFID; + + if ( !(info->access_granted & SC_RIGHT_MGR_LOCK) ) + return WERR_ACCESS_DENIED; + + /* Just open a handle. Doesn't actually lock anything */ + + return create_open_service_handle( p, r->out.lock, SVC_HANDLE_IS_DBLOCK, NULL, 0 ); +} + +/******************************************************************** + _svcctl_UnlockServiceDatabase +********************************************************************/ + +WERROR _svcctl_UnlockServiceDatabase(struct pipes_struct *p, + struct svcctl_UnlockServiceDatabase *r) +{ + SERVICE_INFO *info = find_service_info_by_hnd( p, r->in.lock ); + + + if ( !info || (info->type != SVC_HANDLE_IS_DBLOCK) ) + return WERR_BADFID; + + return close_policy_hnd( p, r->out.lock) ? WERR_OK : WERR_BADFID; +} + +/******************************************************************** + _svcctl_QueryServiceObjectSecurity +********************************************************************/ + +WERROR _svcctl_QueryServiceObjectSecurity(struct pipes_struct *p, + struct svcctl_QueryServiceObjectSecurity *r) +{ + SERVICE_INFO *info = find_service_info_by_hnd( p, r->in.handle ); + struct security_descriptor *sec_desc; + NTSTATUS status; + uint8_t *buffer = NULL; + size_t len = 0; + + + /* only support the SCM and individual services */ + + if ( !info || !(info->type & (SVC_HANDLE_IS_SERVICE|SVC_HANDLE_IS_SCM)) ) + return WERR_BADFID; + + /* check access reights (according to MSDN) */ + + if ( !(info->access_granted & SEC_STD_READ_CONTROL) ) + return WERR_ACCESS_DENIED; + + /* TODO: handle something besides SECINFO_DACL */ + + if ( (r->in.security_flags & SECINFO_DACL) != SECINFO_DACL ) + return WERR_INVALID_PARAM; + + /* Lookup the security descriptor and marshall it up for a reply */ + sec_desc = svcctl_get_secdesc(p->mem_ctx, + p->msg_ctx, + get_server_info_system(), + info->name); + if (sec_desc == NULL) { + return WERR_NOMEM; + } + + *r->out.needed = ndr_size_security_descriptor(sec_desc, 0); + + if ( *r->out.needed > r->in.offered) { + return WERR_INSUFFICIENT_BUFFER; + } + + status = marshall_sec_desc(p->mem_ctx, sec_desc, &buffer, &len); + if (!NT_STATUS_IS_OK(status)) { + return ntstatus_to_werror(status); + } + + *r->out.needed = len; + r->out.buffer = buffer; + + return WERR_OK; +} + +/******************************************************************** + _svcctl_SetServiceObjectSecurity +********************************************************************/ + +WERROR _svcctl_SetServiceObjectSecurity(struct pipes_struct *p, + struct svcctl_SetServiceObjectSecurity *r) +{ + SERVICE_INFO *info = find_service_info_by_hnd( p, r->in.handle ); + struct security_descriptor *sec_desc = NULL; + uint32 required_access; + NTSTATUS status; + + if ( !info || !(info->type & (SVC_HANDLE_IS_SERVICE|SVC_HANDLE_IS_SCM)) ) + return WERR_BADFID; + + /* can't set the security de4scriptor on the ServiceControlManager */ + + if ( info->type == SVC_HANDLE_IS_SCM ) + return WERR_ACCESS_DENIED; + + /* check the access on the open handle */ + + switch ( r->in.security_flags ) { + case SECINFO_DACL: + required_access = SEC_STD_WRITE_DAC; + break; + + case SECINFO_OWNER: + case SECINFO_GROUP: + required_access = SEC_STD_WRITE_OWNER; + break; + + case SECINFO_SACL: + return WERR_INVALID_PARAM; + default: + return WERR_INVALID_PARAM; + } + + if ( !(info->access_granted & required_access) ) + return WERR_ACCESS_DENIED; + + /* read the security descfriptor */ + + status = unmarshall_sec_desc(p->mem_ctx, + r->in.buffer, + r->in.offered, + &sec_desc); + if (!NT_STATUS_IS_OK(status)) { + return ntstatus_to_werror(status); + } + + /* store the new SD */ + + if (!svcctl_set_secdesc(p->msg_ctx, p->server_info, info->name, sec_desc)) + return WERR_ACCESS_DENIED; + + return WERR_OK; +} + + +WERROR _svcctl_DeleteService(struct pipes_struct *p, + struct svcctl_DeleteService *r) +{ + p->rng_fault_state = True; + return WERR_NOT_SUPPORTED; +} + +WERROR _svcctl_SetServiceStatus(struct pipes_struct *p, + struct svcctl_SetServiceStatus *r) +{ + p->rng_fault_state = True; + return WERR_NOT_SUPPORTED; +} + +WERROR _svcctl_NotifyBootConfigStatus(struct pipes_struct *p, + struct svcctl_NotifyBootConfigStatus *r) +{ + p->rng_fault_state = True; + return WERR_NOT_SUPPORTED; +} + +WERROR _svcctl_SCSetServiceBitsW(struct pipes_struct *p, + struct svcctl_SCSetServiceBitsW *r) +{ + p->rng_fault_state = True; + return WERR_NOT_SUPPORTED; +} + +WERROR _svcctl_ChangeServiceConfigW(struct pipes_struct *p, + struct svcctl_ChangeServiceConfigW *r) +{ + p->rng_fault_state = True; + return WERR_NOT_SUPPORTED; +} + +WERROR _svcctl_CreateServiceW(struct pipes_struct *p, + struct svcctl_CreateServiceW *r) +{ + p->rng_fault_state = True; + return WERR_NOT_SUPPORTED; +} + +WERROR _svcctl_QueryServiceLockStatusW(struct pipes_struct *p, + struct svcctl_QueryServiceLockStatusW *r) +{ + p->rng_fault_state = True; + return WERR_NOT_SUPPORTED; +} + +WERROR _svcctl_GetServiceKeyNameW(struct pipes_struct *p, + struct svcctl_GetServiceKeyNameW *r) +{ + p->rng_fault_state = True; + return WERR_NOT_SUPPORTED; +} + +WERROR _svcctl_SCSetServiceBitsA(struct pipes_struct *p, + struct svcctl_SCSetServiceBitsA *r) +{ + p->rng_fault_state = True; + return WERR_NOT_SUPPORTED; +} + +WERROR _svcctl_ChangeServiceConfigA(struct pipes_struct *p, + struct svcctl_ChangeServiceConfigA *r) +{ + p->rng_fault_state = True; + return WERR_NOT_SUPPORTED; +} + +WERROR _svcctl_CreateServiceA(struct pipes_struct *p, + struct svcctl_CreateServiceA *r) +{ + p->rng_fault_state = True; + return WERR_NOT_SUPPORTED; +} + +WERROR _svcctl_EnumDependentServicesA(struct pipes_struct *p, + struct svcctl_EnumDependentServicesA *r) +{ + p->rng_fault_state = True; + return WERR_NOT_SUPPORTED; +} + +WERROR _svcctl_EnumServicesStatusA(struct pipes_struct *p, + struct svcctl_EnumServicesStatusA *r) +{ + p->rng_fault_state = True; + return WERR_NOT_SUPPORTED; +} + +WERROR _svcctl_OpenSCManagerA(struct pipes_struct *p, + struct svcctl_OpenSCManagerA *r) +{ + p->rng_fault_state = True; + return WERR_NOT_SUPPORTED; +} + +WERROR _svcctl_OpenServiceA(struct pipes_struct *p, + struct svcctl_OpenServiceA *r) +{ + p->rng_fault_state = True; + return WERR_NOT_SUPPORTED; +} + +WERROR _svcctl_QueryServiceConfigA(struct pipes_struct *p, + struct svcctl_QueryServiceConfigA *r) +{ + p->rng_fault_state = True; + return WERR_NOT_SUPPORTED; +} + +WERROR _svcctl_QueryServiceLockStatusA(struct pipes_struct *p, + struct svcctl_QueryServiceLockStatusA *r) +{ + p->rng_fault_state = True; + return WERR_NOT_SUPPORTED; +} + +WERROR _svcctl_StartServiceA(struct pipes_struct *p, + struct svcctl_StartServiceA *r) +{ + p->rng_fault_state = True; + return WERR_NOT_SUPPORTED; +} + +WERROR _svcctl_GetServiceDisplayNameA(struct pipes_struct *p, + struct svcctl_GetServiceDisplayNameA *r) +{ + p->rng_fault_state = True; + return WERR_NOT_SUPPORTED; +} + +WERROR _svcctl_GetServiceKeyNameA(struct pipes_struct *p, + struct svcctl_GetServiceKeyNameA *r) +{ + p->rng_fault_state = True; + return WERR_NOT_SUPPORTED; +} + +WERROR _svcctl_GetCurrentGroupeStateW(struct pipes_struct *p, + struct svcctl_GetCurrentGroupeStateW *r) +{ + p->rng_fault_state = True; + return WERR_NOT_SUPPORTED; +} + +WERROR _svcctl_EnumServiceGroupW(struct pipes_struct *p, + struct svcctl_EnumServiceGroupW *r) +{ + p->rng_fault_state = True; + return WERR_NOT_SUPPORTED; +} + +WERROR _svcctl_ChangeServiceConfig2A(struct pipes_struct *p, + struct svcctl_ChangeServiceConfig2A *r) +{ + p->rng_fault_state = True; + return WERR_NOT_SUPPORTED; +} + +WERROR _svcctl_ChangeServiceConfig2W(struct pipes_struct *p, + struct svcctl_ChangeServiceConfig2W *r) +{ + p->rng_fault_state = True; + return WERR_NOT_SUPPORTED; +} + +WERROR _svcctl_QueryServiceConfig2A(struct pipes_struct *p, + struct svcctl_QueryServiceConfig2A *r) +{ + p->rng_fault_state = True; + return WERR_NOT_SUPPORTED; +} + +WERROR _EnumServicesStatusExA(struct pipes_struct *p, + struct EnumServicesStatusExA *r) +{ + p->rng_fault_state = True; + return WERR_NOT_SUPPORTED; +} + +WERROR _EnumServicesStatusExW(struct pipes_struct *p, + struct EnumServicesStatusExW *r) +{ + p->rng_fault_state = True; + return WERR_NOT_SUPPORTED; +} + +WERROR _svcctl_SCSendTSMessage(struct pipes_struct *p, + struct svcctl_SCSendTSMessage *r) +{ + p->rng_fault_state = True; + return WERR_NOT_SUPPORTED; +} diff --git a/source3/rpc_server/svcctl/srv_svcctl_reg.c b/source3/rpc_server/svcctl/srv_svcctl_reg.c new file mode 100644 index 0000000000..84c5f434ee --- /dev/null +++ b/source3/rpc_server/svcctl/srv_svcctl_reg.c @@ -0,0 +1,678 @@ +/* + * 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 "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" + +#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; + } + + status = dcerpc_winreg_int_hklm_openkey(tmp_ctx, + get_server_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); + } + + return ok; +} + +/* vim: set ts=8 sw=8 noet cindent syntax=c.doxygen: */ diff --git a/source3/rpc_server/svcctl/srv_svcctl_reg.h b/source3/rpc_server/svcctl/srv_svcctl_reg.h new file mode 100644 index 0000000000..ab12a03b7b --- /dev/null +++ b/source3/rpc_server/svcctl/srv_svcctl_reg.h @@ -0,0 +1,29 @@ +/* + * Unix SMB/CIFS implementation. + * + * SVCCTL RPC server keys initialization + * + * 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/>. + */ + +#ifndef SRV_SERVICES_REG_H +#define SRV_SERVICES_REG_H + +bool svcctl_init_winreg(struct messaging_context *msg_ctx); + +#endif /* SRV_SERVICES_REG_H */ + +/* vim: set ts=8 sw=8 noet cindent syntax=c.doxygen: */ |