password-quality diff

Pierre Belanger pbelang1 at oss.cantel.rogers.com
Mon May 5 17:29:10 GMT 2003


Hi,

Here's "password-quality.diff" to apply to HEAD. I'm also including
password-quality.smb.conf.5.sgml "diff".

NEW FILES: source/smbd/password-quality.c
            examples/password-quality/password-quality.pl
MOD FILES: source/Makefile.in (added password-quality.o)
            source/param/loadparm.c (added password quality script)
            source/smbd/chgpasswd.c (mod. to call password quality)

Documentation: Sorry but I was not able to ind which file to modif.
for the creation of the "smb.conf.*" files. If someone tells me,
I can provide a diff later. I included a diff againts the old
smb.conf.5.sgml .

Fell free to apply any changes you think is necessary or remove
things you think is ! necessary.

I put some "Doxygen" stuff in there but I really don't know if this
is good or not. Fell free to remove/change it.

The Perl script "simple", I hope it's a "good start" example until
others contribute.

Sorry for the delai, just too much to do since the pass months
besides begin sick like hell for a while.

Best regards,
Pierre B.
-------------- next part --------------
--- examples/password-quality/password-quality.pl	Mon May  5 11:04:41 2003
+++ ../samba-head-work/examples/password-quality/password-quality.pl	Mon May  5 11:03:55 2003
@@ -0,0 +1,187 @@
+#!/usr/bin/perl -w
+#
+#  Simple Password Quality for Samba
+#
+#  Copyright (C) Pierre Belanger 2003 - belanger at pobox.com
+#
+###
+#  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 02139, USA.
+#
+###
+#
+#  The first goal of this program is to show how Samba's "password quality
+#  script" interface works. Fell free to apply your own modifications to
+#  this scritp or to write your own program based on this example. Always
+#  remember to be carefull -- this script is running under uid and gid root!
+# 
+#  This program does the following:
+#
+#  Accept password change request -- if and only if:
+#
+#    1) Password has at least one UPPERcase character
+#    2) Password has at least one LOWERcase character
+#    3) Password has at least one Number character
+#    4) Password has at least one Special character
+#    5) Password has a minimum length of 7 characters
+#    6) Password has a maximum length of 8 characters
+#
+#  All previous values are options that may be turned on or off.
+#
+###
+use strict;
+
+#
+# Configuration options
+#
+my $minLengthPassword = 7;
+my $maxLengthPassword = 8;
+my $mustHaveUppercase = 1; # [A-Z]
+my $mustHaveLowercase = 1; # [a-z]
+my $mustHaveNumber    = 1; # [0-9]
+my $mustHaveSpecial   = 1; # [~!@#$%^&*()_[ ]+=-`{}:;"'<>,./?\|]
+my $MY_PROTOCOL_VERSION = 1; #
+
+# Run as user
+my $runas_user = "nobody";
+
+#
+# End of configuration options
+#
+
+# init variables
+my $line	= "";
+my $protoversion= "";
+my $username	= "";
+my $fullname	= "";
+my $password	= "";
+my $lpassword	= 0;
+my $message	= "";
+
+# NTStatus exit status
+my $NT_STATUS_OK = 0;
+
+# Switch to user $runas_user
+my ($uid,$gid)=(getpwnam($runas_user))[2,3];
+if ($uid && $gid) {
+	$)="$gid $gid";
+	$(=$gid;
+	$<=$uid;
+	$>=$gid;
+	if (($< != $uid) || ($> != $gid)) {
+		printf "NTStatus: NT_STATUS_ACCESS_DENIED\n";
+		printf "Result: Can't switch to user $runas_user!\n";
+		printf ".\n";
+		exit($NT_STATUS_OK);
+	}
+} else {
+	printf "NTStatus: NT_STATUS_ACCESS_DENIED\n";
+	printf "Result: Can't switch to user $runas_user -- getpwnam() failed!\n";
+	printf ".\n";
+	exit($NT_STATUS_OK);
+}
+
+# Get data from smbd
+while ($line = <STDIN>) {
+
+	chomp($line);
+
+	if ("$line" eq ".") {
+		goto CHECKPASSWORD;
+	}
+	if ($line =~ s/^Username: //) {
+		$username = $line;
+		next;
+	}
+	if ($line =~ s/^Fullname: //) {
+		$fullname = $line;
+		next;
+	}
+	if ($line =~ s/^Password: //) {
+		$password = $line;
+		$lpassword = length $password;
+		next;
+	}
+	if ($line =~ s/^Version: //) {
+		$protoversion = $line;
+		next;
+	}
+}
+
+if (! defined $line) {
+	print "NTSTATUS: NT_STATUS_ACCESS_DENIED\n";
+	print "RESULT: FATAL ERROR - Received EOF prior to '.\\n'\n";
+	print ".\n";
+	exit($NT_STATUS_OK);
+}
+
+CHECKPASSWORD:
+
+if ($protoversion gt $MY_PROTOCOL_VERSION) {
+	print "NTSTATUS: NT_STATUS_ACCESS_DENIED\n";
+	print "RESULT: smbd protocol version $protoversion > $MY_PROTOCOL_VERSION\n";
+	print ".\n";
+	exit($NT_STATUS_OK);
+}
+
+if (length $username == 0) {
+	print "NTSTATUS: NT_STATUS_ACCESS_DENIED\n";
+	print "RESULT: FATAL ERROR - Did not receive username\n";
+	print ".\n";
+	exit($NT_STATUS_OK);
+}
+if ($lpassword == 0) {
+	print "NTSTATUS: NT_STATUS_ACCESS_DENIED\n";
+	print "RESULT: FATAL ERROR - Did not receive password\n";
+	print ".\n";
+	exit($NT_STATUS_OK);
+}
+
+if ("$username" eq "$password") {
+	print "NTSTATUS: NT_STATUS_ACCESS_DENIED\n";
+	print "RESULT: FATAL ERROR - Username == Password\n";
+	print ".\n";
+	exit ($NT_STATUS_OK);
+}
+if ($lpassword < $minLengthPassword) {
+	$message .= "Too short. ";
+}
+if ($lpassword > $maxLengthPassword) {
+	$message .= "Too long (" . $lpassword . "). ";
+}
+if (! ($password =~ /[A-Z]+/) && $mustHaveUppercase) {
+	$message .= "No uppercase. ";
+}
+if (! ($password =~ /[a-z]+/) && $mustHaveLowercase) {
+	$message .= "No lowercase. ";
+}
+if (! ($password =~ /[0-9]+/) && $mustHaveNumber) {
+	$message .= "No number. ";
+}
+if (! ($password =~ /[\ -\/]|[:-@]|[\[-`]|[{-~]/) && $mustHaveSpecial) {
+	$message .= "No special char. ";
+}
+
+if (length $message > 0) {
+	$message =~ s/\s*$//; # Strip trailing space.
+	print "NTSTATUS: NT_STATUS_PASSWORD_RESTRICTION\n";
+	print "RESULT: $message\n";
+	print ".\n";
+	exit($NT_STATUS_OK);
+} else {
+	print "NTSTATUS: NT_STATUS_OK\n";
+	print "RESULT: New password accepted.\n";
+	print ".\n";
+	exit($NT_STATUS_OK);
+}
--- source/Makefile.in	Mon May  5 09:26:53 2003
+++ ../samba-head-work/source/Makefile.in	Mon May  5 09:29:45 2003
@@ -331,7 +331,7 @@
 SMBD_OBJ_MAIN = smbd/server.o
 
 SMBD_OBJ_SRV = smbd/files.o smbd/chgpasswd.o smbd/connection.o \
-	       smbd/utmp.o smbd/session.o \
+	       smbd/utmp.o smbd/session.o smbd/password-quality.o \
                smbd/dfree.o smbd/dir.o smbd/password.o smbd/conn.o smbd/fileio.o \
                smbd/ipc.o smbd/lanman.o smbd/negprot.o \
                smbd/message.o smbd/nttrans.o smbd/pipes.o \
--- source/param/loadparm.c	Mon May  5 09:27:06 2003
+++ ../samba-head-work/source/param/loadparm.c	Mon May  5 09:29:52 2003
@@ -290,6 +290,7 @@
 	int name_cache_timeout;
 	BOOL client_signing;
 	param_opt_struct *param_opt;
+	char *szPasswordqualityscript;
 }
 global;
 
@@ -929,6 +930,7 @@
 
 	{"enumports command", P_STRING, P_GLOBAL, &Globals.szEnumPortsCommand, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
 	{"addprinter command", P_STRING, P_GLOBAL, &Globals.szAddPrinterCommand, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
+	{"password quality script", P_STRING, P_GLOBAL, &Globals.szPasswordqualityscript, NULL, NULL, FLAG_GLOBAL},
 	{"deleteprinter command", P_STRING, P_GLOBAL, &Globals.szDeletePrinterCommand, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
 	{"show add printer wizard", P_BOOL, P_GLOBAL, &Globals.bMsAddPrinterWizard, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
 	{"os2 driver map", P_STRING, P_GLOBAL, &Globals.szOs2DriverMap, NULL, NULL, FLAG_ADVANCED | FLAG_DEVELOPER},
@@ -1473,6 +1475,8 @@
 	Globals.bClientUseSpnego = True;
 
 	string_set(&Globals.smb_ports, SMB_PORTS);
+
+	string_set(&Globals.szPasswordqualityscript, "");
 }
 
 static TALLOC_CTX *lp_talloc;
@@ -1593,6 +1597,7 @@
 FN_GLOBAL_STRING(lp_defaultservice, &Globals.szDefaultService)
 FN_GLOBAL_STRING(lp_msg_command, &Globals.szMsgCommand)
 FN_GLOBAL_STRING(lp_dfree_command, &Globals.szDfree)
+FN_GLOBAL_STRING(lp_password_quality_script, &Globals.szPasswordqualityscript)
 FN_GLOBAL_STRING(lp_hosts_equiv, &Globals.szHostsEquiv)
 FN_GLOBAL_STRING(lp_auto_services, &Globals.szAutoServices)
 FN_GLOBAL_STRING(lp_passwd_program, &Globals.szPasswdProgram)
--- source/smbd/password-quality.c	Mon May  5 13:11:20 2003
+++ ../samba-head-work/source/smbd/password-quality.c	Mon May  5 13:01:04 2003
@@ -0,0 +1,458 @@
+/* 
+   Unix SMB/CIFS implementation.
+   Samba utility functions
+
+   Password Quality: Help users not to choose a weak password.
+
+   Copyright (C) Andrew Bartlett 2003
+   Copyright (C) Pierre Belanger 2003 (belanger at pobox.com)
+   
+   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 02139, USA.
+*/
+
+#include "includes.h"
+
+/* Increment when making changes in the communication protocol */
+#define PWQUAL_PROTOCOL_VERSION "1"
+
+static void gotalarm_sig(void);
+static char ascii2hex(char ascii);
+static int ZEROxStr2uint32(char *strx, uint32 *hex32);
+static NTSTATUS password_quality_script(SAM_ACCOUNT *hnd, char *new_passwd);
+static BOOL strhasctrl(const char *str);
+static NTSTATUS pre_chk(const char *username,const char *fullname,char *new_pw);
+
+static int gotalarm;
+
+
+/**@ingroup password-quality
+ * Signal (SIGALRM) function to tell us we timed out.
+ *
+*/
+static void gotalarm_sig(void)
+{
+	gotalarm = 1;
+}
+
+
+/**@ingroup password-quality
+ * Main function to catch weak new passwords
+ *
+ * @param hnd		SAM_ACCOUNT requesting a password change
+ *
+ * @param new_password	The user's new password
+ *
+ * @return		NTSTATUS
+ *
+*/
+NTSTATUS password_quality(SAM_ACCOUNT *hnd, char *new_password)
+{
+	NTSTATUS ntstatusresult;
+
+	ntstatusresult = password_quality_script(hnd, new_password);
+
+	if (!NT_STATUS_IS_OK(ntstatusresult)) {
+		DEBUG(1,("user %s could not change password NTSTATUS=0x%0.8x\n",
+			 pdb_get_username(hnd), ntstatusresult.v));
+		return ntstatusresult;
+	}
+
+	/* Add other supports here if needed */
+
+	DEBUG(0,("user %s changed password\n", pdb_get_username(hnd)));
+	return(ntstatusresult);
+}
+
+
+/**@ingroup password-quality
+ * Function to run the password quality script.
+ *
+ * @param hnd		SAM_ACCOUNT requesting a password change
+ *
+ * @param new_password	The user's new password
+ *
+ * @return		NTSTATUS
+ *
+*/
+static NTSTATUS password_quality_script(SAM_ACCOUNT *hnd, char *new_passwd)
+{
+	int fd1[2], fd2[2];
+	char *cmdname;
+	const char *username, *fullname;
+	pid_t child_pid;
+	NTSTATUS ntprerun;
+
+	/* check if command is configured */
+	cmdname = lp_password_quality_script();
+	if (!cmdname || (*cmdname == '\0'))
+		return NT_STATUS_OK;
+
+	username = pdb_get_username(hnd);
+	fullname = pdb_get_fullname(hnd);
+
+	/* pre-run security check */
+	ntprerun = pre_chk(username, fullname, new_passwd);
+	if (!NT_STATUS_IS_OK(ntprerun)) {
+		return ntprerun;
+	}
+
+        if (pipe(fd1) || pipe(fd2)) {
+		DEBUG(0,("could not create pipes\n"));
+		return NT_STATUS_ACCESS_DENIED;
+	}
+
+	CatchChildLeaveStatus();
+	child_pid = sys_fork();
+
+	if (child_pid < 0) {
+		close(fd1[0]); close(fd1[1]);
+		close(fd2[0]); close(fd2[1]);
+		DEBUG(0,("could not fork -- out of resources\n"));
+		CatchChild();
+		return NT_STATUS_ACCESS_DENIED;
+	}
+
+	if (child_pid > 0) {
+
+		/*
+		 * Parent.
+		 */
+
+		int fd_in, fd_out, done, child_status;
+		pid_t wpid;
+		ssize_t length;
+		char *c, *buf, *reservedword, *msg;
+		pstring pbuf, pntstatus, presult;
+		NTSTATUS ntstatusmap;
+		void (*oldsighandler)(int);
+
+		close(fd1[0]); close(fd2[1]);
+		fd_out = fd1[1]; fd_in = fd2[0];
+
+		asprintf (&buf, "Version: %s\nUsername: %s\nFullName: %s\n"
+			  "Password: %s\n.\n", PWQUAL_PROTOCOL_VERSION,
+			  username, fullname, new_passwd);
+
+		if ((length = sys_write(fd_out, buf, strlen(buf))) < 0 ) {
+			close(fd_out); close(fd_in);
+			SAFE_FREE(buf);
+			DEBUG(0,("sys_write returned %s\n", strerror(errno)));
+			kill(child_pid, SIGKILL);
+			sys_waitpid(child_pid, &child_status, 0);
+		  	return NT_STATUS_ACCESS_DENIED;
+		}
+		SAFE_FREE(buf);
+		DEBUG(5,("sys_write wrote %d characters\n", length));
+
+		/* don't hang in read() due to a broken program */
+		oldsighandler = CatchSignal(SIGALRM,SIGNAL_CAST gotalarm_sig);
+		alarm(15); /* cli->timeout set to 20000ms */
+		gotalarm = 0;
+
+		while((length = read(fd_in, &pbuf[0], PSTRING_LEN - 1)) < 0) {
+
+			if (gotalarm == 1) {
+				kill(child_pid, SIGTERM);
+				DEBUG(0,("read timeout waiting for child\n"));
+			  	break;
+			}
+			if ((length == -1) && (errno == EINTR)) {
+				errno = 0;
+				continue;
+			}
+
+			DEBUG(0,("read error %s\n", strerror(errno)));
+		  	break;
+
+		}
+		close(fd_out); close(fd_in);
+		if (length > 0)
+			DEBUG(5,("read returned %d characters\n", length));
+
+		/* get child exit status */
+		alarm(2); gotalarm = 0;
+		while((wpid = sys_waitpid(child_pid, &child_status, 0)) < 0) {
+
+			if (gotalarm == 1) {
+				DEBUG(0,("exit status timeout\n"));
+				kill(child_pid, SIGKILL);
+				gotalarm = 0; /* avoid loop */
+			  	continue;
+			}
+			if(errno == EINTR) {
+				errno = 0;
+				continue;
+			}
+
+			DEBUG(0,("could not get child exit status\n"));
+			break;
+		}
+		alarm(0);
+		CatchSignal(SIGALRM, SIGNAL_CAST oldsighandler);
+		CatchChild();
+		if (wpid == child_pid)
+			DEBUG(5,("child exit status %d\n", child_status));
+
+		if (length < 0 || wpid < 0)
+			return NT_STATUS_ACCESS_DENIED;
+
+		/* check child exit status */
+		if (!NT_STATUS_IS_OK(map_nt_error_from_unix(child_status))) {
+			DEBUG(0,("error: child exit(%d) != 0\n", child_status));
+			return NT_STATUS_ACCESS_DENIED;
+		}
+
+		if (length == 0) {
+			DEBUG(0,("child returned nothing - read length = 0\n"));
+			return NT_STATUS_ACCESS_DENIED;
+		}
+
+		/* Parse response from external program */
+		pbuf[length] = '\0'; presult[0] = '\0';pntstatus[0] = '\0';
+		done = 0;
+		for (c = &pbuf[0]; *c != '\0'; c++) {
+
+			if (!strcmp(c, ".\n")) {
+				done = 1;
+				break;
+			}
+
+			reservedword = c;
+			if ((c = strpbrk(c, " :")) == (char *)NULL) {
+				DEBUG(1,("received bad response %s\n",
+					 reservedword));
+		  		return NT_STATUS_ACCESS_DENIED;
+			}
+			*c = '\0'; /* end of reserved word */
+			c++;
+
+			while (((*c==' ')||(*c==':'))&&(*c!='\0'))
+				c++;
+			msg = c;
+
+			if ((c = strpbrk(c, "\n")) == (char *)NULL) {
+				DEBUG(1,("received bad response %s: %s!\n",
+					 reservedword, msg));
+		  		return NT_STATUS_ACCESS_DENIED;
+			}
+			*c = '\0'; /* end of message */
+
+			if (!StrCaseCmp(reservedword, "ntstatus")) {
+				pstrcpy(pntstatus, msg);
+				DEBUG(3,("got %s: %s\n", reservedword, msg));
+				continue;
+			}
+			if (!StrCaseCmp(reservedword, "result")) {
+				pstrcpy(presult, msg);
+				DEBUG(3,("got %s: %s\n", reservedword, msg));
+				continue;
+			}
+			DEBUG(1,("unsupported %s: %s\n", reservedword, msg));
+	  		break; /* child is broken or not up to date, break */
+
+		} /* parse end */
+
+		if (!done || (pntstatus[0]=='\0') || (presult[0]=='\0')) {
+			DEBUG(0,("bad communication with child\n"));
+			return NT_STATUS_ACCESS_DENIED;
+		}
+		DEBUG(1,("got NTStatus: %s Result: %s\n", pntstatus, presult));
+
+		if (!StrnCaseCmp(pntstatus, "0x", 2)) {
+			/*
+			 * pntstatus is 0x... hexstring format
+			 */
+			uint32 nthex;
+
+			if (ZEROxStr2uint32(pntstatus, &nthex)) {
+				DEBUG(1,("received bad hex '%s'\n", pntstatus));
+				return NT_STATUS_ACCESS_DENIED;
+			}
+			DEBUG(4,("ZEROxstr2uint32 returned 0x%0.8x\n", nthex));
+			ntstatusmap = NT_STATUS(nthex);
+
+		} else {
+			/*
+			 * pntstatus should be a NTSTATUS string
+			 */
+			ntstatusmap = nt_status_string_to_code(pntstatus);
+
+			if NT_STATUS_EQUAL(ntstatusmap,NT_STATUS_UNSUCCESSFUL) {
+			 	/* if there is no match, deny change */
+				DEBUG(1,("received undefined NTSTATUS %s\n",
+					 pntstatus));
+				return NT_STATUS_ACCESS_DENIED;
+			}
+
+		}
+
+		/* return received hex value or a matched NTSTATUS string */
+		return ntstatusmap;
+
+	} else {
+
+		/*
+		 * Child.
+		 */
+
+		int fd_null;
+
+		CatchChild();
+
+		close(fd1[1]); close(fd2[0]);
+
+		if (sys_dup2(fd1[0], STDIN_FILENO) != STDIN_FILENO) {
+			DEBUG(3,("could not redirect stdin\n"));
+			exit(83);
+		}
+		if (sys_dup2(fd2[1], STDOUT_FILENO) != STDOUT_FILENO) {
+			DEBUG(3,("could not redirect stdout\n"));
+			exit(84);
+		}
+		fd_null = open("/dev/null", O_WRONLY);
+		if (fd_null >= 0 ) {
+			if (sys_dup2(fd_null, STDERR_FILENO) != STDERR_FILENO) {
+				DEBUG(3,("could not redirect stderr\n"));
+				exit(85);
+			}
+			close(fd_null);
+		}
+		DEBUG(5,("child stdin/out/err redirection - done\n"));
+
+		gain_root_privilege();
+		gain_root_group_privilege();
+		DEBUG(5,("gained root uid/gid privilege\n"));
+
+		DEBUG(5,("running command %s\n", cmdname));
+		execl("/bin/sh", "sh", "-c", cmdname, NULL);
+
+		/* not reached */
+		exit(86);
+		return NT_STATUS_ACCESS_DENIED;
+	}
+	/* not reached */
+	return NT_STATUS_ACCESS_DENIED;
+}
+
+
+
+/**@ingroup password-quality
+ * Pre security check function. Checks for CTRL chars.
+ *
+ * @param username	The username requesting password change
+ *
+ * @param fullname	The fullename of the username requesting password change
+ *
+ * @param new_password	The user's new password
+ *
+ * @return		NTSTATUS
+ *
+*/
+static NTSTATUS pre_chk(const char *username, const char *fullname,
+			    char *new_password)
+{
+
+	/* avoid injection attacks */
+	if (strhasctrl(new_password)) {
+		DEBUG(0,("new password contains ctrl char\n"));
+		return NT_STATUS_ILL_FORMED_PASSWORD;
+	}
+	if (strhasctrl(username)) {
+		DEBUG(0,("username contains ctrl char\n"));
+		return NT_STATUS_ACCESS_DENIED;
+	}
+	if (strhasctrl(fullname)) {
+		DEBUG(0,("fullname contains ctrl char\n"));
+		return NT_STATUS_ACCESS_DENIED;
+	}
+
+	return NT_STATUS_OK;
+}
+
+
+
+/**@ingroup password-quality
+ * Returns the 'hex' value of a char.
+ *
+ * @param ascii		character to convert to hex value
+ *
+ * @return		the hex value of the ascii parameter
+ *
+*/
+static char ascii2hex(char ascii)
+{
+	return( (ascii <= '9') ? (ascii - '0') : (ascii - ('A' - 0x0A)));
+}
+
+
+
+/**@ingroup password-quality
+ * Convert a "0x 32bits string" (0x87654321) to uint32.
+ *
+ * @param strx		string to be converted to uint32
+ *
+ * @param hex32		uint32 pointer to save the result
+ *
+ * @return		0 if no error / -1 on error
+ *
+*/
+static int ZEROxStr2uint32(char *strx, uint32 *hex32)
+{
+	char *onehex;
+
+	if (!strx)
+		return(-1);
+
+	if (strlen(strx) > 10 || StrnCaseCmp(strx, "0x", 2)) {
+		return(-1);
+	}
+
+	*hex32 = 0x00000000;
+	for(onehex = &strx[2]; *onehex != '\0'; onehex++) {
+
+		*onehex = toupper(*onehex);
+		if (!((*onehex >= '0') && (*onehex <= '9') ||
+		      (*onehex >= 'A') && (*onehex <= 'F'))) {
+			return(-1);
+		}
+		*hex32 = (*hex32 << 4) | (uint32)ascii2hex(*onehex);
+
+	}
+	return(0);
+}
+
+
+
+/**@ingroup password-quality
+ * Check if string has a CNTRL character.
+ *
+ * @param str		string to be converted to uint32
+ *
+ * @return BOOL		Return True if found a CNTRL character, otherwise
+ *			return False.
+ *
+*/
+static BOOL strhasctrl(const char *str) {
+
+	int i, len;
+
+	len = strlen(str);
+	for (i = 0; i < len; i++) {
+		if (iscntrl((int)str[i])) {
+			return True;
+		}
+	}
+	return False;
+}
--- source/smbd/chgpasswd.c	Mon May  5 09:27:11 2003
+++ ../samba-head-work/source/smbd/chgpasswd.c	Mon May  5 09:29:58 2003
@@ -745,7 +745,6 @@
 			 uchar * ntdata, uchar * nthash)
 {
 	fstring new_passwd;
-	const char *unix_user;
 	SAM_ACCOUNT *sampass = NULL;
 	NTSTATUS nt_status 
 		= check_oem_password(user, lmdata, lmhash, ntdata, nthash,
@@ -763,8 +762,6 @@
 	 * available. JRA.
 	 */
 
-	unix_user = pdb_get_username(sampass);
-
 	nt_status = change_oem_password(sampass, NULL, new_passwd);
 
 	memset(new_passwd, 0, sizeof(new_passwd));
@@ -948,6 +945,7 @@
 {
 	BOOL ret;
 	uint32 min_len;
+	NTSTATUS ntresult;
 
 	if (time(NULL) < pdb_get_pass_can_change_time(hnd)) {
 		DEBUG(1, ("user %s cannot change password now, must wait until %s\n", 
@@ -973,7 +971,11 @@
 /* 		return NT_STATUS_PWD_TOO_SHORT; */
 	}
 
-	/* TODO:  Add cracklib support here */
+	/* Check for weak passwords */
+	ntresult = password_quality(hnd, new_passwd);
+	if ( ! NT_STATUS_IS_OK(ntresult)) {
+		return ntresult;
+	}
 
 	/*
 	 * If unix password sync was requested, attempt to change
-------------- next part --------------
--- smb.conf.5.sgml	Mon May  5 13:27:15 2003
+++ smb.conf.5.sgml.new	Mon May  5 13:26:25 2003
@@ -722,6 +722,7 @@
 		<listitem><para><link linkend="PASSWDCHATDEBUG"><parameter>passwd chat debug</parameter></link></para></listitem>
 		<listitem><para><link linkend="PASSWDPROGRAM"><parameter>passwd program</parameter></link></para></listitem>
 		<listitem><para><link linkend="PASSWORDLEVEL"><parameter>password level</parameter></link></para></listitem>
+		<listitem><para><link linkend="PASSWORDQUALITY"><parameter>password quality script</parameter></link></para></listitem>
 		<listitem><para><link linkend="PASSWORDSERVER"><parameter>password server</parameter></link></para></listitem>
 		<listitem><para><link linkend="PREFEREDMASTER"><parameter>prefered master</parameter></link></para></listitem>
 		<listitem><para><link linkend="PREFERREDMASTER"><parameter>preferred master</parameter></link></para></listitem>
@@ -5667,6 +5668,100 @@
 
 		<para>Default: <command>password level = 0</command></para>
 		<para>Example: <command>password level = 4</command></para>
+		</listitem>
+		</varlistentry>
+
+
+
+		<varlistentry>
+		<term><anchor id="PASSWORDQUALITYSCRIPT">password quality script (G)</term>
+		<listitem><para>Full path to the script that will be called
+		when a request for change password is received. It will be run
+		by smbd as user & group ID "root".</para>
+
+		<para>The script is responsible to accept or refuse the new
+		password based on its own rules. As an example, it can be a
+		script refusing a weak password based on a dictionary word.</para>
+
+		<para>Samba back end communicates with the password quality
+		program by writing data to its STDIN and reading data from its
+		STDOUT. Samba writes a block of data in "Field: value\n"
+		terminated by a ".\n" at the beginning of a new line.</para>
+
+		<para>1) smbd ---> to Password quality script STDIN</para>
+		<screen>
+		<computeroutput>
+Version: Password-quality-protocol-version\n
+		Username: username\n
+		Fullname: fullname\n
+		Password: new-password\n
+		.\n
+		</computeroutput>
+		</screen>
+
+		<para>The above fields are filled with their respective value.
+		Once smbd writes on the last line ".\n", it waits from the
+		script, child process, its response and exit status. If there
+		is still no response after 15 seconds, a SIGTERM is sent
+		to the child process. smbd will then wait no more than 2
+		seconds to get the child's exit status otherwise a SIGKILL
+		is sent to the child process.</para>
+
+		<para>IMPORTANT NOTES: The "password-quality-protocol-version"
+		is an integer value. This value will be increased when changes
+		are made to Samba's <--> Password quality script protocol.
+		The "new-password" may have a leading or trailing space -- be
+		very carefull when parsing the data.</para>
+
+		<para>2) Password quality script STDOUT ---> to smbd</para>
+		<screen>
+		<computeroutput>
+NTStatus: ntstatus-string\n
+	 	Result: result-string\n
+	 	.\n
+		</computeroutput>
+		</screen>
+
+		<para>The "ntstatus-string" value can either use one the
+		pre-defined NTSTATUS (nterr.h) values. For this context:</para>
+
+		<para>NT_STATUS_OK # New password accepted</para>
+		<para>NT_STATUS_ACCESS_DENIED # An error occured</para>
+		<para>NT_STATUS_PASSWORD_RESTRICTION # Too short, too weak, etc</para>
+
+		<para>OR<para>
+
+		The "ntstatus-string" value can be an hex value represented by
+		a string. For example, the NT_STATUS_ACCESS_DENIED hex value is
+		0xC0000022.
+
+		<para>The "result-string" value is used to provide information
+		(debug info) to smbd. For examples:</para>
+
+		<screen>
+		<computeroutput>
+NTStatus: NT_STATUS_PASSWORD_RESTRICTION\n
+		Result: Password is based on dictionary word\n
+		.\n
+
+		or
+
+		NTStatus: 0x0\n
+		Result: New password is accepted\n
+		.\n
+		
+		</computeroutput>
+		</screen>
+
+		<para>To mark the end of communication, a ".\n" on the beginning
+		of a new line is required.</para>
+		<para>smbd will always return an error to the client and will
+		not change the current password unless the NTStatus value is
+		equal to "NT_STATUS_OK" AND the exit status of the script is
+		equal to 0.</para>
+
+		<para>Default: <command>password quality script = &lt;empty string&gt;</command></para>
+		<para>Example: <command>password quality script = /usr/local/samba/sbin/password-quality.pl</command></para>
 		</listitem>
 		</varlistentry>
 


More information about the samba-technical mailing list