2001-11-25 03:18:11 +03:00
/*
Samba Unix / Linux SMB client library
net ads commands
Copyright ( C ) 2001 Andrew Tridgell ( tridge @ samba . org )
2001-12-20 06:54:52 +03:00
Copyright ( C ) 2001 Remus Koos ( remuskoos @ yahoo . com )
2002-02-02 05:06:03 +03:00
Copyright ( C ) 2002 Jim McDonough ( jmcd @ us . ibm . com )
2006-05-13 08:39:19 +04:00
Copyright ( C ) 2006 Gerald ( Jerry ) Carter ( jerry @ samba . org )
2001-11-25 03:18:11 +03:00
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 2 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 , write to the Free Software
Foundation , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*/
# include "includes.h"
2004-10-07 08:01:18 +04:00
# include "utils/net.h"
2001-11-25 03:18:11 +03:00
# ifdef HAVE_ADS
2001-11-26 07:53:08 +03:00
int net_ads_usage ( int argc , const char * * argv )
2001-11-25 03:18:11 +03:00
{
2006-07-26 19:26:51 +04:00
d_printf ( " join [createupn[=principal]] [createcomputer=<org_unit>] \n " ) ;
d_printf ( " Join the local machine to a ADS realm \n " ) ;
d_printf ( " leave \n " ) ;
d_printf ( " Remove the local machine from a ADS realm \n " ) ;
d_printf ( " testjoin \n " ) ;
d_printf ( " Validates the machine account in the domain \n " ) ;
d_printf ( " user \n " ) ;
d_printf ( " List, add, or delete users in the realm \n " ) ;
d_printf ( " group \n " ) ;
d_printf ( " List, add, or delete groups in the realm \n " ) ;
d_printf ( " info \n " ) ;
d_printf ( " Displays details regarding a specific AD server \n " ) ;
d_printf ( " status \n " ) ;
d_printf ( " Display details regarding the machine's account in AD \n " ) ;
d_printf ( " lookup \n " ) ;
d_printf ( " Performs CLDAP query of AD domain controllers \n " ) ;
d_printf ( " password <username@realm> <password> -Uadmin_username@realm%%admin_pass \n " ) ;
d_printf ( " Change a user's password using an admin account \n " ) ;
d_printf ( " (note: use realm in UPPERCASE, prompts if password is obmitted) \n " ) ;
d_printf ( " changetrustpw \n " ) ;
d_printf ( " Change the trust account password of this machine in the AD tree \n " ) ;
d_printf ( " printer [info | publish | remove] <printername> <servername> \n " ) ;
d_printf ( " Lookup, add, or remove directory entry for a printer \n " ) ;
d_printf ( " {search,dn,sid} \n " ) ;
d_printf ( " Issue LDAP search queries using a general filter, by DN, or by SID \n " ) ;
d_printf ( " keytab \n " ) ;
d_printf ( " Manage a local keytab file based on the machine account in AD \n " ) ;
2006-08-24 19:43:32 +04:00
d_printf ( " dns \n " ) ;
d_printf ( " Issue a dynamic DNS update request the server's hostname \n " ) ;
d_printf ( " (using the machine credentials) \n " ) ;
2006-07-26 19:26:51 +04:00
2001-11-25 03:18:11 +03:00
return - 1 ;
}
2006-07-17 15:04:47 +04:00
/* when we do not have sufficient input parameters to contact a remote domain
* we always fall back to our own realm - Guenther */
static const char * assume_own_realm ( void )
{
if ( ! opt_host & & strequal ( lp_workgroup ( ) , opt_target_workgroup ) ) {
return lp_realm ( ) ;
}
return NULL ;
}
2001-12-13 16:19:20 +03:00
2006-05-12 19:17:35 +04:00
/*
do a cldap netlogon query
*/
static int net_ads_cldap_netlogon ( ADS_STRUCT * ads )
{
struct cldap_netlogon_reply reply ;
if ( ! ads_cldap_netlogon ( inet_ntoa ( ads - > ldap_ip ) , ads - > server . realm , & reply ) ) {
d_fprintf ( stderr , " CLDAP query failed! \n " ) ;
return - 1 ;
}
d_printf ( " Information for Domain Controller: %s \n \n " ,
inet_ntoa ( ads - > ldap_ip ) ) ;
d_printf ( " Response Type: " ) ;
switch ( reply . type ) {
case SAMLOGON_AD_UNK_R :
d_printf ( " SAMLOGON \n " ) ;
break ;
case SAMLOGON_AD_R :
d_printf ( " SAMLOGON_USER \n " ) ;
break ;
default :
d_printf ( " 0x%x \n " , reply . type ) ;
break ;
}
d_printf ( " GUID: %s \n " ,
smb_uuid_string_static ( smb_uuid_unpack_static ( reply . guid ) ) ) ;
d_printf ( " Flags: \n "
" \t Is a PDC: %s \n "
" \t Is a GC of the forest: %s \n "
" \t Is an LDAP server: %s \n "
" \t Supports DS: %s \n "
" \t Is running a KDC: %s \n "
" \t Is running time services: %s \n "
" \t Is the closest DC: %s \n "
" \t Is writable: %s \n "
" \t Has a hardware clock: %s \n "
" \t Is a non-domain NC serviced by LDAP server: %s \n " ,
( reply . flags & ADS_PDC ) ? " yes " : " no " ,
( reply . flags & ADS_GC ) ? " yes " : " no " ,
( reply . flags & ADS_LDAP ) ? " yes " : " no " ,
( reply . flags & ADS_DS ) ? " yes " : " no " ,
( reply . flags & ADS_KDC ) ? " yes " : " no " ,
( reply . flags & ADS_TIMESERV ) ? " yes " : " no " ,
( reply . flags & ADS_CLOSEST ) ? " yes " : " no " ,
( reply . flags & ADS_WRITABLE ) ? " yes " : " no " ,
( reply . flags & ADS_GOOD_TIMESERV ) ? " yes " : " no " ,
( reply . flags & ADS_NDNC ) ? " yes " : " no " ) ;
printf ( " Forest: \t \t \t %s \n " , reply . forest ) ;
printf ( " Domain: \t \t \t %s \n " , reply . domain ) ;
printf ( " Domain Controller: \t %s \n " , reply . hostname ) ;
printf ( " Pre-Win2k Domain: \t %s \n " , reply . netbios_domain ) ;
printf ( " Pre-Win2k Hostname: \t %s \n " , reply . netbios_hostname ) ;
if ( * reply . unk ) printf ( " Unk: \t \t \t %s \n " , reply . unk ) ;
if ( * reply . user_name ) printf ( " User name: \t %s \n " , reply . user_name ) ;
2006-08-30 08:40:03 +04:00
printf ( " Server Site Name : \t \t %s \n " , reply . server_site_name ) ;
2006-08-31 03:56:40 +04:00
printf ( " Client Site Name : \t \t %s \n " , reply . client_site_name ) ;
2006-05-12 19:17:35 +04:00
d_printf ( " NT Version: %d \n " , reply . version ) ;
d_printf ( " LMNT Token: %.2x \n " , reply . lmnt_token ) ;
d_printf ( " LM20 Token: %.2x \n " , reply . lm20_token ) ;
2006-05-13 05:29:04 +04:00
return 0 ;
2006-05-12 19:17:35 +04:00
}
2002-09-25 19:19:00 +04:00
/*
this implements the CLDAP based netlogon lookup requests
for finding the domain controller of a ADS domain
*/
static int net_ads_lookup ( int argc , const char * * argv )
{
ADS_STRUCT * ads ;
2006-10-13 01:03:28 +04:00
if ( ! ADS_ERR_OK ( ads_startup_nobind ( False , & ads ) ) ) {
2006-01-18 00:22:00 +03:00
d_fprintf ( stderr , " Didn't find the cldap server! \n " ) ;
2002-09-25 19:19:00 +04:00
return - 1 ;
2006-03-29 19:30:26 +04:00
}
2006-10-13 01:03:28 +04:00
2006-03-29 19:30:26 +04:00
if ( ! ads - > config . realm ) {
2005-07-21 13:28:12 +04:00
ads - > config . realm = CONST_DISCARD ( char * , opt_target_workgroup ) ;
2004-08-12 07:28:57 +04:00
ads - > ldap_port = 389 ;
2002-09-25 19:19:00 +04:00
}
2006-05-12 19:17:35 +04:00
return net_ads_cldap_netlogon ( ads ) ;
2002-09-25 19:19:00 +04:00
}
2001-12-13 16:19:20 +03:00
static int net_ads_info ( int argc , const char * * argv )
{
ADS_STRUCT * ads ;
2006-10-13 01:03:28 +04:00
if ( ! ADS_ERR_OK ( ads_startup_nobind ( False , & ads ) ) ) {
d_fprintf ( stderr , " Didn't find the ldap server! \n " ) ;
return - 1 ;
2002-08-17 18:45:04 +04:00
}
if ( ! ads | | ! ads - > config . realm ) {
2006-01-18 00:22:00 +03:00
d_fprintf ( stderr , " Didn't find the ldap server! \n " ) ;
2001-12-13 16:19:20 +03:00
return - 1 ;
}
2006-05-12 19:17:35 +04:00
/* Try to set the server's current time since we didn't do a full
TCP LDAP session initially */
if ( ! ADS_ERR_OK ( ads_current_time ( ads ) ) ) {
d_fprintf ( stderr , " Failed to get server's current time! \n " ) ;
}
2002-08-17 18:45:04 +04:00
d_printf ( " LDAP server: %s \n " , inet_ntoa ( ads - > ldap_ip ) ) ;
d_printf ( " LDAP server name: %s \n " , ads - > config . ldap_server_name ) ;
d_printf ( " Realm: %s \n " , ads - > config . realm ) ;
d_printf ( " Bind Path: %s \n " , ads - > config . bind_path ) ;
2001-12-13 16:19:20 +03:00
d_printf ( " LDAP port: %d \n " , ads - > ldap_port ) ;
2002-09-25 19:19:00 +04:00
d_printf ( " Server time: %s \n " , http_timestring ( ads - > config . current_time ) ) ;
2001-12-13 16:19:20 +03:00
2004-06-23 04:20:31 +04:00
d_printf ( " KDC server: %s \n " , ads - > auth . kdc_server ) ;
d_printf ( " Server time offset: %d \n " , ads - > auth . time_offset ) ;
2003-05-04 06:48:11 +04:00
2001-12-13 16:19:20 +03:00
return 0 ;
}
2003-02-24 06:06:45 +03:00
static void use_in_memory_ccache ( void ) {
/* Use in-memory credentials cache so we do not interfere with
* existing credentials */
setenv ( KRB5_ENV_CCNAME , " MEMORY:net_ads " , 1 ) ;
}
2001-12-13 16:19:20 +03:00
2006-10-13 01:03:28 +04:00
static ADS_STATUS ads_startup_int ( BOOL only_own_domain , uint32 auth_flags , ADS_STRUCT * * ads_ret )
2001-11-25 03:18:11 +03:00
{
2006-10-13 01:03:28 +04:00
ADS_STRUCT * ads = NULL ;
2001-12-19 15:21:12 +03:00
ADS_STATUS status ;
2002-01-26 01:07:46 +03:00
BOOL need_password = False ;
BOOL second_time = False ;
2003-09-04 23:45:04 +04:00
char * cp ;
2006-07-17 15:04:47 +04:00
const char * realm = NULL ;
2006-10-13 01:03:28 +04:00
BOOL tried_closest_dc = False ;
2003-09-04 23:45:04 +04:00
/* lp_realm() should be handled by a command line param,
However , the join requires that realm be set in smb . conf
and compares our realm with the remote server ' s so this is
ok until someone needs more flexibility */
2006-07-17 15:04:47 +04:00
2006-10-13 01:03:28 +04:00
* ads_ret = NULL ;
retry_connect :
2006-07-17 15:04:47 +04:00
if ( only_own_domain ) {
realm = lp_realm ( ) ;
2006-10-13 01:03:28 +04:00
} else {
realm = assume_own_realm ( ) ;
2006-07-17 15:04:47 +04:00
}
2006-10-13 01:03:28 +04:00
ads = ads_init ( realm , opt_target_workgroup , opt_host ) ;
2001-11-25 04:06:56 +03:00
2001-12-08 14:18:56 +03:00
if ( ! opt_user_name ) {
opt_user_name = " administrator " ;
}
2003-02-24 06:06:45 +03:00
if ( opt_user_specified ) {
2002-01-26 01:07:46 +03:00
need_password = True ;
2003-02-24 06:06:45 +03:00
}
2002-01-26 01:07:46 +03:00
retry :
2003-10-23 18:33:19 +04:00
if ( ! opt_password & & need_password & & ! opt_machine_pass ) {
2006-10-13 01:03:28 +04:00
char * prompt = NULL ;
2004-03-14 06:47:03 +03:00
asprintf ( & prompt , " %s's password: " , opt_user_name ) ;
2006-10-13 01:03:28 +04:00
if ( ! prompt ) {
ads_destroy ( & ads ) ;
return ADS_ERROR ( LDAP_NO_MEMORY ) ;
}
2001-12-08 14:18:56 +03:00
opt_password = getpass ( prompt ) ;
free ( prompt ) ;
}
2002-01-26 01:07:46 +03:00
2003-03-18 01:33:34 +03:00
if ( opt_password ) {
use_in_memory_ccache ( ) ;
2006-10-13 01:03:28 +04:00
SAFE_FREE ( ads - > auth . password ) ;
ads - > auth . password = smb_xstrdup ( opt_password ) ;
2003-03-18 01:33:34 +03:00
}
2002-02-17 01:11:49 +03:00
2006-10-13 01:03:28 +04:00
ads - > auth . flags | = auth_flags ;
SAFE_FREE ( ads - > auth . user_name ) ;
ads - > auth . user_name = smb_xstrdup ( opt_user_name ) ;
2003-05-04 06:48:11 +04:00
/*
* If the username is of the form " name@realm " ,
* extract the realm and convert to upper case .
* This is only used to establish the connection .
*/
2006-10-13 01:03:28 +04:00
if ( ( cp = strchr_m ( ads - > auth . user_name , ' @ ' ) ) ! = 0 ) {
* cp + + = ' \0 ' ;
SAFE_FREE ( ads - > auth . realm ) ;
ads - > auth . realm = smb_xstrdup ( cp ) ;
strupper_m ( ads - > auth . realm ) ;
2003-05-04 06:48:11 +04:00
}
2001-12-08 14:18:56 +03:00
2006-10-13 01:03:28 +04:00
status = ads_connect ( ads ) ;
2003-05-04 06:48:11 +04:00
2001-12-19 15:21:12 +03:00
if ( ! ADS_ERR_OK ( status ) ) {
2006-10-25 16:10:48 +04:00
if ( NT_STATUS_EQUAL ( ads_ntstatus ( status ) ,
NT_STATUS_NO_LOGON_SERVERS ) ) {
DEBUG ( 0 , ( " ads_connect: %s \n " , ads_errstr ( status ) ) ) ;
ads_destroy ( & ads ) ;
return status ;
}
2007-01-17 21:25:35 +03:00
if ( ! need_password & & ! second_time & & ! ( auth_flags & ADS_AUTH_NO_BIND ) ) {
2002-01-26 01:07:46 +03:00
need_password = True ;
second_time = True ;
goto retry ;
} else {
2006-10-13 01:03:28 +04:00
ads_destroy ( & ads ) ;
return status ;
}
}
/* when contacting our own domain, make sure we use the closest DC.
* This is done by reconnecting to ADS because only the first call to
* ads_connect will give us our own sitename */
if ( ( only_own_domain | | ! opt_host ) & & ! tried_closest_dc ) {
tried_closest_dc = True ; /* avoid loop */
2007-01-17 21:25:35 +03:00
if ( ! ads - > config . tried_closest_dc ) {
2006-10-13 01:03:28 +04:00
namecache_delete ( ads - > server . realm , 0x1C ) ;
namecache_delete ( ads - > server . workgroup , 0x1C ) ;
ads_destroy ( & ads ) ;
ads = NULL ;
goto retry_connect ;
2002-01-26 01:07:46 +03:00
}
2001-11-25 04:31:07 +03:00
}
2006-10-13 01:03:28 +04:00
* ads_ret = ads ;
2006-08-17 16:44:59 +04:00
return status ;
2001-11-25 04:31:07 +03:00
}
2006-10-13 01:03:28 +04:00
ADS_STATUS ads_startup ( BOOL only_own_domain , ADS_STRUCT * * ads )
{
return ads_startup_int ( only_own_domain , 0 , ads ) ;
}
ADS_STATUS ads_startup_nobind ( BOOL only_own_domain , ADS_STRUCT * * ads )
{
return ads_startup_int ( only_own_domain , ADS_AUTH_NO_BIND , ads ) ;
}
2002-04-04 20:47:24 +04:00
/*
Check to see if connection can be made via ads .
ads_startup ( ) stores the password in opt_password if it needs to so
that rpc or rap can use it without re - prompting .
*/
2006-07-17 15:04:47 +04:00
static int net_ads_check_int ( const char * realm , const char * workgroup , const char * host )
2002-04-04 20:47:24 +04:00
{
ADS_STRUCT * ads ;
2006-05-12 19:17:35 +04:00
ADS_STATUS status ;
2002-04-04 20:47:24 +04:00
2006-07-17 15:04:47 +04:00
if ( ( ads = ads_init ( realm , workgroup , host ) ) = = NULL ) {
2002-04-04 20:47:24 +04:00
return - 1 ;
2006-05-12 19:17:35 +04:00
}
ads - > auth . flags | = ADS_AUTH_NO_BIND ;
status = ads_connect ( ads ) ;
if ( ! ADS_ERR_OK ( status ) ) {
return - 1 ;
}
2002-04-04 20:47:24 +04:00
ads_destroy ( & ads ) ;
return 0 ;
}
2006-07-17 15:04:47 +04:00
int net_ads_check_our_domain ( void )
{
return net_ads_check_int ( lp_realm ( ) , lp_workgroup ( ) , NULL ) ;
}
int net_ads_check ( void )
{
return net_ads_check_int ( NULL , opt_workgroup , opt_host ) ;
}
2002-08-17 18:45:04 +04:00
/*
determine the netbios workgroup name for a domain
*/
static int net_ads_workgroup ( int argc , const char * * argv )
{
ADS_STRUCT * ads ;
2006-05-12 19:17:35 +04:00
struct cldap_netlogon_reply reply ;
2002-08-17 18:45:04 +04:00
2006-10-13 01:03:28 +04:00
if ( ! ADS_ERR_OK ( ads_startup_nobind ( False , & ads ) ) ) {
2006-05-12 19:17:35 +04:00
d_fprintf ( stderr , " Didn't find the cldap server! \n " ) ;
return - 1 ;
}
if ( ! ads - > config . realm ) {
ads - > config . realm = CONST_DISCARD ( char * , opt_target_workgroup ) ;
ads - > ldap_port = 389 ;
}
if ( ! ads_cldap_netlogon ( inet_ntoa ( ads - > ldap_ip ) , ads - > server . realm , & reply ) ) {
d_fprintf ( stderr , " CLDAP query failed! \n " ) ;
2002-08-17 18:45:04 +04:00
return - 1 ;
}
2002-04-04 20:47:24 +04:00
2006-05-12 19:17:35 +04:00
d_printf ( " Workgroup: %s \n " , reply . netbios_domain ) ;
2002-08-17 18:45:04 +04:00
2004-06-23 04:20:31 +04:00
ads_destroy ( & ads ) ;
2006-05-12 19:17:35 +04:00
2002-08-17 18:45:04 +04:00
return 0 ;
}
2002-09-25 19:19:00 +04:00
static BOOL usergrp_display ( char * field , void * * values , void * data_area )
2002-03-30 00:09:44 +03:00
{
char * * disp_fields = ( char * * ) data_area ;
if ( ! field ) { /* must be end of record */
2005-05-17 02:54:46 +04:00
if ( disp_fields [ 0 ] ) {
if ( ! strchr_m ( disp_fields [ 0 ] , ' $ ' ) ) {
if ( disp_fields [ 1 ] )
d_printf ( " %-21.21s %s \n " ,
disp_fields [ 0 ] , disp_fields [ 1 ] ) ;
else
d_printf ( " %s \n " , disp_fields [ 0 ] ) ;
}
2002-04-05 23:28:02 +04:00
}
2002-03-30 00:09:44 +03:00
SAFE_FREE ( disp_fields [ 0 ] ) ;
SAFE_FREE ( disp_fields [ 1 ] ) ;
2002-09-25 19:19:00 +04:00
return True ;
2002-03-30 00:09:44 +03:00
}
2002-07-15 14:35:28 +04:00
if ( ! values ) /* must be new field, indicate string field */
2002-09-25 19:19:00 +04:00
return True ;
2002-03-30 00:09:44 +03:00
if ( StrCaseCmp ( field , " sAMAccountName " ) = = 0 ) {
2004-12-07 21:25:53 +03:00
disp_fields [ 0 ] = SMB_STRDUP ( ( char * ) values [ 0 ] ) ;
2002-03-30 00:09:44 +03:00
}
if ( StrCaseCmp ( field , " description " ) = = 0 )
2004-12-07 21:25:53 +03:00
disp_fields [ 1 ] = SMB_STRDUP ( ( char * ) values [ 0 ] ) ;
2002-09-25 19:19:00 +04:00
return True ;
2002-03-30 00:09:44 +03:00
}
2002-04-04 06:53:42 +04:00
static int net_ads_user_usage ( int argc , const char * * argv )
{
2002-04-05 05:36:28 +04:00
return net_help_user ( argc , argv ) ;
2002-04-04 06:53:42 +04:00
}
static int ads_user_add ( int argc , const char * * argv )
{
ADS_STRUCT * ads ;
ADS_STATUS status ;
2002-07-15 14:35:28 +04:00
char * upn , * userdn ;
2006-09-04 01:07:16 +04:00
LDAPMessage * res = NULL ;
2002-04-04 06:53:42 +04:00
int rc = - 1 ;
2006-11-01 14:19:33 +03:00
char * ou_str = NULL ;
2002-04-04 06:53:42 +04:00
if ( argc < 1 ) return net_ads_user_usage ( argc , argv ) ;
2006-08-17 16:44:59 +04:00
if ( ! ADS_ERR_OK ( ads_startup ( False , & ads ) ) ) {
2004-06-23 04:20:31 +04:00
return - 1 ;
}
2002-04-04 06:53:42 +04:00
status = ads_find_user_acct ( ads , & res , argv [ 0 ] ) ;
if ( ! ADS_ERR_OK ( status ) ) {
2006-01-18 00:22:00 +03:00
d_fprintf ( stderr , " ads_user_add: %s \n " , ads_errstr ( status ) ) ;
2002-04-04 06:53:42 +04:00
goto done ;
}
if ( ads_count_replies ( ads , res ) ) {
2006-01-18 00:22:00 +03:00
d_fprintf ( stderr , " ads_user_add: User %s already exists \n " , argv [ 0 ] ) ;
2002-04-04 06:53:42 +04:00
goto done ;
}
2006-11-01 14:19:33 +03:00
if ( opt_container ) {
ou_str = SMB_STRDUP ( opt_container ) ;
} else {
ou_str = ads_default_ou_string ( ads , WELL_KNOWN_GUID_USERS ) ;
2004-10-06 20:21:35 +04:00
}
2006-11-01 14:19:33 +03:00
status = ads_add_user_acct ( ads , argv [ 0 ] , ou_str , opt_comment ) ;
2002-04-04 06:53:42 +04:00
2002-07-15 14:35:28 +04:00
if ( ! ADS_ERR_OK ( status ) ) {
2006-01-18 00:22:00 +03:00
d_fprintf ( stderr , " Could not add user %s: %s \n " , argv [ 0 ] ,
2002-07-15 14:35:28 +04:00
ads_errstr ( status ) ) ;
goto done ;
}
/* if no password is to be set, we're done */
if ( argc = = 1 ) {
d_printf ( " User %s added \n " , argv [ 0 ] ) ;
rc = 0 ;
goto done ;
}
/* try setting the password */
2002-08-17 18:45:04 +04:00
asprintf ( & upn , " %s@%s " , argv [ 0 ] , ads - > config . realm ) ;
2003-05-30 23:51:09 +04:00
status = ads_krb5_set_password ( ads - > auth . kdc_server , upn , argv [ 1 ] ,
ads - > auth . time_offset ) ;
2002-07-15 14:35:28 +04:00
safe_free ( upn ) ;
2002-04-04 06:53:42 +04:00
if ( ADS_ERR_OK ( status ) ) {
d_printf ( " User %s added \n " , argv [ 0 ] ) ;
rc = 0 ;
2002-07-15 14:35:28 +04:00
goto done ;
}
/* password didn't set, delete account */
2006-01-18 00:22:00 +03:00
d_fprintf ( stderr , " Could not add user %s. Error setting password %s \n " ,
2002-07-15 14:35:28 +04:00
argv [ 0 ] , ads_errstr ( status ) ) ;
ads_msgfree ( ads , res ) ;
status = ads_find_user_acct ( ads , & res , argv [ 0 ] ) ;
if ( ADS_ERR_OK ( status ) ) {
userdn = ads_get_dn ( ads , res ) ;
ads_del_dn ( ads , userdn ) ;
ads_memfree ( ads , userdn ) ;
2002-04-04 06:53:42 +04:00
}
done :
if ( res )
ads_msgfree ( ads , res ) ;
ads_destroy ( & ads ) ;
2006-11-01 14:19:33 +03:00
SAFE_FREE ( ou_str ) ;
2002-04-04 06:53:42 +04:00
return rc ;
}
static int ads_user_info ( int argc , const char * * argv )
2001-11-25 04:31:07 +03:00
{
ADS_STRUCT * ads ;
2001-12-19 15:21:12 +03:00
ADS_STATUS rc ;
2006-09-04 01:07:16 +04:00
LDAPMessage * res ;
2002-04-04 06:53:42 +04:00
const char * attrs [ ] = { " memberOf " , NULL } ;
char * searchstring = NULL ;
char * * grouplist ;
2005-05-19 14:52:36 +04:00
char * escaped_user ;
2002-04-04 06:53:42 +04:00
2004-06-23 04:20:31 +04:00
if ( argc < 1 ) {
return net_ads_user_usage ( argc , argv ) ;
}
2005-05-19 14:52:36 +04:00
escaped_user = escape_ldap_string_alloc ( argv [ 0 ] ) ;
2001-11-25 04:31:07 +03:00
2003-02-01 10:59:29 +03:00
if ( ! escaped_user ) {
2006-01-18 00:22:00 +03:00
d_fprintf ( stderr , " ads_user_info: failed to escape user %s \n " , argv [ 0 ] ) ;
2006-03-09 23:51:22 +03:00
return - 1 ;
}
2006-08-17 16:44:59 +04:00
if ( ! ADS_ERR_OK ( ads_startup ( False , & ads ) ) ) {
2006-03-09 23:51:22 +03:00
SAFE_FREE ( escaped_user ) ;
return - 1 ;
2003-02-01 10:59:29 +03:00
}
asprintf ( & searchstring , " (sAMAccountName=%s) " , escaped_user ) ;
2002-04-04 06:53:42 +04:00
rc = ads_search ( ads , & res , searchstring , attrs ) ;
safe_free ( searchstring ) ;
2001-11-25 04:31:07 +03:00
2002-03-20 01:16:19 +03:00
if ( ! ADS_ERR_OK ( rc ) ) {
2006-01-18 00:22:00 +03:00
d_fprintf ( stderr , " ads_search: %s \n " , ads_errstr ( rc ) ) ;
2004-06-23 04:20:31 +04:00
ads_destroy ( & ads ) ;
2006-03-09 23:51:22 +03:00
SAFE_FREE ( escaped_user ) ;
2002-03-20 01:16:19 +03:00
return - 1 ;
}
2002-03-30 00:09:44 +03:00
2006-07-11 22:01:26 +04:00
grouplist = ldap_get_values ( ( LDAP * ) ads - > ld ,
( LDAPMessage * ) res , " memberOf " ) ;
2002-04-04 06:53:42 +04:00
if ( grouplist ) {
int i ;
char * * groupname ;
for ( i = 0 ; grouplist [ i ] ; i + + ) {
groupname = ldap_explode_dn ( grouplist [ i ] , 1 ) ;
2002-07-15 14:35:28 +04:00
d_printf ( " %s \n " , groupname [ 0 ] ) ;
2002-04-04 06:53:42 +04:00
ldap_value_free ( groupname ) ;
}
ldap_value_free ( grouplist ) ;
}
2002-03-30 00:09:44 +03:00
ads_msgfree ( ads , res ) ;
2001-11-29 09:21:56 +03:00
ads_destroy ( & ads ) ;
2006-03-09 23:51:22 +03:00
SAFE_FREE ( escaped_user ) ;
2001-11-25 04:31:07 +03:00
return 0 ;
}
2002-04-04 06:53:42 +04:00
static int ads_user_delete ( int argc , const char * * argv )
{
ADS_STRUCT * ads ;
ADS_STATUS rc ;
2006-11-01 13:38:54 +03:00
LDAPMessage * res = NULL ;
2002-04-04 06:53:42 +04:00
char * userdn ;
2004-06-23 04:20:31 +04:00
if ( argc < 1 ) {
return net_ads_user_usage ( argc , argv ) ;
}
2002-04-04 06:53:42 +04:00
2006-08-17 16:44:59 +04:00
if ( ! ADS_ERR_OK ( ads_startup ( False , & ads ) ) ) {
2004-06-23 04:20:31 +04:00
return - 1 ;
}
2002-04-04 06:53:42 +04:00
rc = ads_find_user_acct ( ads , & res , argv [ 0 ] ) ;
2006-11-01 13:38:54 +03:00
if ( ! ADS_ERR_OK ( rc ) | | ads_count_replies ( ads , res ) ! = 1 ) {
2006-08-15 13:53:16 +04:00
d_printf ( " User %s does not exist. \n " , argv [ 0 ] ) ;
2006-11-01 13:38:54 +03:00
ads_msgfree ( ads , res ) ;
2004-06-23 04:20:31 +04:00
ads_destroy ( & ads ) ;
2002-04-04 06:53:42 +04:00
return - 1 ;
}
userdn = ads_get_dn ( ads , res ) ;
ads_msgfree ( ads , res ) ;
rc = ads_del_dn ( ads , userdn ) ;
ads_memfree ( ads , userdn ) ;
2006-11-01 13:38:54 +03:00
if ( ADS_ERR_OK ( rc ) ) {
2002-04-04 06:53:42 +04:00
d_printf ( " User %s deleted \n " , argv [ 0 ] ) ;
2004-06-23 04:20:31 +04:00
ads_destroy ( & ads ) ;
2002-04-04 06:53:42 +04:00
return 0 ;
}
2006-01-18 00:22:00 +03:00
d_fprintf ( stderr , " Error deleting user %s: %s \n " , argv [ 0 ] ,
2002-04-04 07:06:22 +04:00
ads_errstr ( rc ) ) ;
2004-06-23 04:20:31 +04:00
ads_destroy ( & ads ) ;
2002-04-04 06:53:42 +04:00
return - 1 ;
}
2002-04-04 20:47:24 +04:00
int net_ads_user ( int argc , const char * * argv )
2002-04-04 06:53:42 +04:00
{
struct functable func [ ] = {
{ " ADD " , ads_user_add } ,
{ " INFO " , ads_user_info } ,
{ " DELETE " , ads_user_delete } ,
{ NULL , NULL }
} ;
ADS_STRUCT * ads ;
ADS_STATUS rc ;
const char * shortattrs [ ] = { " sAMAccountName " , NULL } ;
const char * longattrs [ ] = { " sAMAccountName " , " description " , NULL } ;
char * disp_fields [ 2 ] = { NULL , NULL } ;
if ( argc = = 0 ) {
2006-08-17 16:44:59 +04:00
if ( ! ADS_ERR_OK ( ads_startup ( False , & ads ) ) ) {
2004-06-23 04:20:31 +04:00
return - 1 ;
}
2002-04-04 06:53:42 +04:00
if ( opt_long_list_entries )
d_printf ( " \n User name Comment " \
" \n ----------------------------- \n " ) ;
2002-08-17 18:45:04 +04:00
rc = ads_do_search_all_fn ( ads , ads - > config . bind_path ,
2002-04-10 17:29:23 +04:00
LDAP_SCOPE_SUBTREE ,
2006-05-05 19:44:00 +04:00
" (objectCategory=user) " ,
2002-04-10 17:29:23 +04:00
opt_long_list_entries ? longattrs :
shortattrs , usergrp_display ,
disp_fields ) ;
2002-04-04 06:53:42 +04:00
ads_destroy ( & ads ) ;
2006-07-11 22:01:26 +04:00
return ADS_ERR_OK ( rc ) ? 0 : - 1 ;
2002-04-04 06:53:42 +04:00
}
return net_run_function ( argc , argv , func , net_ads_user_usage ) ;
}
2002-07-15 14:35:28 +04:00
static int net_ads_group_usage ( int argc , const char * * argv )
{
return net_help_group ( argc , argv ) ;
}
static int ads_group_add ( int argc , const char * * argv )
{
ADS_STRUCT * ads ;
ADS_STATUS status ;
2006-09-04 01:07:16 +04:00
LDAPMessage * res = NULL ;
2002-07-15 14:35:28 +04:00
int rc = - 1 ;
2006-11-01 14:19:33 +03:00
char * ou_str = NULL ;
2002-07-15 14:35:28 +04:00
2004-06-23 04:20:31 +04:00
if ( argc < 1 ) {
return net_ads_group_usage ( argc , argv ) ;
}
2002-07-15 14:35:28 +04:00
2006-08-17 16:44:59 +04:00
if ( ! ADS_ERR_OK ( ads_startup ( False , & ads ) ) ) {
2004-06-23 04:20:31 +04:00
return - 1 ;
}
2002-07-15 14:35:28 +04:00
status = ads_find_user_acct ( ads , & res , argv [ 0 ] ) ;
if ( ! ADS_ERR_OK ( status ) ) {
2006-01-18 00:22:00 +03:00
d_fprintf ( stderr , " ads_group_add: %s \n " , ads_errstr ( status ) ) ;
2002-07-15 14:35:28 +04:00
goto done ;
}
if ( ads_count_replies ( ads , res ) ) {
2006-01-18 00:22:00 +03:00
d_fprintf ( stderr , " ads_group_add: Group %s already exists \n " , argv [ 0 ] ) ;
2002-07-15 14:35:28 +04:00
goto done ;
}
2006-11-01 14:19:33 +03:00
if ( opt_container ) {
ou_str = SMB_STRDUP ( opt_container ) ;
} else {
ou_str = ads_default_ou_string ( ads , WELL_KNOWN_GUID_USERS ) ;
2004-10-06 20:21:35 +04:00
}
2006-11-01 14:19:33 +03:00
status = ads_add_group_acct ( ads , argv [ 0 ] , ou_str , opt_comment ) ;
2002-07-15 14:35:28 +04:00
if ( ADS_ERR_OK ( status ) ) {
d_printf ( " Group %s added \n " , argv [ 0 ] ) ;
rc = 0 ;
} else {
2006-01-18 00:22:00 +03:00
d_fprintf ( stderr , " Could not add group %s: %s \n " , argv [ 0 ] ,
2002-07-15 14:35:28 +04:00
ads_errstr ( status ) ) ;
}
done :
if ( res )
ads_msgfree ( ads , res ) ;
ads_destroy ( & ads ) ;
2006-11-01 14:19:33 +03:00
SAFE_FREE ( ou_str ) ;
2002-07-15 14:35:28 +04:00
return rc ;
}
static int ads_group_delete ( int argc , const char * * argv )
{
ADS_STRUCT * ads ;
ADS_STATUS rc ;
2006-11-01 13:59:28 +03:00
LDAPMessage * res = NULL ;
2002-07-15 14:35:28 +04:00
char * groupdn ;
2004-06-23 04:20:31 +04:00
if ( argc < 1 ) {
return net_ads_group_usage ( argc , argv ) ;
}
2002-07-15 14:35:28 +04:00
2006-08-17 16:44:59 +04:00
if ( ! ADS_ERR_OK ( ads_startup ( False , & ads ) ) ) {
2004-06-23 04:20:31 +04:00
return - 1 ;
}
2002-07-15 14:35:28 +04:00
rc = ads_find_user_acct ( ads , & res , argv [ 0 ] ) ;
2006-11-01 13:59:28 +03:00
if ( ! ADS_ERR_OK ( rc ) | | ads_count_replies ( ads , res ) ! = 1 ) {
2006-08-15 13:53:16 +04:00
d_printf ( " Group %s does not exist. \n " , argv [ 0 ] ) ;
2006-11-01 13:59:28 +03:00
ads_msgfree ( ads , res ) ;
2004-06-23 04:20:31 +04:00
ads_destroy ( & ads ) ;
2002-07-15 14:35:28 +04:00
return - 1 ;
}
groupdn = ads_get_dn ( ads , res ) ;
ads_msgfree ( ads , res ) ;
rc = ads_del_dn ( ads , groupdn ) ;
ads_memfree ( ads , groupdn ) ;
2006-11-01 13:59:28 +03:00
if ( ADS_ERR_OK ( rc ) ) {
2002-07-15 14:35:28 +04:00
d_printf ( " Group %s deleted \n " , argv [ 0 ] ) ;
2004-06-23 04:20:31 +04:00
ads_destroy ( & ads ) ;
2002-07-15 14:35:28 +04:00
return 0 ;
}
2006-01-18 00:22:00 +03:00
d_fprintf ( stderr , " Error deleting group %s: %s \n " , argv [ 0 ] ,
2002-07-15 14:35:28 +04:00
ads_errstr ( rc ) ) ;
2004-06-23 04:20:31 +04:00
ads_destroy ( & ads ) ;
2002-07-15 14:35:28 +04:00
return - 1 ;
}
int net_ads_group ( int argc , const char * * argv )
2001-11-25 04:31:07 +03:00
{
2002-07-15 14:35:28 +04:00
struct functable func [ ] = {
{ " ADD " , ads_group_add } ,
{ " DELETE " , ads_group_delete } ,
{ NULL , NULL }
} ;
2001-11-25 04:31:07 +03:00
ADS_STRUCT * ads ;
2001-12-19 15:21:12 +03:00
ADS_STATUS rc ;
2002-03-30 00:09:44 +03:00
const char * shortattrs [ ] = { " sAMAccountName " , NULL } ;
const char * longattrs [ ] = { " sAMAccountName " , " description " , NULL } ;
char * disp_fields [ 2 ] = { NULL , NULL } ;
2001-11-25 04:31:07 +03:00
2002-07-15 14:35:28 +04:00
if ( argc = = 0 ) {
2006-08-17 16:44:59 +04:00
if ( ! ADS_ERR_OK ( ads_startup ( False , & ads ) ) ) {
2004-06-23 04:20:31 +04:00
return - 1 ;
}
2001-11-25 04:31:07 +03:00
2002-07-15 14:35:28 +04:00
if ( opt_long_list_entries )
d_printf ( " \n Group name Comment " \
" \n ----------------------------- \n " ) ;
2002-08-17 18:45:04 +04:00
rc = ads_do_search_all_fn ( ads , ads - > config . bind_path ,
2002-07-15 14:35:28 +04:00
LDAP_SCOPE_SUBTREE ,
2006-05-05 19:44:00 +04:00
" (objectCategory=group) " ,
2002-07-15 14:35:28 +04:00
opt_long_list_entries ? longattrs :
shortattrs , usergrp_display ,
disp_fields ) ;
2002-03-30 00:09:44 +03:00
2002-07-15 14:35:28 +04:00
ads_destroy ( & ads ) ;
2006-07-11 22:01:26 +04:00
return ADS_ERR_OK ( rc ) ? 0 : - 1 ;
2002-07-15 14:35:28 +04:00
}
return net_run_function ( argc , argv , func , net_ads_group_usage ) ;
2001-11-25 04:31:07 +03:00
}
static int net_ads_status ( int argc , const char * * argv )
{
ADS_STRUCT * ads ;
2001-12-19 15:21:12 +03:00
ADS_STATUS rc ;
2006-09-04 01:07:16 +04:00
LDAPMessage * res ;
2001-11-25 04:31:07 +03:00
2006-08-17 16:44:59 +04:00
if ( ! ADS_ERR_OK ( ads_startup ( True , & ads ) ) ) {
2004-06-23 04:20:31 +04:00
return - 1 ;
}
2001-11-25 04:31:07 +03:00
2002-11-13 02:20:50 +03:00
rc = ads_find_machine_acct ( ads , & res , global_myname ( ) ) ;
2001-12-19 15:21:12 +03:00
if ( ! ADS_ERR_OK ( rc ) ) {
2006-01-18 00:22:00 +03:00
d_fprintf ( stderr , " ads_find_machine_acct: %s \n " , ads_errstr ( rc ) ) ;
2004-06-23 04:20:31 +04:00
ads_destroy ( & ads ) ;
2001-11-25 04:06:56 +03:00
return - 1 ;
}
if ( ads_count_replies ( ads , res ) = = 0 ) {
2006-01-18 00:22:00 +03:00
d_fprintf ( stderr , " No machine account for '%s' found \n " , global_myname ( ) ) ;
2004-06-23 04:20:31 +04:00
ads_destroy ( & ads ) ;
2001-11-25 04:06:56 +03:00
return - 1 ;
}
ads_dump ( ads , res ) ;
2004-06-23 04:20:31 +04:00
ads_destroy ( & ads ) ;
2001-11-25 04:06:56 +03:00
return 0 ;
}
2006-05-19 00:12:45 +04:00
/*******************************************************************
Leave an AD domain . Windows XP disables the machine account .
We ' ll try the same . The old code would do an LDAP delete .
That only worked using the machine creds because added the machine
with full control to the computer object ' s ACL .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2006-09-13 17:28:42 +04:00
2001-11-25 04:06:56 +03:00
static int net_ads_leave ( int argc , const char * * argv )
{
2001-12-17 14:16:22 +03:00
ADS_STRUCT * ads = NULL ;
2006-09-13 13:03:42 +04:00
ADS_STATUS adsret ;
2006-09-13 17:28:42 +04:00
NTSTATUS status ;
2006-05-19 00:12:45 +04:00
int ret = - 1 ;
struct cli_state * cli = NULL ;
TALLOC_CTX * ctx ;
DOM_SID * dom_sid = NULL ;
2006-11-17 02:48:46 +03:00
char * short_domain_name = NULL ;
2001-11-25 03:18:11 +03:00
2002-03-10 04:52:09 +03:00
if ( ! secrets_init ( ) ) {
DEBUG ( 1 , ( " Failed to initialise secrets database \n " ) ) ;
2001-12-17 14:16:22 +03:00
return - 1 ;
}
2001-11-25 04:31:07 +03:00
2006-05-19 00:12:45 +04:00
if ( ! ( ctx = talloc_init ( " net_ads_leave " ) ) ) {
2006-08-15 13:53:16 +04:00
d_fprintf ( stderr , " Could not initialise talloc context. \n " ) ;
2006-05-19 00:12:45 +04:00
return - 1 ;
2002-03-10 04:52:09 +03:00
}
2006-05-19 00:12:45 +04:00
/* The finds a DC and takes care of getting the
user creds if necessary */
2006-08-17 16:44:59 +04:00
if ( ! ADS_ERR_OK ( ads_startup ( True , & ads ) ) ) {
2001-11-25 03:18:11 +03:00
return - 1 ;
}
2006-05-19 00:12:45 +04:00
/* make RPC calls here */
if ( ! NT_STATUS_IS_OK ( connect_to_ipc_krb5 ( & cli , & ads - > ldap_ip ,
ads - > config . ldap_server_name ) ) )
{
goto done ;
}
2006-11-17 02:48:46 +03:00
if ( ! NT_STATUS_IS_OK ( netdom_get_domain_sid ( ctx , cli , & short_domain_name , & dom_sid ) ) ) {
2006-05-19 00:12:45 +04:00
goto done ;
}
2006-11-17 02:48:46 +03:00
saf_delete ( short_domain_name ) ;
2006-09-13 17:28:42 +04:00
status = netdom_leave_domain ( ctx , cli , dom_sid ) ;
2006-05-19 00:12:45 +04:00
2006-11-17 02:48:46 +03:00
/* Try and delete it via LDAP - the old way we used to. */
2006-09-13 13:03:42 +04:00
adsret = ads_leave_realm ( ads , global_myname ( ) ) ;
if ( ADS_ERR_OK ( adsret ) ) {
d_printf ( " Deleted account for '%s' in realm '%s' \n " ,
global_myname ( ) , ads - > config . realm ) ;
2006-09-13 17:28:42 +04:00
ret = 0 ;
2006-09-13 13:03:42 +04:00
} else {
2006-09-13 17:28:42 +04:00
/* We couldn't delete it - see if the disable succeeded. */
if ( NT_STATUS_IS_OK ( status ) ) {
d_printf ( " Disabled account for '%s' in realm '%s' \n " ,
global_myname ( ) , ads - > config . realm ) ;
ret = 0 ;
} else {
d_fprintf ( stderr , " Failed to disable machine account for '%s' in realm '%s' \n " ,
global_myname ( ) , ads - > config . realm ) ;
}
2006-09-13 13:03:42 +04:00
}
2006-05-19 00:12:45 +04:00
done :
2006-09-13 17:28:42 +04:00
2006-05-19 00:12:45 +04:00
if ( cli )
cli_shutdown ( cli ) ;
2001-11-25 03:18:11 +03:00
2004-06-23 04:20:31 +04:00
ads_destroy ( & ads ) ;
2006-05-19 00:12:45 +04:00
TALLOC_FREE ( ctx ) ;
return ret ;
2002-08-17 18:45:04 +04:00
}
2006-08-18 16:45:51 +04:00
static NTSTATUS net_ads_join_ok ( void )
2002-08-17 18:45:04 +04:00
{
ADS_STRUCT * ads = NULL ;
2006-08-18 16:45:51 +04:00
ADS_STATUS status ;
2002-08-17 18:45:04 +04:00
if ( ! secrets_init ( ) ) {
DEBUG ( 1 , ( " Failed to initialise secrets database \n " ) ) ;
2006-08-18 16:45:51 +04:00
return NT_STATUS_ACCESS_DENIED ;
2002-08-17 18:45:04 +04:00
}
2003-08-20 02:47:10 +04:00
net_use_machine_password ( ) ;
2001-11-25 03:18:11 +03:00
2006-08-18 16:45:51 +04:00
status = ads_startup ( True , & ads ) ;
if ( ! ADS_ERR_OK ( status ) ) {
return ads_ntstatus ( status ) ;
2002-08-17 18:45:04 +04:00
}
ads_destroy ( & ads ) ;
2006-08-18 16:45:51 +04:00
return NT_STATUS_OK ;
2001-11-25 03:18:11 +03:00
}
2002-08-17 18:45:04 +04:00
/*
check that an existing join is OK
*/
int net_ads_testjoin ( int argc , const char * * argv )
{
2006-08-18 16:45:51 +04:00
NTSTATUS status ;
2003-02-24 06:06:45 +03:00
use_in_memory_ccache ( ) ;
2002-08-17 18:45:04 +04:00
/* Display success or failure */
2006-08-18 16:45:51 +04:00
status = net_ads_join_ok ( ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
fprintf ( stderr , " Join to domain is not valid: %s \n " ,
get_friendly_nt_error_msg ( status ) ) ;
2002-08-17 18:45:04 +04:00
return - 1 ;
}
printf ( " Join is OK \n " ) ;
return 0 ;
}
2006-05-12 19:17:35 +04:00
/*******************************************************************
Simple configu checks before beginning the join
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2001-12-17 14:16:22 +03:00
2006-08-18 16:39:21 +04:00
static NTSTATUS check_ads_config ( void )
2006-05-12 19:17:35 +04:00
{
if ( lp_server_role ( ) ! = ROLE_DOMAIN_MEMBER ) {
d_printf ( " Host is not configured as a member server. \n " ) ;
2006-08-18 16:39:21 +04:00
return NT_STATUS_INVALID_DOMAIN_ROLE ;
2006-04-18 17:22:14 +04:00
}
2006-04-19 19:43:48 +04:00
if ( strlen ( global_myname ( ) ) > 15 ) {
2006-05-06 17:33:14 +04:00
d_printf ( " Our netbios name can be at most 15 chars long, "
2006-08-18 16:39:21 +04:00
" \" %s \" is %u chars long \n " , global_myname ( ) ,
( unsigned int ) strlen ( global_myname ( ) ) ) ;
return NT_STATUS_NAME_TOO_LONG ;
2006-04-19 19:43:48 +04:00
}
2006-04-18 17:22:14 +04:00
2006-05-12 19:17:35 +04:00
if ( lp_security ( ) = = SEC_ADS & & ! * lp_realm ( ) ) {
2007-01-19 17:29:42 +03:00
d_fprintf ( stderr , " realm must be set in in %s for ADS "
" join to succeed. \n " , dyn_CONFIGFILE ) ;
2006-08-18 16:39:21 +04:00
return NT_STATUS_INVALID_PARAMETER ;
2004-06-23 04:20:31 +04:00
}
2001-11-25 03:18:11 +03:00
if ( ! secrets_init ( ) ) {
DEBUG ( 1 , ( " Failed to initialise secrets database \n " ) ) ;
2006-08-18 16:39:21 +04:00
/* This is a good bet for failure of secrets_init ... */
return NT_STATUS_ACCESS_DENIED ;
2001-11-25 03:18:11 +03:00
}
2006-05-12 19:17:35 +04:00
2006-08-18 16:39:21 +04:00
return NT_STATUS_OK ;
2006-05-12 19:17:35 +04:00
}
2001-12-17 14:16:22 +03:00
2006-05-12 19:17:35 +04:00
/*******************************************************************
Do the domain join
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2006-08-15 18:27:20 +04:00
static NTSTATUS net_join_domain ( TALLOC_CTX * ctx , const char * servername ,
2006-11-17 02:48:46 +03:00
struct in_addr * ip , char * * domain ,
DOM_SID * * dom_sid ,
2006-08-15 18:27:20 +04:00
const char * password )
2006-05-12 19:17:35 +04:00
{
2006-08-15 18:27:20 +04:00
NTSTATUS ret = NT_STATUS_UNSUCCESSFUL ;
2006-05-12 19:17:35 +04:00
struct cli_state * cli = NULL ;
2006-08-15 18:27:20 +04:00
ret = connect_to_ipc_krb5 ( & cli , ip , servername ) ;
if ( ! NT_STATUS_IS_OK ( ret ) ) {
2006-05-12 19:17:35 +04:00
goto done ;
2006-08-15 18:27:20 +04:00
}
2006-05-12 19:17:35 +04:00
2006-11-17 02:48:46 +03:00
ret = netdom_get_domain_sid ( ctx , cli , domain , dom_sid ) ;
2006-08-15 18:27:20 +04:00
if ( ! NT_STATUS_IS_OK ( ret ) ) {
2006-05-12 19:17:35 +04:00
goto done ;
2006-05-19 00:12:45 +04:00
}
2006-08-15 18:27:20 +04:00
2006-11-17 02:48:46 +03:00
/* cli->server_domain is not filled in when using krb5
session setups */
saf_store ( * domain , cli - > desthost ) ;
2006-08-15 18:27:20 +04:00
ret = netdom_join_domain ( ctx , cli , * dom_sid , password , ND_TYPE_AD ) ;
2006-05-12 19:17:35 +04:00
done :
if ( cli )
cli_shutdown ( cli ) ;
return ret ;
}
/*******************************************************************
Set a machines dNSHostName and servicePrincipalName attributes
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static ADS_STATUS net_set_machine_spn ( TALLOC_CTX * ctx , ADS_STRUCT * ads_s )
{
ADS_STATUS status = ADS_ERROR ( LDAP_SERVER_DOWN ) ;
2006-07-20 18:39:06 +04:00
char * new_dn ;
2006-05-12 19:17:35 +04:00
ADS_MODLIST mods ;
const char * servicePrincipalName [ 3 ] = { NULL , NULL , NULL } ;
char * psp ;
fstring my_fqdn ;
LDAPMessage * res = NULL ;
char * dn_string = NULL ;
const char * machine_name = global_myname ( ) ;
int count ;
if ( ! machine_name ) {
return ADS_ERROR ( LDAP_NO_MEMORY ) ;
}
/* Find our DN */
2006-09-04 01:07:16 +04:00
status = ads_find_machine_acct ( ads_s , & res , machine_name ) ;
2006-05-12 19:17:35 +04:00
if ( ! ADS_ERR_OK ( status ) )
return status ;
if ( ( count = ads_count_replies ( ads_s , res ) ) ! = 1 ) {
DEBUG ( 1 , ( " net_set_machine_spn: %d entries returned! \n " , count ) ) ;
return ADS_ERROR ( LDAP_NO_MEMORY ) ;
}
if ( ( dn_string = ads_get_dn ( ads_s , res ) ) = = NULL ) {
DEBUG ( 1 , ( " ads_add_machine_acct: ads_get_dn returned NULL (malloc failure?) \n " ) ) ;
goto done ;
}
new_dn = talloc_strdup ( ctx , dn_string ) ;
ads_memfree ( ads_s , dn_string ) ;
if ( ! new_dn ) {
return ADS_ERROR ( LDAP_NO_MEMORY ) ;
}
2006-07-20 18:39:06 +04:00
/* Windows only creates HOST/shortname & HOST/fqdn. */
2006-05-12 19:17:35 +04:00
if ( ! ( psp = talloc_asprintf ( ctx , " HOST/%s " , machine_name ) ) )
goto done ;
strupper_m ( psp ) ;
servicePrincipalName [ 0 ] = psp ;
name_to_fqdn ( my_fqdn , machine_name ) ;
strlower_m ( my_fqdn ) ;
if ( ! ( psp = talloc_asprintf ( ctx , " HOST/%s " , my_fqdn ) ) )
goto done ;
servicePrincipalName [ 1 ] = psp ;
2006-07-20 18:39:06 +04:00
if ( ! ( mods = ads_init_mods ( ctx ) ) ) {
2006-05-12 19:17:35 +04:00
goto done ;
2006-07-20 18:39:06 +04:00
}
/* fields of primary importance */
ads_mod_str ( ctx , & mods , " dNSHostName " , my_fqdn ) ;
ads_mod_strlist ( ctx , & mods , " servicePrincipalName " , servicePrincipalName ) ;
status = ads_gen_mod ( ads_s , new_dn , mods ) ;
done :
ads_msgfree ( ads_s , res ) ;
return status ;
}
/*******************************************************************
Set a machines dNSHostName and servicePrincipalName attributes
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2006-05-12 19:17:35 +04:00
2006-07-20 18:39:06 +04:00
static ADS_STATUS net_set_machine_upn ( TALLOC_CTX * ctx , ADS_STRUCT * ads_s , const char * upn )
{
ADS_STATUS status = ADS_ERROR ( LDAP_SERVER_DOWN ) ;
char * new_dn ;
ADS_MODLIST mods ;
LDAPMessage * res = NULL ;
char * dn_string = NULL ;
const char * machine_name = global_myname ( ) ;
int count ;
if ( ! machine_name ) {
return ADS_ERROR ( LDAP_NO_MEMORY ) ;
}
/* Find our DN */
2006-09-04 01:07:16 +04:00
status = ads_find_machine_acct ( ads_s , & res , machine_name ) ;
2006-07-20 18:39:06 +04:00
if ( ! ADS_ERR_OK ( status ) )
return status ;
if ( ( count = ads_count_replies ( ads_s , res ) ) ! = 1 ) {
DEBUG ( 1 , ( " net_set_machine_spn: %d entries returned! \n " , count ) ) ;
return ADS_ERROR ( LDAP_NO_MEMORY ) ;
}
if ( ( dn_string = ads_get_dn ( ads_s , res ) ) = = NULL ) {
DEBUG ( 1 , ( " ads_add_machine_acct: ads_get_dn returned NULL (malloc failure?) \n " ) ) ;
goto done ;
}
new_dn = talloc_strdup ( ctx , dn_string ) ;
ads_memfree ( ads_s , dn_string ) ;
if ( ! new_dn ) {
return ADS_ERROR ( LDAP_NO_MEMORY ) ;
}
2006-05-12 19:17:35 +04:00
/* now do the mods */
if ( ! ( mods = ads_init_mods ( ctx ) ) ) {
goto done ;
}
/* fields of primary importance */
2006-07-20 18:39:06 +04:00
ads_mod_str ( ctx , & mods , " userPrincipalName " , upn ) ;
2006-05-12 19:17:35 +04:00
status = ads_gen_mod ( ads_s , new_dn , mods ) ;
done :
ads_msgfree ( ads_s , res ) ;
return status ;
}
2006-05-13 08:39:19 +04:00
/*******************************************************************
join a domain using ADS ( LDAP mods )
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static ADS_STATUS net_precreate_machine_acct ( ADS_STRUCT * ads , const char * ou )
{
ADS_STATUS rc = ADS_ERROR ( LDAP_SERVER_DOWN ) ;
char * dn , * ou_str ;
LDAPMessage * res = NULL ;
ou_str = ads_ou_string ( ads , ou ) ;
2006-11-01 14:19:33 +03:00
if ( ( asprintf ( & dn , " %s,%s " , ou_str , ads - > config . bind_path ) ) = = - 1 ) {
SAFE_FREE ( ou_str ) ;
return ADS_ERROR ( LDAP_NO_MEMORY ) ;
}
2006-05-13 08:39:19 +04:00
2006-07-11 22:01:26 +04:00
rc = ads_search_dn ( ads , & res , dn , NULL ) ;
2006-05-13 08:39:19 +04:00
ads_msgfree ( ads , res ) ;
2006-05-18 08:13:07 +04:00
if ( ADS_ERR_OK ( rc ) ) {
/* Attempt to create the machine account and bail if this fails.
Assume that the admin wants exactly what they requested */
2006-05-13 08:39:19 +04:00
2006-05-18 08:13:07 +04:00
rc = ads_create_machine_acct ( ads , global_myname ( ) , dn ) ;
if ( rc . error_type = = ENUM_ADS_ERROR_LDAP & & rc . err . rc = = LDAP_ALREADY_EXISTS ) {
rc = ADS_SUCCESS ;
}
2006-05-13 08:39:19 +04:00
}
2006-11-01 14:19:33 +03:00
SAFE_FREE ( ou_str ) ;
2006-05-13 08:39:19 +04:00
SAFE_FREE ( dn ) ;
return rc ;
}
2006-07-11 22:45:22 +04:00
/************************************************************************
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static BOOL net_derive_salting_principal ( TALLOC_CTX * ctx , ADS_STRUCT * ads )
{
uint32 domain_func ;
ADS_STATUS status ;
fstring salt ;
char * std_salt ;
LDAPMessage * res = NULL ;
const char * machine_name = global_myname ( ) ;
status = ads_domain_func_level ( ads , & domain_func ) ;
if ( ! ADS_ERR_OK ( status ) ) {
DEBUG ( 2 , ( " Failed to determine domain functional level! \n " ) ) ;
return False ;
}
/* go ahead and setup the default salt */
if ( ( std_salt = kerberos_standard_des_salt ( ) ) = = NULL ) {
2006-08-15 13:53:16 +04:00
d_fprintf ( stderr , " net_derive_salting_principal: failed to obtain stanard DES salt \n " ) ;
2006-07-11 22:45:22 +04:00
return False ;
}
fstrcpy ( salt , std_salt ) ;
SAFE_FREE ( std_salt ) ;
/* if it's a Windows functional domain, we have to look for the UPN */
if ( domain_func = = DS_DOMAIN_FUNCTION_2000 ) {
char * upn ;
int count ;
2006-09-04 01:07:16 +04:00
status = ads_find_machine_acct ( ads , & res , machine_name ) ;
2006-07-11 22:45:22 +04:00
if ( ! ADS_ERR_OK ( status ) ) {
return False ;
}
if ( ( count = ads_count_replies ( ads , res ) ) ! = 1 ) {
DEBUG ( 1 , ( " net_set_machine_spn: %d entries returned! \n " , count ) ) ;
return False ;
}
upn = ads_pull_string ( ads , ctx , res , " userPrincipalName " ) ;
if ( upn ) {
fstrcpy ( salt , upn ) ;
}
ads_msgfree ( ads , res ) ;
}
return kerberos_secrets_store_des_salt ( salt ) ;
}
2006-08-24 19:43:32 +04:00
/*******************************************************************
Send a DNS update request
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# if defined(WITH_DNS_UPDATES)
2006-11-18 00:46:26 +03:00
# include "dns.h"
2006-12-14 20:00:10 +03:00
DNS_ERROR DoDNSUpdate ( char * pszServerName ,
2006-11-18 00:46:26 +03:00
const char * pszDomainName ,
const char * pszHostName ,
const struct in_addr * iplist , int num_addrs ) ;
static NTSTATUS net_update_dns_internal ( TALLOC_CTX * ctx , ADS_STRUCT * ads ,
const char * machine_name ,
const struct in_addr * addrs ,
int num_addrs )
2006-08-24 19:43:32 +04:00
{
struct dns_rr_ns * nameservers = NULL ;
int ns_count = 0 ;
2006-11-18 00:46:26 +03:00
NTSTATUS status = NT_STATUS_UNSUCCESSFUL ;
DNS_ERROR dns_err ;
2006-08-24 19:43:32 +04:00
fstring dns_server ;
2006-12-14 20:00:10 +03:00
const char * dnsdomain = NULL ;
char * root_domain = NULL ;
2006-08-24 19:43:32 +04:00
if ( ( dnsdomain = strchr_m ( machine_name , ' . ' ) ) = = NULL ) {
2006-11-18 00:46:26 +03:00
d_printf ( " No DNS domain configured for %s. "
" Unable to perform DNS Update. \n " , machine_name ) ;
status = NT_STATUS_INVALID_PARAMETER ;
2006-08-24 19:43:32 +04:00
goto done ;
}
dnsdomain + + ;
2006-11-18 00:46:26 +03:00
status = ads_dns_lookup_ns ( ctx , dnsdomain , & nameservers , & ns_count ) ;
if ( ! NT_STATUS_IS_OK ( status ) | | ( ns_count = = 0 ) ) {
2006-12-14 20:00:10 +03:00
/* Child domains often do not have NS records. Look
for the NS record for the forest root domain
( rootDomainNamingContext in therootDSE ) */
const char * rootname_attrs [ ] = { " rootDomainNamingContext " , NULL } ;
LDAPMessage * msg = NULL ;
char * root_dn ;
ADS_STATUS ads_status ;
if ( ! ads - > ld ) {
ads_status = ads_connect ( ads ) ;
if ( ! ADS_ERR_OK ( ads_status ) ) {
DEBUG ( 0 , ( " net_update_dns_internal: Failed to connect to our DC! \n " ) ) ;
goto done ;
}
}
ads_status = ads_do_search ( ads , " " , LDAP_SCOPE_BASE ,
" (objectclass=*) " , rootname_attrs , & msg ) ;
if ( ! ADS_ERR_OK ( ads_status ) ) {
goto done ;
}
root_dn = ads_pull_string ( ads , ctx , msg , " rootDomainNamingContext " ) ;
if ( ! root_dn ) {
ads_msgfree ( ads , msg ) ;
goto done ;
}
root_domain = ads_build_domain ( root_dn ) ;
/* cleanup */
ads_msgfree ( ads , msg ) ;
/* try again for NS servers */
status = ads_dns_lookup_ns ( ctx , root_domain , & nameservers , & ns_count ) ;
if ( ! NT_STATUS_IS_OK ( status ) | | ( ns_count = = 0 ) ) {
DEBUG ( 3 , ( " net_ads_join: Failed to find name server for the %s "
2006-11-18 00:46:26 +03:00
" realm \n " , ads - > config . realm ) ) ;
2006-12-14 20:00:10 +03:00
goto done ;
}
dnsdomain = root_domain ;
2006-08-24 19:43:32 +04:00
}
2006-11-18 00:46:26 +03:00
/* Now perform the dns update - we'll try non-secure and if we fail,
we ' ll follow it up with a secure update */
2006-08-24 19:43:32 +04:00
2006-11-18 00:46:26 +03:00
fstrcpy ( dns_server , nameservers [ 0 ] . hostname ) ;
2006-08-24 19:43:32 +04:00
2006-12-14 20:00:10 +03:00
dns_err = DoDNSUpdate ( dns_server , dnsdomain , machine_name , addrs , num_addrs ) ;
2006-11-18 00:46:26 +03:00
if ( ! ERR_DNS_IS_OK ( dns_err ) ) {
status = NT_STATUS_UNSUCCESSFUL ;
2006-08-24 19:43:32 +04:00
}
2006-11-18 00:46:26 +03:00
done :
2006-12-14 20:00:10 +03:00
SAFE_FREE ( root_domain ) ;
2006-11-18 00:46:26 +03:00
return status ;
2006-12-14 20:00:10 +03:00
}
2006-08-24 19:43:32 +04:00
2006-11-18 00:46:26 +03:00
static NTSTATUS net_update_dns ( TALLOC_CTX * mem_ctx , ADS_STRUCT * ads )
{
int num_addrs ;
struct in_addr * iplist = NULL ;
fstring machine_name ;
NTSTATUS status ;
2006-08-24 19:43:32 +04:00
2006-11-18 00:46:26 +03:00
name_to_fqdn ( machine_name , global_myname ( ) ) ;
strlower_m ( machine_name ) ;
2006-08-24 19:43:32 +04:00
2006-11-18 00:46:26 +03:00
/* Get our ip address (not the 127.0.0.x address but a real ip
* address ) */
2006-08-24 19:43:32 +04:00
2006-11-18 00:46:26 +03:00
num_addrs = get_my_ip_address ( & iplist ) ;
if ( num_addrs < = 0 ) {
DEBUG ( 4 , ( " net_ads_join: Failed to find my non-loopback IP "
" addresses! \n " ) ) ;
return NT_STATUS_INVALID_PARAMETER ;
2006-08-24 19:43:32 +04:00
}
2006-11-18 00:46:26 +03:00
status = net_update_dns_internal ( mem_ctx , ads , machine_name ,
iplist , num_addrs ) ;
2006-08-24 19:43:32 +04:00
SAFE_FREE ( iplist ) ;
2006-11-18 00:46:26 +03:00
return status ;
2006-08-24 19:43:32 +04:00
}
# endif
/*******************************************************************
2006-07-20 18:39:06 +04:00
utility function to parse an integer parameter from
" parameter = value "
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static char * get_string_param ( const char * param )
{
char * p ;
if ( ( p = strchr ( param , ' = ' ) ) = = NULL )
return NULL ;
return ( p + 1 ) ;
}
2006-07-26 19:26:51 +04:00
/*******************************************************************
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static int net_ads_join_usage ( int argc , const char * * argv )
{
d_printf ( " net ads join [options] \n " ) ;
d_printf ( " Valid options: \n " ) ;
d_printf ( " createupn[=UPN] Set the userPrincipalName attribute during the join. \n " ) ;
d_printf ( " The deault UPN is in the form host/netbiosname@REALM. \n " ) ;
d_printf ( " createcomputer=OU Precreate the computer account in a specific OU. \n " ) ;
d_printf ( " The OU string read from top to bottom without RDNs and delimited by a '/'. \n " ) ;
d_printf ( " E.g. \" createcomputer=Computers/Servers/Unix \" \n " ) ;
2006-12-12 19:40:57 +03:00
d_printf ( " NB: A backslash ' \\ ' is used as escape at multiple levels and may \n " ) ;
d_printf ( " need to be doubled or even quadrupled. It is not used as a separator " ) ;
2006-07-26 19:26:51 +04:00
return - 1 ;
}
2006-05-12 19:17:35 +04:00
/*******************************************************************
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
int net_ads_join ( int argc , const char * * argv )
{
2006-08-03 16:41:20 +04:00
ADS_STRUCT * ads = NULL ;
2006-05-12 19:17:35 +04:00
ADS_STATUS status ;
2006-08-18 16:39:21 +04:00
NTSTATUS nt_status ;
2006-08-24 19:43:32 +04:00
char * machine_account = NULL ;
2006-11-17 02:48:46 +03:00
char * short_domain_name = NULL ;
2006-05-12 19:17:35 +04:00
char * tmp_password , * password ;
2006-08-17 18:38:59 +04:00
TALLOC_CTX * ctx = NULL ;
2006-05-12 19:17:35 +04:00
DOM_SID * domain_sid = NULL ;
2006-07-20 18:39:06 +04:00
BOOL createupn = False ;
const char * machineupn = NULL ;
const char * create_in_ou = NULL ;
int i ;
2006-12-14 20:00:10 +03:00
fstring dc_name ;
struct in_addr dcip ;
2006-05-12 19:17:35 +04:00
2006-08-18 16:39:21 +04:00
nt_status = check_ads_config ( ) ;
if ( ! NT_STATUS_IS_OK ( nt_status ) ) {
2006-05-12 19:17:35 +04:00
d_fprintf ( stderr , " Invalid configuration. Exiting.... \n " ) ;
2006-08-03 16:41:20 +04:00
goto fail ;
2001-11-25 03:18:11 +03:00
}
2006-12-14 20:00:10 +03:00
/* find a DC to initialize the server affinity cache */
get_dc_name ( lp_workgroup ( ) , lp_realm ( ) , dc_name , & dcip ) ;
2006-08-17 16:44:59 +04:00
status = ads_startup ( True , & ads ) ;
if ( ! ADS_ERR_OK ( status ) ) {
DEBUG ( 1 , ( " error on ads_startup: %s \n " , ads_errstr ( status ) ) ) ;
nt_status = ads_ntstatus ( status ) ;
2006-08-03 16:41:20 +04:00
goto fail ;
2003-08-20 02:47:10 +04:00
}
2006-05-12 19:17:35 +04:00
if ( strcmp ( ads - > config . realm , lp_realm ( ) ) ! = 0 ) {
2007-01-19 17:29:42 +03:00
d_fprintf ( stderr , " realm of remote server (%s) and realm in %s "
2006-05-12 19:17:35 +04:00
" (%s) DO NOT match. Aborting join \n " , ads - > config . realm ,
2007-01-19 17:29:42 +03:00
dyn_CONFIGFILE , lp_realm ( ) ) ;
2006-08-18 16:39:21 +04:00
nt_status = NT_STATUS_INVALID_PARAMETER ;
2006-08-03 16:41:20 +04:00
goto fail ;
2001-11-25 03:18:11 +03:00
}
2006-05-13 08:39:19 +04:00
if ( ! ( ctx = talloc_init ( " net_ads_join " ) ) ) {
2006-08-15 13:53:16 +04:00
d_fprintf ( stderr , " Could not initialise talloc context. \n " ) ;
2006-08-18 16:39:21 +04:00
nt_status = NT_STATUS_NO_MEMORY ;
2006-08-03 16:41:20 +04:00
goto fail ;
2001-12-21 02:35:53 +03:00
}
2006-05-12 19:17:35 +04:00
2006-07-20 18:39:06 +04:00
/* process additional command line args */
for ( i = 0 ; i < argc ; i + + ) {
if ( ! StrnCaseCmp ( argv [ i ] , " createupn " , strlen ( " createupn " ) ) ) {
createupn = True ;
machineupn = get_string_param ( argv [ i ] ) ;
}
else if ( ! StrnCaseCmp ( argv [ i ] , " createcomputer " , strlen ( " createcomputer " ) ) ) {
if ( ( create_in_ou = get_string_param ( argv [ i ] ) ) = = NULL ) {
d_fprintf ( stderr , " Please supply a valid OU path \n " ) ;
2006-08-18 16:39:21 +04:00
nt_status = NT_STATUS_INVALID_PARAMETER ;
2006-08-03 16:41:20 +04:00
goto fail ;
2006-07-20 18:39:06 +04:00
}
}
else {
d_fprintf ( stderr , " Bad option: %s \n " , argv [ i ] ) ;
2006-08-17 16:44:59 +04:00
nt_status = NT_STATUS_INVALID_PARAMETER ;
2006-08-03 16:41:20 +04:00
goto fail ;
2006-07-20 18:39:06 +04:00
}
}
/* If we were given an OU, try to create the machine in
the OU account first and then do the normal RPC join */
2006-05-13 08:39:19 +04:00
2006-07-20 18:39:06 +04:00
if ( create_in_ou ) {
status = net_precreate_machine_acct ( ads , create_in_ou ) ;
2006-05-13 08:39:19 +04:00
if ( ! ADS_ERR_OK ( status ) ) {
d_fprintf ( stderr , " Failed to pre-create the machine object "
" in OU %s. \n " , argv [ 0 ] ) ;
2006-08-17 16:44:59 +04:00
DEBUG ( 1 , ( " error calling net_precreate_machine_acct: %s \n " ,
ads_errstr ( status ) ) ) ;
nt_status = ads_ntstatus ( status ) ;
2006-08-03 16:41:20 +04:00
goto fail ;
2006-05-13 08:39:19 +04:00
}
}
2006-05-12 19:17:35 +04:00
/* Do the domain join here */
tmp_password = generate_random_str ( DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH ) ;
password = talloc_strdup ( ctx , tmp_password ) ;
2003-09-05 21:57:45 +04:00
2006-08-15 18:27:20 +04:00
nt_status = net_join_domain ( ctx , ads - > config . ldap_server_name ,
2006-11-17 02:48:46 +03:00
& ads - > ldap_ip , & short_domain_name , & domain_sid , password ) ;
2006-08-15 18:27:20 +04:00
if ( ! NT_STATUS_IS_OK ( nt_status ) ) {
2006-08-17 16:44:59 +04:00
DEBUG ( 1 , ( " call of net_join_domain failed: %s \n " ,
get_friendly_nt_error_msg ( nt_status ) ) ) ;
2006-08-03 16:41:20 +04:00
goto fail ;
2003-09-05 21:57:45 +04:00
}
2006-11-17 02:48:46 +03:00
2006-05-12 19:17:35 +04:00
/* Check the short name of the domain */
2006-11-17 02:48:46 +03:00
if ( ! strequal ( lp_workgroup ( ) , short_domain_name ) ) {
2007-01-19 17:29:42 +03:00
d_printf ( " The workgroup in %s does not match the short \n " , dyn_CONFIGFILE ) ;
2006-11-17 02:48:46 +03:00
d_printf ( " domain name obtained from the server. \n " ) ;
d_printf ( " Using the name [%s] from the server. \n " , short_domain_name ) ;
2007-01-19 17:29:42 +03:00
d_printf ( " You should set \" workgroup = %s \" in %s. \n " ,
short_domain_name , dyn_CONFIGFILE ) ;
2004-06-23 01:58:35 +04:00
}
2003-09-05 21:57:45 +04:00
d_printf ( " Using short domain name -- %s \n " , short_domain_name ) ;
2006-05-12 19:17:35 +04:00
/* HACK ALERT! Store the sid and password under both the lp_workgroup()
2003-09-05 21:57:45 +04:00
value from smb . conf and the string returned from the server . The former is
neede to bootstrap winbindd ' s first connection to the DC to get the real
short domain name - - jerry */
2006-05-12 19:17:35 +04:00
2006-05-19 00:12:45 +04:00
if ( ( netdom_store_machine_account ( lp_workgroup ( ) , domain_sid , password ) = = - 1 )
| | ( netdom_store_machine_account ( short_domain_name , domain_sid , password ) = = - 1 ) )
2006-05-12 19:17:35 +04:00
{
2006-08-18 16:39:21 +04:00
/* issue an internal error here for now.
* everything else would mean changing tdb routines . */
nt_status = NT_STATUS_INTERNAL_ERROR ;
2006-08-03 16:41:20 +04:00
goto fail ;
2001-12-21 02:35:53 +03:00
}
2006-05-12 19:17:35 +04:00
/* Verify that everything is ok */
2001-11-25 03:18:11 +03:00
2006-05-12 19:17:35 +04:00
if ( net_rpc_join_ok ( short_domain_name , ads - > config . ldap_server_name , & ads - > ldap_ip ) ! = 0 ) {
d_fprintf ( stderr , " Failed to verify membership in domain! \n " ) ;
2006-08-03 16:41:20 +04:00
goto fail ;
2006-05-12 19:17:35 +04:00
}
2006-05-18 08:13:07 +04:00
/* create the dNSHostName & servicePrincipalName values */
2006-05-12 19:17:35 +04:00
2006-05-18 08:13:07 +04:00
status = net_set_machine_spn ( ctx , ads ) ;
if ( ! ADS_ERR_OK ( status ) ) {
2006-05-12 19:17:35 +04:00
2006-07-20 00:56:11 +04:00
d_fprintf ( stderr , " Failed to set servicePrincipalNames. Please ensure that \n " ) ;
d_fprintf ( stderr , " the DNS domain of this server matches the AD domain, \n " ) ;
d_fprintf ( stderr , " Or rejoin with using Domain Admin credentials. \n " ) ;
/* Disable the machine account in AD. Better to fail than to leave
a confused admin . */
if ( net_ads_leave ( 0 , NULL ) ! = 0 ) {
d_fprintf ( stderr , " Failed to disable machine account in AD. Please do so manually. \n " ) ;
}
/* clear out the machine password */
netdom_store_machine_account ( lp_workgroup ( ) , domain_sid , " " ) ;
netdom_store_machine_account ( short_domain_name , domain_sid , " " ) ;
2006-08-17 16:44:59 +04:00
nt_status = ads_ntstatus ( status ) ;
2006-08-03 16:41:20 +04:00
goto fail ;
2004-11-03 00:28:14 +03:00
}
2006-07-11 22:45:22 +04:00
if ( ! net_derive_salting_principal ( ctx , ads ) ) {
2006-05-12 19:17:35 +04:00
DEBUG ( 1 , ( " Failed to determine salting principal \n " ) ) ;
2006-08-03 16:41:20 +04:00
goto fail ;
2003-09-05 21:57:45 +04:00
}
2006-07-20 18:39:06 +04:00
if ( createupn ) {
pstring upn ;
/* default to using the short UPN name */
if ( ! machineupn ) {
snprintf ( upn , sizeof ( upn ) , " host/%s@%s " , global_myname ( ) ,
ads - > config . realm ) ;
machineupn = upn ;
}
status = net_set_machine_upn ( ctx , ads , machineupn ) ;
if ( ! ADS_ERR_OK ( status ) ) {
d_fprintf ( stderr , " Failed to set userPrincipalName. Are you a Domain Admin? \n " ) ;
}
}
2004-06-23 01:58:35 +04:00
/* Now build the keytab, using the same ADS connection */
if ( lp_use_kerberos_keytab ( ) & & ads_keytab_create_default ( ads ) ) {
DEBUG ( 1 , ( " Error creating host keytab! \n " ) ) ;
}
2006-08-24 19:43:32 +04:00
# if defined(WITH_DNS_UPDATES)
/* We enter this block with user creds */
2006-11-18 00:46:26 +03:00
ads_kdestroy ( NULL ) ;
ads_destroy ( & ads ) ;
ads = NULL ;
if ( ( ads = ads_init ( lp_realm ( ) , NULL , NULL ) ) ! = NULL ) {
/* kinit with the machine password */
use_in_memory_ccache ( ) ;
asprintf ( & ads - > auth . user_name , " %s$ " , global_myname ( ) ) ;
ads - > auth . password = secrets_fetch_machine_password (
lp_workgroup ( ) , NULL , NULL ) ;
ads - > auth . realm = SMB_STRDUP ( lp_realm ( ) ) ;
ads_kinit_password ( ads ) ;
}
2006-08-24 19:43:32 +04:00
2006-11-18 00:46:26 +03:00
if ( ! ads | | ! NT_STATUS_IS_OK ( net_update_dns ( ctx , ads ) ) ) {
2006-08-24 19:43:32 +04:00
d_fprintf ( stderr , " DNS update failed! \n " ) ;
}
/* exit from this block using machine creds */
# endif
2006-11-17 02:48:46 +03:00
d_printf ( " Joined '%s' to realm '%s' \n " , global_myname ( ) , ads - > server . realm ) ;
2001-11-25 03:18:11 +03:00
2006-08-24 19:43:32 +04:00
SAFE_FREE ( machine_account ) ;
2006-05-12 19:17:35 +04:00
TALLOC_FREE ( ctx ) ;
2004-06-23 04:20:31 +04:00
ads_destroy ( & ads ) ;
2006-05-12 19:17:35 +04:00
2001-11-25 03:18:11 +03:00
return 0 ;
2006-08-03 16:41:20 +04:00
fail :
2006-08-15 13:53:16 +04:00
/* issue an overall failure message at the end. */
2006-08-24 19:43:32 +04:00
d_printf ( " Failed to join domain: %s \n " , get_friendly_nt_error_msg ( nt_status ) ) ;
SAFE_FREE ( machine_account ) ;
TALLOC_FREE ( ctx ) ;
ads_destroy ( & ads ) ;
return - 1 ;
}
/*******************************************************************
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static int net_ads_dns_usage ( int argc , const char * * argv )
{
# if defined(WITH_DNS_UPDATES)
d_printf ( " net ads dns <command> \n " ) ;
d_printf ( " Valid commands: \n " ) ;
d_printf ( " register Issue a dynamic DNS update request for our hostname \n " ) ;
return 0 ;
# else
d_fprintf ( stderr , " DNS update support not enabled at compile time! \n " ) ;
return - 1 ;
# endif
}
/*******************************************************************
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2006-11-18 00:46:26 +03:00
static int net_ads_dns_register ( int argc , const char * * argv )
2006-08-24 19:43:32 +04:00
{
# if defined(WITH_DNS_UPDATES)
ADS_STRUCT * ads ;
ADS_STATUS status ;
TALLOC_CTX * ctx ;
2006-11-18 00:46:26 +03:00
# ifdef DEVELOPER
talloc_enable_leak_report ( ) ;
# endif
2006-12-14 20:00:10 +03:00
if ( argc > 0 ) {
2006-11-18 00:46:26 +03:00
d_fprintf ( stderr , " net ads dns register <name> <ip> \n " ) ;
2006-08-24 19:43:32 +04:00
return - 1 ;
}
if ( ! ( ctx = talloc_init ( " net_ads_dns " ) ) ) {
2006-11-18 00:46:26 +03:00
d_fprintf ( stderr , " Could not initialise talloc context \n " ) ;
2006-08-24 19:43:32 +04:00
return - 1 ;
}
2006-12-14 20:00:10 +03:00
status = ads_startup ( True , & ads ) ;
2006-11-18 00:46:26 +03:00
if ( ! ADS_ERR_OK ( status ) ) {
DEBUG ( 1 , ( " error on ads_startup: %s \n " , ads_errstr ( status ) ) ) ;
TALLOC_FREE ( ctx ) ;
return - 1 ;
}
2006-12-14 20:00:10 +03:00
if ( ! NT_STATUS_IS_OK ( net_update_dns ( ctx , ads ) ) ) {
2006-08-24 19:43:32 +04:00
d_fprintf ( stderr , " DNS update failed! \n " ) ;
ads_destroy ( & ads ) ;
TALLOC_FREE ( ctx ) ;
return - 1 ;
}
d_fprintf ( stderr , " Successfully registered hostname with DNS \n " ) ;
2006-08-03 16:41:20 +04:00
ads_destroy ( & ads ) ;
2006-08-17 18:38:59 +04:00
TALLOC_FREE ( ctx ) ;
2006-08-24 19:43:32 +04:00
return 0 ;
# else
d_fprintf ( stderr , " DNS update support not enabled at compile time! \n " ) ;
2006-08-03 16:41:20 +04:00
return - 1 ;
2006-08-24 19:43:32 +04:00
# endif
2001-11-25 03:18:11 +03:00
}
2006-11-18 00:46:26 +03:00
# if defined(WITH_DNS_UPDATES)
DNS_ERROR do_gethostbyname ( const char * server , const char * host ) ;
# endif
static int net_ads_dns_gethostbyname ( int argc , const char * * argv )
{
# if defined(WITH_DNS_UPDATES)
DNS_ERROR err ;
# ifdef DEVELOPER
talloc_enable_leak_report ( ) ;
# endif
if ( argc ! = 2 ) {
d_fprintf ( stderr , " net ads dns gethostbyname <server> "
" <name> \n " ) ;
return - 1 ;
}
err = do_gethostbyname ( argv [ 0 ] , argv [ 1 ] ) ;
d_printf ( " do_gethostbyname returned %d \n " , ERROR_DNS_V ( err ) ) ;
# endif
return 0 ;
}
static int net_ads_dns ( int argc , const char * argv [ ] )
{
struct functable func [ ] = {
{ " REGISTER " , net_ads_dns_register } ,
{ " GETHOSTBYNAME " , net_ads_dns_gethostbyname } ,
{ NULL , NULL }
} ;
return net_run_function ( argc , argv , func , net_ads_dns_usage ) ;
}
2006-05-12 19:17:35 +04:00
/*******************************************************************
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2002-02-02 05:06:03 +03:00
int net_ads_printer_usage ( int argc , const char * * argv )
{
d_printf (
2003-07-03 09:08:51 +04:00
" \n net ads printer search <printer> "
2004-09-29 13:56:35 +04:00
" \n \t search for a printer in the directory \n "
2002-02-02 05:06:03 +03:00
" \n net ads printer info <printer> <server> "
" \n \t lookup info in directory for printer on server "
" \n \t (note: printer defaults to \" * \" , server defaults to local) \n "
" \n net ads printer publish <printername> "
" \n \t publish printer in directory "
" \n \t (note: printer name is required) \n "
" \n net ads printer remove <printername> "
" \n \t remove printer from directory "
" \n \t (note: printer name is required) \n " ) ;
return - 1 ;
}
2006-05-12 19:17:35 +04:00
/*******************************************************************
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2003-07-03 09:08:51 +04:00
static int net_ads_printer_search ( int argc , const char * * argv )
{
ADS_STRUCT * ads ;
ADS_STATUS rc ;
2006-09-04 01:07:16 +04:00
LDAPMessage * res = NULL ;
2003-07-03 09:08:51 +04:00
2006-08-17 16:44:59 +04:00
if ( ! ADS_ERR_OK ( ads_startup ( False , & ads ) ) ) {
2003-07-03 09:08:51 +04:00
return - 1 ;
2004-06-23 04:20:31 +04:00
}
2003-07-03 09:08:51 +04:00
rc = ads_find_printers ( ads , & res ) ;
if ( ! ADS_ERR_OK ( rc ) ) {
2006-01-18 00:22:00 +03:00
d_fprintf ( stderr , " ads_find_printer: %s \n " , ads_errstr ( rc ) ) ;
2003-07-03 09:08:51 +04:00
ads_msgfree ( ads , res ) ;
2004-06-23 04:20:31 +04:00
ads_destroy ( & ads ) ;
return - 1 ;
2003-07-03 09:08:51 +04:00
}
if ( ads_count_replies ( ads , res ) = = 0 ) {
2006-01-18 00:22:00 +03:00
d_fprintf ( stderr , " No results found \n " ) ;
2003-07-03 09:08:51 +04:00
ads_msgfree ( ads , res ) ;
2004-06-23 04:20:31 +04:00
ads_destroy ( & ads ) ;
2003-07-03 09:08:51 +04:00
return - 1 ;
}
ads_dump ( ads , res ) ;
ads_msgfree ( ads , res ) ;
2004-06-23 04:20:31 +04:00
ads_destroy ( & ads ) ;
2003-07-03 09:08:51 +04:00
return 0 ;
}
2002-02-02 05:06:03 +03:00
static int net_ads_printer_info ( int argc , const char * * argv )
{
ADS_STRUCT * ads ;
ADS_STATUS rc ;
2002-07-15 14:35:28 +04:00
const char * servername , * printername ;
2006-09-04 01:07:16 +04:00
LDAPMessage * res = NULL ;
2002-02-02 05:06:03 +03:00
2006-08-17 16:44:59 +04:00
if ( ! ADS_ERR_OK ( ads_startup ( False , & ads ) ) ) {
2004-06-23 04:20:31 +04:00
return - 1 ;
}
2002-02-02 05:06:03 +03:00
2004-06-23 04:20:31 +04:00
if ( argc > 0 ) {
2002-02-02 05:06:03 +03:00
printername = argv [ 0 ] ;
2004-06-23 04:20:31 +04:00
} else {
2002-02-02 05:06:03 +03:00
printername = " * " ;
2004-06-23 04:20:31 +04:00
}
2002-02-02 05:06:03 +03:00
2004-06-23 04:20:31 +04:00
if ( argc > 1 ) {
2002-02-02 05:06:03 +03:00
servername = argv [ 1 ] ;
2004-06-23 04:20:31 +04:00
} else {
2002-11-13 02:20:50 +03:00
servername = global_myname ( ) ;
2004-06-23 04:20:31 +04:00
}
2002-02-02 05:06:03 +03:00
rc = ads_find_printer_on_server ( ads , & res , printername , servername ) ;
if ( ! ADS_ERR_OK ( rc ) ) {
2006-10-02 16:06:49 +04:00
d_fprintf ( stderr , " Server '%s' not found: %s \n " ,
servername , ads_errstr ( rc ) ) ;
2002-02-02 05:06:03 +03:00
ads_msgfree ( ads , res ) ;
2004-06-23 04:20:31 +04:00
ads_destroy ( & ads ) ;
2002-02-02 05:06:03 +03:00
return - 1 ;
}
if ( ads_count_replies ( ads , res ) = = 0 ) {
2006-01-18 00:22:00 +03:00
d_fprintf ( stderr , " Printer '%s' not found \n " , printername ) ;
2002-02-02 05:06:03 +03:00
ads_msgfree ( ads , res ) ;
2004-06-23 04:20:31 +04:00
ads_destroy ( & ads ) ;
2002-02-02 05:06:03 +03:00
return - 1 ;
}
ads_dump ( ads , res ) ;
2002-03-30 00:09:44 +03:00
ads_msgfree ( ads , res ) ;
2004-06-23 04:20:31 +04:00
ads_destroy ( & ads ) ;
2002-02-02 05:06:03 +03:00
return 0 ;
}
2005-09-30 21:13:37 +04:00
void do_drv_upgrade_printer ( int msg_type , struct process_id src ,
r21064: The core of this patch is
void message_register(int msg_type,
void (*fn)(int msg_type, struct process_id pid,
- void *buf, size_t len))
+ void *buf, size_t len,
+ void *private_data),
+ void *private_data)
{
struct dispatch_fns *dfn;
So this adds a (so far unused) private pointer that is passed from
message_register to the message handler. A prerequisite to implement a tiny
samba4-API compatible wrapper around our messaging system. That itself is
necessary for the Samba4 notify system.
Yes, I know, I could import the whole Samba4 messaging system, but I want to
do it step by step and I think getting notify in is more important in this
step.
Volker
(This used to be commit c8ae60ed65dcce9660ee39c75488f2838cf9a28b)
2007-01-31 01:22:06 +03:00
void * buf , size_t len , void * private_data )
2002-07-15 14:35:28 +04:00
{
return ;
}
2002-02-02 05:06:03 +03:00
static int net_ads_printer_publish ( int argc , const char * * argv )
{
ADS_STRUCT * ads ;
ADS_STATUS rc ;
2003-07-03 09:58:55 +04:00
const char * servername , * printername ;
2002-11-18 23:23:05 +03:00
struct cli_state * cli ;
2005-09-30 21:13:37 +04:00
struct rpc_pipe_client * pipe_hnd ;
2002-11-18 23:23:05 +03:00
struct in_addr server_ip ;
NTSTATUS nt_status ;
2002-12-20 23:21:31 +03:00
TALLOC_CTX * mem_ctx = talloc_init ( " net_ads_printer_publish " ) ;
2002-11-18 23:23:05 +03:00
ADS_MODLIST mods = ads_init_mods ( mem_ctx ) ;
char * prt_dn , * srv_dn , * * srv_cn ;
2006-09-04 01:07:16 +04:00
LDAPMessage * res = NULL ;
2002-02-02 05:06:03 +03:00
2006-08-17 16:44:59 +04:00
if ( ! ADS_ERR_OK ( ads_startup ( True , & ads ) ) ) {
2004-06-23 04:20:31 +04:00
return - 1 ;
}
2002-02-02 05:06:03 +03:00
2004-06-23 04:20:31 +04:00
if ( argc < 1 ) {
2002-02-02 05:06:03 +03:00
return net_ads_printer_usage ( argc , argv ) ;
2004-06-23 04:20:31 +04:00
}
2002-11-18 23:23:05 +03:00
2003-07-03 09:58:55 +04:00
printername = argv [ 0 ] ;
2004-06-23 04:20:31 +04:00
if ( argc = = 2 ) {
2002-11-18 23:23:05 +03:00
servername = argv [ 1 ] ;
2004-06-23 04:20:31 +04:00
} else {
2002-11-18 23:23:05 +03:00
servername = global_myname ( ) ;
2004-06-23 04:20:31 +04:00
}
2002-11-18 23:23:05 +03:00
2003-07-03 09:58:55 +04:00
/* Get printer data from SPOOLSS */
2002-11-18 23:23:05 +03:00
resolve_name ( servername , & server_ip , 0x20 ) ;
nt_status = cli_full_connection ( & cli , global_myname ( ) , servername ,
& server_ip , 0 ,
" IPC$ " , " IPC " ,
opt_user_name , opt_workgroup ,
opt_password ? opt_password : " " ,
CLI_FULL_CONNECTION_USE_KERBEROS ,
2003-07-31 03:49:29 +04:00
Undefined , NULL ) ;
2002-11-18 23:23:05 +03:00
2003-07-03 09:58:55 +04:00
if ( NT_STATUS_IS_ERR ( nt_status ) ) {
2006-01-18 00:22:00 +03:00
d_fprintf ( stderr , " Unable to open a connnection to %s to obtain data "
2003-07-03 09:58:55 +04:00
" for %s \n " , servername , printername ) ;
2004-06-23 04:20:31 +04:00
ads_destroy ( & ads ) ;
2003-07-03 09:58:55 +04:00
return - 1 ;
}
/* Publish on AD server */
ads_find_machine_acct ( ads , & res , servername ) ;
if ( ads_count_replies ( ads , res ) = = 0 ) {
2006-01-18 00:22:00 +03:00
d_fprintf ( stderr , " Could not find machine account for server %s \n " ,
2003-07-03 09:58:55 +04:00
servername ) ;
2004-06-23 04:20:31 +04:00
ads_destroy ( & ads ) ;
2003-07-03 09:58:55 +04:00
return - 1 ;
}
2006-07-11 22:01:26 +04:00
srv_dn = ldap_get_dn ( ( LDAP * ) ads - > ld , ( LDAPMessage * ) res ) ;
2003-07-03 09:58:55 +04:00
srv_cn = ldap_explode_dn ( srv_dn , 1 ) ;
asprintf ( & prt_dn , " cn=%s-%s,%s " , srv_cn [ 0 ] , printername , srv_dn ) ;
2005-09-30 21:13:37 +04:00
pipe_hnd = cli_rpc_pipe_open_noauth ( cli , PI_SPOOLSS , & nt_status ) ;
2006-03-31 04:47:08 +04:00
if ( ! pipe_hnd ) {
d_fprintf ( stderr , " Unable to open a connnection to the spoolss pipe on %s \n " ,
servername ) ;
ads_destroy ( & ads ) ;
return - 1 ;
}
2006-09-06 16:29:45 +04:00
if ( ! W_ERROR_IS_OK ( get_remote_printer_publishing_data ( pipe_hnd , mem_ctx , & mods ,
printername ) ) ) {
ads_destroy ( & ads ) ;
return - 1 ;
}
2002-11-18 23:23:05 +03:00
rc = ads_add_printer_entry ( ads , prt_dn , mem_ctx , & mods ) ;
2002-02-02 05:06:03 +03:00
if ( ! ADS_ERR_OK ( rc ) ) {
2006-01-18 00:22:00 +03:00
d_fprintf ( stderr , " ads_publish_printer: %s \n " , ads_errstr ( rc ) ) ;
2004-06-23 04:20:31 +04:00
ads_destroy ( & ads ) ;
2002-02-02 05:06:03 +03:00
return - 1 ;
}
d_printf ( " published printer \n " ) ;
2004-06-23 04:20:31 +04:00
ads_destroy ( & ads ) ;
2002-02-02 05:06:03 +03:00
return 0 ;
}
static int net_ads_printer_remove ( int argc , const char * * argv )
{
ADS_STRUCT * ads ;
ADS_STATUS rc ;
2002-11-13 02:20:50 +03:00
const char * servername ;
char * prt_dn ;
2006-09-04 01:07:16 +04:00
LDAPMessage * res = NULL ;
2002-02-02 05:06:03 +03:00
2006-08-17 16:44:59 +04:00
if ( ! ADS_ERR_OK ( ads_startup ( True , & ads ) ) ) {
2004-06-23 04:20:31 +04:00
return - 1 ;
}
2002-02-02 05:06:03 +03:00
2004-06-23 04:20:31 +04:00
if ( argc < 1 ) {
2002-02-02 05:06:03 +03:00
return net_ads_printer_usage ( argc , argv ) ;
2004-06-23 04:20:31 +04:00
}
2002-02-02 05:06:03 +03:00
2004-06-23 04:20:31 +04:00
if ( argc > 1 ) {
2002-02-02 05:06:03 +03:00
servername = argv [ 1 ] ;
2004-06-23 04:20:31 +04:00
} else {
2002-11-13 02:20:50 +03:00
servername = global_myname ( ) ;
2004-06-23 04:20:31 +04:00
}
2002-02-02 05:06:03 +03:00
rc = ads_find_printer_on_server ( ads , & res , argv [ 0 ] , servername ) ;
if ( ! ADS_ERR_OK ( rc ) ) {
2006-01-18 00:22:00 +03:00
d_fprintf ( stderr , " ads_find_printer_on_server: %s \n " , ads_errstr ( rc ) ) ;
2002-02-02 05:06:03 +03:00
ads_msgfree ( ads , res ) ;
2004-06-23 04:20:31 +04:00
ads_destroy ( & ads ) ;
2002-02-02 05:06:03 +03:00
return - 1 ;
}
if ( ads_count_replies ( ads , res ) = = 0 ) {
2006-01-18 00:22:00 +03:00
d_fprintf ( stderr , " Printer '%s' not found \n " , argv [ 1 ] ) ;
2002-02-02 05:06:03 +03:00
ads_msgfree ( ads , res ) ;
2004-06-23 04:20:31 +04:00
ads_destroy ( & ads ) ;
2002-02-02 05:06:03 +03:00
return - 1 ;
}
prt_dn = ads_get_dn ( ads , res ) ;
ads_msgfree ( ads , res ) ;
rc = ads_del_dn ( ads , prt_dn ) ;
ads_memfree ( ads , prt_dn ) ;
if ( ! ADS_ERR_OK ( rc ) ) {
2006-01-18 00:22:00 +03:00
d_fprintf ( stderr , " ads_del_dn: %s \n " , ads_errstr ( rc ) ) ;
2004-06-23 04:20:31 +04:00
ads_destroy ( & ads ) ;
2002-02-02 05:06:03 +03:00
return - 1 ;
}
2004-06-23 04:20:31 +04:00
ads_destroy ( & ads ) ;
2002-02-02 05:06:03 +03:00
return 0 ;
}
static int net_ads_printer ( int argc , const char * * argv )
{
struct functable func [ ] = {
2003-07-03 09:08:51 +04:00
{ " SEARCH " , net_ads_printer_search } ,
2002-02-02 05:06:03 +03:00
{ " INFO " , net_ads_printer_info } ,
{ " PUBLISH " , net_ads_printer_publish } ,
{ " REMOVE " , net_ads_printer_remove } ,
{ NULL , NULL }
} ;
return net_run_function ( argc , argv , func , net_ads_printer_usage ) ;
}
2001-12-20 06:54:52 +03:00
static int net_ads_password ( int argc , const char * * argv )
{
2004-06-23 01:58:35 +04:00
ADS_STRUCT * ads ;
const char * auth_principal = opt_user_name ;
const char * auth_password = opt_password ;
char * realm = NULL ;
char * new_password = NULL ;
char * c , * prompt ;
const char * user ;
ADS_STATUS ret ;
if ( opt_user_name = = NULL | | opt_password = = NULL ) {
2006-01-18 00:22:00 +03:00
d_fprintf ( stderr , " You must supply an administrator username/password \n " ) ;
2004-06-23 01:58:35 +04:00
return - 1 ;
}
2003-06-10 08:15:55 +04:00
2004-06-23 01:58:35 +04:00
if ( argc < 1 ) {
2006-01-18 00:22:00 +03:00
d_fprintf ( stderr , " ERROR: You must say which username to change password for \n " ) ;
2004-06-23 01:58:35 +04:00
return - 1 ;
}
user = argv [ 0 ] ;
if ( ! strchr_m ( user , ' @ ' ) ) {
asprintf ( & c , " %s@%s " , argv [ 0 ] , lp_realm ( ) ) ;
user = c ;
}
use_in_memory_ccache ( ) ;
2004-10-27 04:41:41 +04:00
c = strchr_m ( auth_principal , ' @ ' ) ;
2004-06-23 01:58:35 +04:00
if ( c ) {
realm = + + c ;
} else {
realm = lp_realm ( ) ;
}
/* use the realm so we can eventually change passwords for users
in realms other than default */
2006-07-17 15:04:47 +04:00
if ( ! ( ads = ads_init ( realm , opt_workgroup , opt_host ) ) ) {
2004-06-23 01:58:35 +04:00
return - 1 ;
}
/* we don't actually need a full connect, but it's the easy way to
fill in the KDC ' s addresss */
ads_connect ( ads ) ;
2001-12-20 06:54:52 +03:00
2004-06-23 01:58:35 +04:00
if ( ! ads | | ! ads - > config . realm ) {
2006-01-18 00:22:00 +03:00
d_fprintf ( stderr , " Didn't find the kerberos server! \n " ) ;
2004-06-23 01:58:35 +04:00
return - 1 ;
}
2003-03-18 01:58:24 +03:00
2004-06-23 01:58:35 +04:00
if ( argv [ 1 ] ) {
2005-05-31 17:46:45 +04:00
new_password = ( char * ) argv [ 1 ] ;
2004-06-23 01:58:35 +04:00
} else {
asprintf ( & prompt , " Enter new password for %s: " , user ) ;
new_password = getpass ( prompt ) ;
free ( prompt ) ;
}
2001-12-20 06:54:52 +03:00
2004-06-23 01:58:35 +04:00
ret = kerberos_set_password ( ads - > auth . kdc_server , auth_principal ,
2003-06-10 08:15:55 +04:00
auth_password , user , new_password , ads - > auth . time_offset ) ;
2004-06-23 01:58:35 +04:00
if ( ! ADS_ERR_OK ( ret ) ) {
2006-06-15 20:09:31 +04:00
d_fprintf ( stderr , " Password change failed: %s \n " , ads_errstr ( ret ) ) ;
2004-06-23 01:58:35 +04:00
ads_destroy ( & ads ) ;
return - 1 ;
}
2001-12-20 06:54:52 +03:00
2004-06-23 01:58:35 +04:00
d_printf ( " Password change for %s completed. \n " , user ) ;
ads_destroy ( & ads ) ;
2001-12-20 06:54:52 +03:00
2004-06-23 01:58:35 +04:00
return 0 ;
2001-12-20 06:54:52 +03:00
}
2003-04-15 02:27:09 +04:00
int net_ads_changetrustpw ( int argc , const char * * argv )
2001-12-20 06:54:52 +03:00
{
2004-06-23 01:58:35 +04:00
ADS_STRUCT * ads ;
char * host_principal ;
2004-07-08 19:36:23 +04:00
fstring my_name ;
2004-06-23 01:58:35 +04:00
ADS_STATUS ret ;
2001-12-20 06:54:52 +03:00
2004-06-23 01:58:35 +04:00
if ( ! secrets_init ( ) ) {
DEBUG ( 1 , ( " Failed to initialise secrets database \n " ) ) ;
return - 1 ;
}
2002-08-17 18:45:04 +04:00
2004-06-23 01:58:35 +04:00
net_use_machine_password ( ) ;
2002-08-17 18:45:04 +04:00
2004-06-23 01:58:35 +04:00
use_in_memory_ccache ( ) ;
2003-02-24 06:06:45 +03:00
2006-08-17 16:44:59 +04:00
if ( ! ADS_ERR_OK ( ads_startup ( True , & ads ) ) ) {
2004-06-23 01:58:35 +04:00
return - 1 ;
}
2004-07-08 19:36:23 +04:00
fstrcpy ( my_name , global_myname ( ) ) ;
strlower_m ( my_name ) ;
2006-06-09 14:50:28 +04:00
asprintf ( & host_principal , " %s$@%s " , my_name , ads - > config . realm ) ;
d_printf ( " Changing password for principal: %s \n " , host_principal ) ;
2004-06-23 01:58:35 +04:00
ret = ads_change_trust_account_password ( ads , host_principal ) ;
2001-12-20 06:54:52 +03:00
2004-06-23 01:58:35 +04:00
if ( ! ADS_ERR_OK ( ret ) ) {
2006-06-09 14:50:28 +04:00
d_fprintf ( stderr , " Password change failed: %s \n " , ads_errstr ( ret ) ) ;
2004-06-23 01:58:35 +04:00
ads_destroy ( & ads ) ;
SAFE_FREE ( host_principal ) ;
return - 1 ;
}
2001-12-20 06:54:52 +03:00
2006-06-09 14:50:28 +04:00
d_printf ( " Password change for principal %s succeeded. \n " , host_principal ) ;
2004-06-23 01:58:35 +04:00
if ( lp_use_kerberos_keytab ( ) ) {
d_printf ( " Attempting to update system keytab with new password. \n " ) ;
if ( ads_keytab_create_default ( ads ) ) {
d_printf ( " Failed to update system keytab. \n " ) ;
}
}
2001-12-20 06:54:52 +03:00
ads_destroy ( & ads ) ;
SAFE_FREE ( host_principal ) ;
2004-06-23 01:58:35 +04:00
return 0 ;
2001-12-20 06:54:52 +03:00
}
2002-07-15 14:35:28 +04:00
/*
help for net ads search
*/
static int net_ads_search_usage ( int argc , const char * * argv )
{
d_printf (
" \n net ads search <expression> <attributes...> \n " \
" \n perform a raw LDAP search on a ADS server and dump the results \n " \
" The expression is a standard LDAP search expression, and the \n " \
" attributes are a list of LDAP fields to show in the results \n \n " \
" Example: net ads search '(objectCategory=group)' sAMAccountName \n \n "
) ;
net_common_flags_usage ( argc , argv ) ;
return - 1 ;
}
/*
general ADS search function . Useful in diagnosing problems in ADS
*/
static int net_ads_search ( int argc , const char * * argv )
{
ADS_STRUCT * ads ;
ADS_STATUS rc ;
2003-06-30 09:45:27 +04:00
const char * ldap_exp ;
2002-07-15 14:35:28 +04:00
const char * * attrs ;
2006-09-04 01:07:16 +04:00
LDAPMessage * res = NULL ;
2002-07-15 14:35:28 +04:00
if ( argc < 1 ) {
return net_ads_search_usage ( argc , argv ) ;
}
2006-08-17 16:44:59 +04:00
if ( ! ADS_ERR_OK ( ads_startup ( False , & ads ) ) ) {
2002-07-15 14:35:28 +04:00
return - 1 ;
}
2003-06-30 09:45:27 +04:00
ldap_exp = argv [ 0 ] ;
2002-07-15 14:35:28 +04:00
attrs = ( argv + 1 ) ;
2003-03-18 01:33:34 +03:00
rc = ads_do_search_all ( ads , ads - > config . bind_path ,
2002-07-15 14:35:28 +04:00
LDAP_SCOPE_SUBTREE ,
2003-06-30 09:45:27 +04:00
ldap_exp , attrs , & res ) ;
2002-07-15 14:35:28 +04:00
if ( ! ADS_ERR_OK ( rc ) ) {
2006-01-18 00:22:00 +03:00
d_fprintf ( stderr , " search failed: %s \n " , ads_errstr ( rc ) ) ;
2004-06-23 04:20:31 +04:00
ads_destroy ( & ads ) ;
2002-07-15 14:35:28 +04:00
return - 1 ;
}
d_printf ( " Got %d replies \n \n " , ads_count_replies ( ads , res ) ) ;
/* dump the results */
ads_dump ( ads , res ) ;
ads_msgfree ( ads , res ) ;
ads_destroy ( & ads ) ;
return 0 ;
}
2003-03-18 01:33:34 +03:00
/*
help for net ads search
*/
static int net_ads_dn_usage ( int argc , const char * * argv )
{
d_printf (
" \n net ads dn <dn> <attributes...> \n " \
" \n perform a raw LDAP search on a ADS server and dump the results \n " \
" The DN standard LDAP DN, and the attributes are a list of LDAP fields \n " \
" to show in the results \n \n " \
" Example: net ads dn 'CN=administrator,CN=Users,DC=my,DC=domain' sAMAccountName \n \n "
) ;
net_common_flags_usage ( argc , argv ) ;
return - 1 ;
}
/*
general ADS search function . Useful in diagnosing problems in ADS
*/
static int net_ads_dn ( int argc , const char * * argv )
{
ADS_STRUCT * ads ;
ADS_STATUS rc ;
const char * dn ;
const char * * attrs ;
2006-09-04 01:07:16 +04:00
LDAPMessage * res = NULL ;
2003-03-18 01:33:34 +03:00
if ( argc < 1 ) {
return net_ads_dn_usage ( argc , argv ) ;
}
2006-08-17 16:44:59 +04:00
if ( ! ADS_ERR_OK ( ads_startup ( False , & ads ) ) ) {
2003-03-18 01:33:34 +03:00
return - 1 ;
}
dn = argv [ 0 ] ;
attrs = ( argv + 1 ) ;
rc = ads_do_search_all ( ads , dn ,
LDAP_SCOPE_BASE ,
" (objectclass=*) " , attrs , & res ) ;
if ( ! ADS_ERR_OK ( rc ) ) {
2006-01-18 00:22:00 +03:00
d_fprintf ( stderr , " search failed: %s \n " , ads_errstr ( rc ) ) ;
2004-06-23 04:20:31 +04:00
ads_destroy ( & ads ) ;
2003-03-18 01:33:34 +03:00
return - 1 ;
}
d_printf ( " Got %d replies \n \n " , ads_count_replies ( ads , res ) ) ;
/* dump the results */
ads_dump ( ads , res ) ;
ads_msgfree ( ads , res ) ;
ads_destroy ( & ads ) ;
return 0 ;
}
2006-04-28 18:44:43 +04:00
/*
help for net ads sid search
*/
static int net_ads_sid_usage ( int argc , const char * * argv )
{
d_printf (
" \n net ads sid <sid> <attributes...> \n " \
" \n perform a raw LDAP search on a ADS server and dump the results \n " \
" The SID is in string format, and the attributes are a list of LDAP fields \n " \
" to show in the results \n \n " \
" Example: net ads sid 'S-1-5-32' distinguishedName \n \n "
) ;
net_common_flags_usage ( argc , argv ) ;
return - 1 ;
}
/*
general ADS search function . Useful in diagnosing problems in ADS
*/
static int net_ads_sid ( int argc , const char * * argv )
{
ADS_STRUCT * ads ;
ADS_STATUS rc ;
const char * sid_string ;
const char * * attrs ;
2006-09-04 01:07:16 +04:00
LDAPMessage * res = NULL ;
2006-04-28 18:44:43 +04:00
DOM_SID sid ;
if ( argc < 1 ) {
return net_ads_sid_usage ( argc , argv ) ;
}
2006-08-17 16:44:59 +04:00
if ( ! ADS_ERR_OK ( ads_startup ( False , & ads ) ) ) {
2006-04-28 18:44:43 +04:00
return - 1 ;
}
sid_string = argv [ 0 ] ;
attrs = ( argv + 1 ) ;
if ( ! string_to_sid ( & sid , sid_string ) ) {
2006-04-30 03:41:09 +04:00
d_fprintf ( stderr , " could not convert sid \n " ) ;
2006-04-28 18:44:43 +04:00
ads_destroy ( & ads ) ;
return - 1 ;
}
rc = ads_search_retry_sid ( ads , & res , & sid , attrs ) ;
if ( ! ADS_ERR_OK ( rc ) ) {
d_fprintf ( stderr , " search failed: %s \n " , ads_errstr ( rc ) ) ;
ads_destroy ( & ads ) ;
return - 1 ;
}
d_printf ( " Got %d replies \n \n " , ads_count_replies ( ads , res ) ) ;
/* dump the results */
ads_dump ( ads , res ) ;
ads_msgfree ( ads , res ) ;
ads_destroy ( & ads ) ;
return 0 ;
}
2004-06-23 01:58:35 +04:00
static int net_ads_keytab_usage ( int argc , const char * * argv )
{
d_printf (
" net ads keytab <COMMAND> \n " \
" <COMMAND> can be either: \n " \
" CREATE Creates a fresh keytab \n " \
" ADD Adds new service principal \n " \
" FLUSH Flushes out all keytab entries \n " \
" HELP Prints this help message \n " \
" The ADD command will take arguments, the other commands \n " \
" will not take any arguments. The arguments given to ADD \n " \
" should be a list of principals to add. For example, \n " \
" net ads keytab add srv1 srv2 \n " \
" will add principals for the services srv1 and srv2 to the \n " \
" system's keytab. \n " \
" \n "
) ;
return - 1 ;
}
static int net_ads_keytab_flush ( int argc , const char * * argv )
{
int ret ;
ADS_STRUCT * ads ;
2006-08-17 16:44:59 +04:00
if ( ! ADS_ERR_OK ( ads_startup ( True , & ads ) ) ) {
2004-06-23 01:58:35 +04:00
return - 1 ;
}
ret = ads_keytab_flush ( ads ) ;
ads_destroy ( & ads ) ;
return ret ;
}
static int net_ads_keytab_add ( int argc , const char * * argv )
{
int i ;
int ret = 0 ;
ADS_STRUCT * ads ;
d_printf ( " Processing principals to add... \n " ) ;
2006-08-17 16:44:59 +04:00
if ( ! ADS_ERR_OK ( ads_startup ( True , & ads ) ) ) {
2004-06-23 01:58:35 +04:00
return - 1 ;
}
for ( i = 0 ; i < argc ; i + + ) {
ret | = ads_keytab_add_entry ( ads , argv [ i ] ) ;
}
ads_destroy ( & ads ) ;
return ret ;
}
static int net_ads_keytab_create ( int argc , const char * * argv )
{
ADS_STRUCT * ads ;
int ret ;
2006-08-17 16:44:59 +04:00
if ( ! ADS_ERR_OK ( ads_startup ( True , & ads ) ) ) {
2004-06-23 01:58:35 +04:00
return - 1 ;
}
ret = ads_keytab_create_default ( ads ) ;
ads_destroy ( & ads ) ;
return ret ;
}
int net_ads_keytab ( int argc , const char * * argv )
{
struct functable func [ ] = {
{ " CREATE " , net_ads_keytab_create } ,
{ " ADD " , net_ads_keytab_add } ,
{ " FLUSH " , net_ads_keytab_flush } ,
{ " HELP " , net_ads_keytab_usage } ,
{ NULL , NULL }
} ;
if ( ! lp_use_kerberos_keytab ( ) ) {
d_printf ( " \n Warning: \" use kerberos keytab \" must be set to \" true \" in order to \
use keytab functions . \ n " );
}
return net_run_function ( argc , argv , func , net_ads_keytab_usage ) ;
}
2003-03-18 01:33:34 +03:00
2002-04-04 06:53:42 +04:00
int net_ads_help ( int argc , const char * * argv )
{
struct functable func [ ] = {
{ " USER " , net_ads_user_usage } ,
2002-07-15 14:35:28 +04:00
{ " GROUP " , net_ads_group_usage } ,
{ " PRINTER " , net_ads_printer_usage } ,
{ " SEARCH " , net_ads_search_usage } ,
2002-04-04 06:53:42 +04:00
{ " INFO " , net_ads_info } ,
2006-07-26 19:26:51 +04:00
{ " JOIN " , net_ads_join_usage } ,
2006-08-24 19:43:32 +04:00
{ " DNS " , net_ads_dns_usage } ,
2002-04-04 06:53:42 +04:00
{ " LEAVE " , net_ads_leave } ,
{ " STATUS " , net_ads_status } ,
{ " PASSWORD " , net_ads_password } ,
2003-04-15 02:27:09 +04:00
{ " CHANGETRUSTPW " , net_ads_changetrustpw } ,
2002-04-04 06:53:42 +04:00
{ NULL , NULL }
} ;
return net_run_function ( argc , argv , func , net_ads_usage ) ;
}
2001-12-20 06:54:52 +03:00
2001-11-25 03:18:11 +03:00
int net_ads ( int argc , const char * * argv )
{
struct functable func [ ] = {
2001-12-13 16:19:20 +03:00
{ " INFO " , net_ads_info } ,
2001-11-25 03:18:11 +03:00
{ " JOIN " , net_ads_join } ,
2002-08-17 18:45:04 +04:00
{ " TESTJOIN " , net_ads_testjoin } ,
2001-11-25 03:18:11 +03:00
{ " LEAVE " , net_ads_leave } ,
2001-11-25 04:06:56 +03:00
{ " STATUS " , net_ads_status } ,
2001-11-25 04:31:07 +03:00
{ " USER " , net_ads_user } ,
{ " GROUP " , net_ads_group } ,
2006-08-24 19:43:32 +04:00
{ " DNS " , net_ads_dns } ,
2001-12-20 06:54:52 +03:00
{ " PASSWORD " , net_ads_password } ,
2003-04-15 02:27:09 +04:00
{ " CHANGETRUSTPW " , net_ads_changetrustpw } ,
2002-02-02 05:06:03 +03:00
{ " PRINTER " , net_ads_printer } ,
2002-07-15 14:35:28 +04:00
{ " SEARCH " , net_ads_search } ,
2003-03-18 01:33:34 +03:00
{ " DN " , net_ads_dn } ,
2006-04-28 18:44:43 +04:00
{ " SID " , net_ads_sid } ,
2002-08-17 18:45:04 +04:00
{ " WORKGROUP " , net_ads_workgroup } ,
2002-09-25 19:19:00 +04:00
{ " LOOKUP " , net_ads_lookup } ,
2004-06-23 01:58:35 +04:00
{ " KEYTAB " , net_ads_keytab } ,
2006-09-29 21:15:45 +04:00
{ " GPO " , net_ads_gpo } ,
2002-04-04 06:53:42 +04:00
{ " HELP " , net_ads_help } ,
2001-11-25 03:18:11 +03:00
{ NULL , NULL }
} ;
return net_run_function ( argc , argv , func , net_ads_usage ) ;
}
# else
2004-06-24 23:25:20 +04:00
static int net_ads_noads ( void )
2001-11-25 03:18:11 +03:00
{
2006-01-18 00:22:00 +03:00
d_fprintf ( stderr , " ADS support not compiled in \n " ) ;
2001-11-25 03:18:11 +03:00
return - 1 ;
}
2004-06-23 01:58:35 +04:00
int net_ads_keytab ( int argc , const char * * argv )
{
return net_ads_noads ( ) ;
}
2002-04-04 20:47:24 +04:00
int net_ads_usage ( int argc , const char * * argv )
{
return net_ads_noads ( ) ;
}
2002-04-04 07:14:25 +04:00
int net_ads_help ( int argc , const char * * argv )
{
2002-04-04 20:47:24 +04:00
return net_ads_noads ( ) ;
2002-04-04 07:14:25 +04:00
}
2003-04-15 02:27:09 +04:00
int net_ads_changetrustpw ( int argc , const char * * argv )
{
return net_ads_noads ( ) ;
}
2002-03-16 04:30:09 +03:00
int net_ads_join ( int argc , const char * * argv )
2002-04-04 20:47:24 +04:00
{
return net_ads_noads ( ) ;
}
int net_ads_user ( int argc , const char * * argv )
{
return net_ads_noads ( ) ;
}
2002-07-15 14:35:28 +04:00
int net_ads_group ( int argc , const char * * argv )
{
return net_ads_noads ( ) ;
}
2002-04-04 20:47:24 +04:00
/* this one shouldn't display a message */
int net_ads_check ( void )
2002-03-16 04:30:09 +03:00
{
return - 1 ;
}
2006-07-17 15:04:47 +04:00
int net_ads_check_our_domain ( void )
{
return - 1 ;
}
2001-11-25 03:18:11 +03:00
int net_ads ( int argc , const char * * argv )
{
2001-11-26 07:53:08 +03:00
return net_ads_usage ( argc , argv ) ;
2001-11-25 03:18:11 +03:00
}
2006-07-20 00:56:11 +04:00
# endif /* WITH_ADS */