2010-01-09 09:11:01 +03:00
/*
Unix SMB / CIFS implementation .
implement the DsWriteAccountSpn call
Copyright ( C ) Stefan Metzmacher 2009
Copyright ( C ) Andrew Tridgell 2010
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 "rpc_server/dcerpc_server.h"
# include "dsdb/samdb/samdb.h"
2010-02-16 06:29:07 +03:00
# include "dsdb/common/util.h"
2010-11-08 11:01:36 +03:00
# include "system/kerberos.h"
# include "auth/kerberos/kerberos.h"
# include "libcli/security/security.h"
2010-11-27 21:42:31 +03:00
# include "libcli/security/session.h"
# include "rpc_server/drsuapi/dcesrv_drsuapi.h"
2016-05-24 16:10:14 +03:00
# include "librpc/gen_ndr/ndr_drsuapi.h"
2010-11-08 11:01:36 +03:00
# include "auth/session.h"
2017-09-06 07:37:34 +03:00
# undef DBGC_CLASS
# define DBGC_CLASS DBGC_DRS_REPL
2010-11-08 11:01:36 +03:00
/*
check that the SPN update should be allowed as an override
via sam_ctx_system
This is only called if the client is not a domain controller or
administrator
*/
static bool writespn_check_spn ( struct drsuapi_bind_state * b_state ,
struct dcesrv_call_state * dce_call ,
struct ldb_dn * dn ,
const char * spn )
{
/*
2010-11-08 11:58:59 +03:00
* we only allow SPN updates if :
*
* 1 ) they are on the clients own account object
* 2 ) they are of the form SERVICE / dnshostname
2010-11-08 11:01:36 +03:00
*/
2018-11-03 03:19:51 +03:00
struct auth_session_info * session_info =
dcesrv_call_session_info ( dce_call ) ;
2010-11-08 11:01:36 +03:00
struct dom_sid * user_sid , * sid ;
TALLOC_CTX * tmp_ctx = talloc_new ( dce_call ) ;
struct ldb_result * res ;
2010-11-08 11:58:59 +03:00
const char * attrs [ ] = { " objectSID " , " dNSHostName " , NULL } ;
2010-11-08 11:01:36 +03:00
int ret ;
krb5_context krb_ctx ;
krb5_error_code kerr ;
krb5_principal principal ;
2015-03-18 12:15:47 +03:00
const krb5_data * component ;
2010-11-08 11:01:36 +03:00
const char * dns_name , * dnsHostName ;
2011-03-01 15:03:59 +03:00
/* The service principal name shouldn't be NULL */
if ( spn = = NULL ) {
talloc_free ( tmp_ctx ) ;
return false ;
}
2010-11-08 11:01:36 +03:00
/*
get the objectSid of the DN that is being modified , and
check it matches the user_sid in their token
*/
2010-11-08 11:58:59 +03:00
ret = dsdb_search_dn ( b_state - > sam_ctx , tmp_ctx , & res , dn , attrs ,
DSDB_SEARCH_ONE_ONLY ) ;
2010-11-08 11:01:36 +03:00
if ( ret ! = LDB_SUCCESS ) {
talloc_free ( tmp_ctx ) ;
return false ;
}
2018-11-03 03:19:51 +03:00
user_sid = & session_info - > security_token - > sids [ PRIMARY_USER_SID_INDEX ] ;
2010-11-08 11:01:36 +03:00
sid = samdb_result_dom_sid ( tmp_ctx , res - > msgs [ 0 ] , " objectSid " ) ;
if ( sid = = NULL ) {
talloc_free ( tmp_ctx ) ;
return false ;
}
2010-11-08 11:58:59 +03:00
dnsHostName = ldb_msg_find_attr_as_string ( res - > msgs [ 0 ] , " dNSHostName " ,
NULL ) ;
2010-11-08 11:01:36 +03:00
if ( dnsHostName = = NULL ) {
talloc_free ( tmp_ctx ) ;
return false ;
}
if ( ! dom_sid_equal ( sid , user_sid ) ) {
talloc_free ( tmp_ctx ) ;
return false ;
}
2010-11-08 11:58:59 +03:00
kerr = smb_krb5_init_context_basic ( tmp_ctx ,
dce_call - > conn - > dce_ctx - > lp_ctx ,
& krb_ctx ) ;
2010-11-08 11:01:36 +03:00
if ( kerr ! = 0 ) {
talloc_free ( tmp_ctx ) ;
return false ;
}
2010-11-08 11:58:59 +03:00
ret = krb5_parse_name_flags ( krb_ctx , spn , KRB5_PRINCIPAL_PARSE_NO_REALM ,
& principal ) ;
2010-11-08 11:01:36 +03:00
if ( kerr ! = 0 ) {
krb5_free_context ( krb_ctx ) ;
talloc_free ( tmp_ctx ) ;
return false ;
}
2012-05-02 21:22:08 +04:00
if ( krb5_princ_size ( krb_ctx , principal ) ! = 2 ) {
2010-11-08 11:01:36 +03:00
krb5_free_principal ( krb_ctx , principal ) ;
krb5_free_context ( krb_ctx ) ;
talloc_free ( tmp_ctx ) ;
return false ;
}
2012-05-02 21:22:08 +04:00
component = krb5_princ_component ( krb_ctx , principal , 1 ) ;
dns_name = ( const char * ) component - > data ;
2010-11-08 11:01:36 +03:00
if ( strcasecmp ( dns_name , dnsHostName ) ! = 0 ) {
krb5_free_principal ( krb_ctx , principal ) ;
krb5_free_context ( krb_ctx ) ;
talloc_free ( tmp_ctx ) ;
return false ;
}
/* its a simple update on their own account - allow it with
* permissions override */
krb5_free_principal ( krb_ctx , principal ) ;
krb5_free_context ( krb_ctx ) ;
talloc_free ( tmp_ctx ) ;
return true ;
}
2010-01-09 09:11:01 +03:00
/*
drsuapi_DsWriteAccountSpn
*/
WERROR dcesrv_drsuapi_DsWriteAccountSpn ( struct dcesrv_call_state * dce_call , TALLOC_CTX * mem_ctx ,
struct drsuapi_DsWriteAccountSpn * r )
{
struct drsuapi_bind_state * b_state ;
struct dcesrv_handle * h ;
* r - > out . level_out = r - > in . level ;
DCESRV_PULL_HANDLE_WERR ( h , r - > in . bind_handle , DRSUAPI_BIND_HANDLE ) ;
b_state = h - > data ;
r - > out . res = talloc ( mem_ctx , union drsuapi_DsWriteAccountSpnResult ) ;
W_ERROR_HAVE_NO_MEMORY ( r - > out . res ) ;
switch ( r - > in . level ) {
case 1 : {
struct drsuapi_DsWriteAccountSpnRequest1 * req ;
struct ldb_message * msg ;
2010-10-31 13:01:00 +03:00
uint32_t count ;
unsigned int i ;
int ret ;
2010-01-09 09:42:59 +03:00
unsigned spn_count = 0 ;
2010-11-08 11:01:36 +03:00
bool passed_checks = true ;
2010-11-13 11:08:45 +03:00
struct ldb_context * sam_ctx ;
2010-01-09 09:42:59 +03:00
2010-01-09 09:11:01 +03:00
req = & r - > in . req - > req1 ;
count = req - > count ;
msg = ldb_msg_new ( mem_ctx ) ;
if ( msg = = NULL ) {
2015-12-03 17:24:19 +03:00
return WERR_NOT_ENOUGH_MEMORY ;
2010-01-09 09:11:01 +03:00
}
2010-11-08 11:58:59 +03:00
msg - > dn = ldb_dn_new ( msg , b_state - > sam_ctx ,
req - > object_dn ) ;
2010-01-09 09:11:01 +03:00
if ( ! ldb_dn_validate ( msg - > dn ) ) {
r - > out . res - > res1 . status = WERR_OK ;
return WERR_OK ;
}
/* construct mods */
for ( i = 0 ; i < count ; i + + ) {
2010-11-08 11:01:36 +03:00
if ( ! writespn_check_spn ( b_state ,
dce_call ,
msg - > dn ,
req - > spn_names [ i ] . str ) ) {
passed_checks = false ;
}
2010-10-15 00:26:44 +04:00
ret = ldb_msg_add_string ( msg ,
" servicePrincipalName " ,
req - > spn_names [ i ] . str ) ;
2010-01-09 09:42:59 +03:00
if ( ret ! = LDB_SUCCESS ) {
2015-12-03 17:24:19 +03:00
return WERR_NOT_ENOUGH_MEMORY ;
2010-01-09 09:42:59 +03:00
}
spn_count + + ;
2010-01-09 09:11:01 +03:00
}
2010-01-09 10:10:38 +03:00
if ( msg - > num_elements = = 0 ) {
2010-11-08 11:58:59 +03:00
DEBUG ( 2 , ( " No SPNs need changing on %s \n " ,
ldb_dn_get_linearized ( msg - > dn ) ) ) ;
2010-01-09 10:10:38 +03:00
r - > out . res - > res1 . status = WERR_OK ;
return WERR_OK ;
}
2010-01-09 09:11:01 +03:00
for ( i = 0 ; i < msg - > num_elements ; i + + ) {
switch ( req - > operation ) {
case DRSUAPI_DS_SPN_OPERATION_ADD :
msg - > elements [ i ] . flags = LDB_FLAG_MOD_ADD ;
break ;
case DRSUAPI_DS_SPN_OPERATION_REPLACE :
msg - > elements [ i ] . flags = LDB_FLAG_MOD_REPLACE ;
break ;
case DRSUAPI_DS_SPN_OPERATION_DELETE :
msg - > elements [ i ] . flags = LDB_FLAG_MOD_DELETE ;
break ;
}
}
2010-11-13 11:08:45 +03:00
if ( passed_checks & & b_state - > sam_ctx_system ) {
sam_ctx = b_state - > sam_ctx_system ;
} else {
sam_ctx = b_state - > sam_ctx ;
}
2010-01-09 09:11:01 +03:00
/* Apply to database */
2010-11-13 11:08:45 +03:00
ret = dsdb_modify ( sam_ctx , msg , DSDB_MODIFY_PERMISSIVE ) ;
2010-10-31 13:01:00 +03:00
if ( ret ! = LDB_SUCCESS ) {
2010-01-09 09:11:01 +03:00
DEBUG ( 0 , ( " Failed to modify SPNs on %s: %s \n " ,
ldb_dn_get_linearized ( msg - > dn ) ,
ldb_errstring ( b_state - > sam_ctx ) ) ) ;
2016-05-24 16:10:14 +03:00
NDR_PRINT_IN_DEBUG (
drsuapi_DsWriteAccountSpn , r ) ;
2010-01-09 09:11:01 +03:00
r - > out . res - > res1 . status = WERR_ACCESS_DENIED ;
} else {
2010-11-08 11:58:59 +03:00
DEBUG ( 2 , ( " Modified %u SPNs on %s \n " , spn_count ,
ldb_dn_get_linearized ( msg - > dn ) ) ) ;
2010-01-09 09:11:01 +03:00
r - > out . res - > res1 . status = WERR_OK ;
}
return WERR_OK ;
}
}
2015-12-03 17:24:42 +03:00
return WERR_INVALID_LEVEL ;
2010-01-09 09:11:01 +03:00
}