Subject: Preventing password changes on remote computers (encrypted

From: Beau Kuiper <[email-masked]>
Date: Thu, 11 Mar 1999 02:32:01 +1100

Hello All,

I needed to be able to prevent the users on a network I am setting
up from changing the special guest account we have set up. We
are using samba as a PDC on a win 95/win 98 network. We use
encrypted passwords so we could have a separate unix user list to
the samba users list.

Now instead of asking someone for a solution to my problem, I
actually coded one myself. I have attached a patch doing this.

WHAT THE PATCH DOES AND HOW IT DOES IT:

I needed individual account control. Ie i wanted normal users to
be able to change their passwords easily, but not change the 
guest accounts password. So i added a new setting to the 
smbpasswd database file in the ACB field. I increased the size 
of the field to 12 characters. I added a new character that can be
inserted, and thought 'P' would do nicely for my job. I added the
constant ACB_PWLOCK     0x0800 so it works much like the other
settings in that field. The way i prevent password changes ( i have
no idea if i did it correctly ) was to check for ACB_PWLOCK after the
password is loaded for changing in smbd/chgpasswd.c. If it
is set, then i change the return value so the password doesn't get
changed and the user is told incorrect old password. But it worked
of me :)

I also modified smbpasswd, and added the -p option, which when
used to add a user or change their password, will put the 'P' 
character in the ACB field in smbpasswd. Unfortunately i haven't got a
way to switch it off yet :(, but you can always edit the smbpasswd
file manual and delete the 'P' char.

The patch applies cleanly to samba 2.0.3.  It is a context patch (3 lines
context (and i managed to get a normal diff, not a reversed diff :). 
To apply:

	1) goto the dir above the samba-2.0.3 dir (eg if the dir for the
	samba source is /usr/src/samba-2.0.3, goto the /usr/src dir)

	2) run the command
		patch -p0 < lockedpasswords.samba.2-0-3.patch		

Then just make samba as usual.

THANKS TO THE SAMBA DEVELOPMENT TEAM / COMMUNITY:

I found modifying the samba code really easy. Thanks for keeping the
source code easy to read and understand. I use samba on as a PDC on
a school network since NT doesn't ship with quotas. I have found samba's
performance and flexibility legendary. I look forward to using the full PDC
features *evil grin*. I hope you find my patch useful.

Please feel free to contact me if you want help / want to flame or want to
shoot me. I don't get enough email :)

Beau Kuiper
[email-masked]
diff -C 3 -r old/samba-2.0.3/source/include/proto.h samba-2.0.3/source/include/proto.h
*** old/samba-2.0.3/source/include/proto.h	Sun Feb 28 06:08:59 1999
--- samba-2.0.3/source/include/proto.h	Wed Mar 10 22:12:23 1999
***************
*** 1260,1265 ****
--- 1260,1266 ----
  
  BOOL local_password_change(char *user_name, BOOL trust_account, BOOL add_user,
  			   BOOL enable_user, BOOL disable_user, BOOL set_no_password,
+ 			   BOOL password_locked,
  			   char *new_passwd, 
  			   char *err_str, size_t err_str_len,
  			   char *msg_str, size_t msg_str_len);
diff -C 3 -r old/samba-2.0.3/source/include/smb.h samba-2.0.3/source/include/smb.h
*** old/samba-2.0.3/source/include/smb.h	Sun Feb 28 06:08:59 1999
--- samba-2.0.3/source/include/smb.h	Wed Mar 10 02:57:00 1999
***************
*** 366,372 ****
  #define ACB_SVRTRUST   0x0100  /* 1 = Server trust account */
  #define ACB_PWNOEXP    0x0200  /* 1 = User password does not expire */
  #define ACB_AUTOLOCK   0x0400  /* 1 = Account auto locked */
!  
  #define MAX_HOURS_LEN 32
  
  struct sam_passwd
--- 366,373 ----
  #define ACB_SVRTRUST   0x0100  /* 1 = Server trust account */
  #define ACB_PWNOEXP    0x0200  /* 1 = User password does not expire */
  #define ACB_AUTOLOCK   0x0400  /* 1 = Account auto locked */
! #define ACB_PWLOCK     0x0800  /* 1 = Password Unchangeable */
! 
  #define MAX_HOURS_LEN 32
  
  struct sam_passwd
***************
*** 1704,1710 ****
   * Size of new password account encoding string. DO NOT CHANGE.
   */
  
! #define NEW_PW_FORMAT_SPACE_PADDED_LEN 14
  
  /*
     Do you want session setups at user level security with a invalid
--- 1705,1711 ----
   * Size of new password account encoding string. DO NOT CHANGE.
   */
  
! #define NEW_PW_FORMAT_SPACE_PADDED_LEN 15
  
  /*
     Do you want session setups at user level security with a invalid
diff -C 3 -r old/samba-2.0.3/source/passdb/passdb.c samba-2.0.3/source/passdb/passdb.c
*** old/samba-2.0.3/source/passdb/passdb.c	Sat Feb  6 10:17:50 1999
--- samba-2.0.3/source/passdb/passdb.c	Wed Mar 10 04:02:24 1999
***************
*** 535,541 ****
    if (acct_ctrl & ACB_AUTOLOCK ) acct_str[i++] = 'L';
    if (acct_ctrl & ACB_PWNOEXP  ) acct_str[i++] = 'X';
    if (acct_ctrl & ACB_DOMTRUST ) acct_str[i++] = 'I';
! 
    for ( ; i < length - 2 ; i++ ) { acct_str[i] = ' '; }
  
    i = length - 2;
--- 535,542 ----
    if (acct_ctrl & ACB_AUTOLOCK ) acct_str[i++] = 'L';
    if (acct_ctrl & ACB_PWNOEXP  ) acct_str[i++] = 'X';
    if (acct_ctrl & ACB_DOMTRUST ) acct_str[i++] = 'I';
!   if (acct_ctrl & ACB_PWLOCK   ) acct_str[i++] = 'P';
!   
    for ( ; i < length - 2 ; i++ ) { acct_str[i] = ' '; }
  
    i = length - 2;
***************
*** 580,585 ****
--- 581,587 ----
  			case 'L': { acct_ctrl |= ACB_AUTOLOCK ; break; /* 'L'ocked account. */ } 
  			case 'X': { acct_ctrl |= ACB_PWNOEXP  ; break; /* No 'X'piry on password */ } 
  			case 'I': { acct_ctrl |= ACB_DOMTRUST ; break; /* 'I'nterdomain trust account. */ }
+   			case 'P': { acct_ctrl |= ACB_PWLOCK   ; break; /* 'P'assword cannot be changed. */ }
              case ' ': { break; }
  			case ':':
  			case '\n':
diff -C 3 -r old/samba-2.0.3/source/passdb/smbpasschange.c samba-2.0.3/source/passdb/smbpasschange.c
*** old/samba-2.0.3/source/passdb/smbpasschange.c	Sun Feb 28 06:09:01 1999
--- samba-2.0.3/source/passdb/smbpasschange.c	Wed Mar 10 22:09:58 1999
***************
*** 57,62 ****
--- 57,63 ----
  
  BOOL local_password_change(char *user_name, BOOL trust_account, BOOL add_user,
  			   BOOL enable_user, BOOL disable_user, BOOL set_no_password,
+ 			   BOOL password_locked,
  			   char *new_passwd, 
  			   char *err_str, size_t err_str_len,
  			   char *msg_str, size_t msg_str_len)
***************
*** 168,173 ****
--- 169,178 ----
  		smb_pwent->smb_nt_passwd = new_nt_p16;
  	}
  	
+ 	if (password_locked)
+ 	{
+ 		smb_pwent->acct_ctrl |= ACB_PWLOCK;
+ 	}		
  	if(mod_smbpwd_entry(smb_pwent,True) = False) {
  		slprintf(err_str, err_str_len-1, "Failed to modify entry for user %s.\n",
  			pwd->pw_name);
diff -C 3 -r old/samba-2.0.3/source/smbd/chgpasswd.c samba-2.0.3/source/smbd/chgpasswd.c
*** old/samba-2.0.3/source/smbd/chgpasswd.c	Sun Feb 28 06:09:09 1999
--- samba-2.0.3/source/smbd/chgpasswd.c	Wed Mar 10 03:28:55 1999
***************
*** 593,598 ****
--- 593,603 ----
  	 * as the plaintext of the old users password is not 
  	 * available. JRA.
  	 */
+ 	
+ 	if (sampw->acct_ctrl & ACB_PWLOCK)
+ 	{
+ 		ret = False;
+ 	}
  
  	if ( ret && lp_unix_password_sync())
  	{
diff -C 3 -r old/samba-2.0.3/source/utils/smbpasswd.c samba-2.0.3/source/utils/smbpasswd.c
*** old/samba-2.0.3/source/utils/smbpasswd.c	Sun Feb 28 06:09:10 1999
--- samba-2.0.3/source/utils/smbpasswd.c	Wed Mar 10 22:26:55 1999
***************
*** 59,64 ****
--- 59,65 ----
  	printf("  -D LEVEL             debug level\n");
  	printf("  -U USER              remote username\n");
  	printf("  -r MACHINE           remote machine\n");
+ 	printf("  -p                   protect password from remote changes\n");
  
  	if (getuid() = 0) {
  		printf("  -R ORDER             name resolve order\n");
***************
*** 225,238 ****
  			    char *old_passwd, char *new_passwd, 
  			    BOOL add_user, BOOL enable_user, 
  			    BOOL disable_user, BOOL set_no_password,
! 			    BOOL trust_account)
  {
  	BOOL ret;
  	pstring err_str;
  	pstring msg_str;
  
  	if (remote_machine != NULL) {
! 		if (add_user || enable_user || disable_user || set_no_password || trust_account) {
  			/* these things can't be done remotely yet */
  			return False;
  		}
--- 226,239 ----
  			    char *old_passwd, char *new_passwd, 
  			    BOOL add_user, BOOL enable_user, 
  			    BOOL disable_user, BOOL set_no_password,
! 			    BOOL trust_account, BOOL password_locked)
  {
  	BOOL ret;
  	pstring err_str;
  	pstring msg_str;
  
  	if (remote_machine != NULL) {
! 		if (add_user || enable_user || disable_user || set_no_password || trust_account || password_locked) {
  			/* these things can't be done remotely yet */
  			return False;
  		}
***************
*** 244,250 ****
  	}
  	
  	ret = local_password_change(user_name, trust_account, add_user, enable_user, 
! 				     disable_user, set_no_password, new_passwd, 
  				     err_str, sizeof(err_str), msg_str, sizeof(msg_str));
  
  	if(*msg_str)
--- 245,251 ----
  	}
  	
  	ret = local_password_change(user_name, trust_account, add_user, enable_user, 
! 				     disable_user, set_no_password, password_locked, new_passwd,
  				     err_str, sizeof(err_str), msg_str, sizeof(msg_str));
  
  	if(*msg_str)
***************
*** 270,282 ****
  	BOOL enable_user = False;
  	BOOL set_no_password = False;
  	BOOL stdin_passwd_get = False;
  	char *user_name = NULL;
  	char *new_domain = NULL;
  	char *new_passwd = NULL;
  	char *old_passwd = NULL;
  	char *remote_machine = NULL;
  
! 	while ((ch = getopt(argc, argv, "adehmnj:r:sR:D:U:")) != EOF) {
  		switch(ch) {
  		case 'a':
  			add_user = True;
--- 271,284 ----
  	BOOL enable_user = False;
  	BOOL set_no_password = False;
  	BOOL stdin_passwd_get = False;
+ 	BOOL password_locked = False;
  	char *user_name = NULL;
  	char *new_domain = NULL;
  	char *new_passwd = NULL;
  	char *old_passwd = NULL;
  	char *remote_machine = NULL;
  
! 	while ((ch = getopt(argc, argv, "adehmnpj:r:sR:D:U:")) != EOF) {
  		switch(ch) {
  		case 'a':
  			add_user = True;
***************
*** 317,322 ****
--- 319,327 ----
  		case 'U':
  			user_name = optarg;
  			break;
+ 		case 'p':
+ 			password_locked = True;
+ 			break;
  		default:
  			usage();
  		}
***************
*** 432,438 ****
  	
  	if (!password_change(remote_machine, user_name, old_passwd, new_passwd,
  			     add_user, enable_user, disable_user, set_no_password,
! 			     trust_account)) {
  		fprintf(stderr,"Failed to change password entry for %s\n", user_name);
  		return 1;
  	} 
--- 437,443 ----
  	
  	if (!password_change(remote_machine, user_name, old_passwd, new_passwd,
  			     add_user, enable_user, disable_user, set_no_password,
! 			     trust_account, password_locked)) {
  		fprintf(stderr,"Failed to change password entry for %s\n", user_name);
  		return 1;
  	} 
***************
*** 464,475 ****
  	struct passwd  *pwd = NULL;
  	int ch;
  	BOOL stdin_passwd_get = False;
  	char *old_passwd = NULL;
  	char *remote_machine = NULL;
  	char *user_name = NULL;
  	char *new_passwd = NULL;
  
! 	while ((ch = getopt(argc, argv, "hD:r:sU:")) != EOF) {
  		switch(ch) {
  		case 'D':
  			DEBUGLEVEL = atoi(optarg);
--- 469,481 ----
  	struct passwd  *pwd = NULL;
  	int ch;
  	BOOL stdin_passwd_get = False;
+ 	BOOL password_locked = False;
  	char *old_passwd = NULL;
  	char *remote_machine = NULL;
  	char *user_name = NULL;
  	char *new_passwd = NULL;
  
! 	while ((ch = getopt(argc, argv, "phD:r:sU:")) != EOF) {
  		switch(ch) {
  		case 'D':
  			DEBUGLEVEL = atoi(optarg);
***************
*** 486,491 ****
--- 492,500 ----
  		case 'U':
  			user_name = optarg;
  			break;
+ 		case 'p':
+ 			password_locked = True;
+ 			break;
  		default:
  			usage();
  		}
***************
*** 536,542 ****
  	}
  
  	if (!password_change(remote_machine, user_name, old_passwd, new_passwd,
! 			     False, False, False, False, False)) {
  		fprintf(stderr,"Failed to change password for %s\n", user_name);
  		return 1;
  	}
--- 545,551 ----
  	}
  
  	if (!password_change(remote_machine, user_name, old_passwd, new_passwd,
! 			     False, False, False, False, False, password_locked)) {
  		fprintf(stderr,"Failed to change password for %s\n", user_name);
  		return 1;
  	}
diff -C 3 -r old/samba-2.0.3/source/web/swat.c samba-2.0.3/source/web/swat.c
*** old/samba-2.0.3/source/web/swat.c	Fri Jan 15 04:07:40 1999
--- samba-2.0.3/source/web/swat.c	Wed Mar 10 22:17:36 1999
***************
*** 631,639 ****
  		printf("Can't setup password database vectors.\n<p>");
  		return False;
  	}
! 	
  	ret = local_password_change(user_name, False, add_user, enable_user, 
! 				     disable_user, False, new_passwd, err_str, sizeof(err_str),
  					 msg_str, sizeof(msg_str));
  
  	if(*msg_str)
--- 631,639 ----
  		printf("Can't setup password database vectors.\n<p>");
  		return False;
  	}
! 
  	ret = local_password_change(user_name, False, add_user, enable_user, 
! 				     disable_user, False, False, new_passwd, err_str, sizeof(err_str),
  					 msg_str, sizeof(msg_str));
  
  	if(*msg_str)