mirror of
https://github.com/samba-team/samba.git
synced 2025-01-19 10:03:58 +03:00
0d8dcfa13c
The biggest thing is the integration of Lukes new nmbd. Its still largely untested, so we will really need some feedback I've also added auto prototype generation and cleaned up a lot of minor things as a result
-
458 lines
12 KiB
C
458 lines
12 KiB
C
#ifdef SMB_PASSWD
|
|
|
|
/*
|
|
* Unix SMB/Netbios implementation. Version 1.9. smbpasswd module. Copyright
|
|
* (C) Jeremy Allison 1995.
|
|
*
|
|
* 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"
|
|
#include "des.h"
|
|
|
|
/* Static buffers we will return. */
|
|
static struct smb_passwd pw_buf;
|
|
static pstring user_name;
|
|
static unsigned char smbpwd[16];
|
|
static unsigned char smbntpwd[16];
|
|
|
|
static int gethexpwd(char *p, char *pwd)
|
|
{
|
|
int i;
|
|
unsigned char lonybble, hinybble;
|
|
char *hexchars = "0123456789ABCDEF";
|
|
char *p1, *p2;
|
|
for (i = 0; i < 32; i += 2) {
|
|
hinybble = toupper(p[i]);
|
|
lonybble = toupper(p[i + 1]);
|
|
|
|
p1 = strchr(hexchars, hinybble);
|
|
p2 = strchr(hexchars, lonybble);
|
|
if (!p1 || !p2)
|
|
return (False);
|
|
|
|
hinybble = PTR_DIFF(p1, hexchars);
|
|
lonybble = PTR_DIFF(p2, hexchars);
|
|
|
|
pwd[i / 2] = (hinybble << 4) | lonybble;
|
|
}
|
|
return (True);
|
|
}
|
|
|
|
struct smb_passwd *
|
|
_my_get_smbpwnam(FILE * fp, char *name, BOOL * valid_old_pwd,
|
|
BOOL *got_valid_nt_entry, long *pwd_seekpos)
|
|
{
|
|
char linebuf[256];
|
|
unsigned char c;
|
|
unsigned char *p;
|
|
long uidval;
|
|
long linebuf_len;
|
|
|
|
/*
|
|
* Scan the file, a line at a time and check if the name matches.
|
|
*/
|
|
while (!feof(fp)) {
|
|
linebuf[0] = '\0';
|
|
*pwd_seekpos = ftell(fp);
|
|
|
|
fgets(linebuf, 256, fp);
|
|
if (ferror(fp))
|
|
return NULL;
|
|
|
|
/*
|
|
* Check if the string is terminated with a newline - if not
|
|
* then we must keep reading and discard until we get one.
|
|
*/
|
|
linebuf_len = strlen(linebuf);
|
|
if (linebuf[linebuf_len - 1] != '\n') {
|
|
c = '\0';
|
|
while (!ferror(fp) && !feof(fp)) {
|
|
c = fgetc(fp);
|
|
if (c == '\n')
|
|
break;
|
|
}
|
|
} else
|
|
linebuf[linebuf_len - 1] = '\0';
|
|
|
|
if ((linebuf[0] == 0) && feof(fp))
|
|
break;
|
|
/*
|
|
* The line we have should be of the form :-
|
|
*
|
|
* username:uid:[32hex bytes]:....other flags presently
|
|
* ignored....
|
|
*
|
|
* or,
|
|
*
|
|
* username:uid:[32hex bytes]:[32hex bytes]:....ignored....
|
|
*
|
|
* if Windows NT compatible passwords are also present.
|
|
*/
|
|
|
|
if (linebuf[0] == '#' || linebuf[0] == '\0')
|
|
continue;
|
|
p = (unsigned char *) strchr(linebuf, ':');
|
|
if (p == NULL)
|
|
continue;
|
|
/*
|
|
* As 256 is shorter than a pstring we don't need to check
|
|
* length here - if this ever changes....
|
|
*/
|
|
strncpy(user_name, linebuf, PTR_DIFF(p, linebuf));
|
|
user_name[PTR_DIFF(p, linebuf)] = '\0';
|
|
if (!strequal(user_name, name))
|
|
continue;
|
|
|
|
/* User name matches - get uid and password */
|
|
p++; /* Go past ':' */
|
|
if (!isdigit(*p))
|
|
return (False);
|
|
|
|
uidval = atoi((char *) p);
|
|
while (*p && isdigit(*p))
|
|
p++;
|
|
|
|
if (*p != ':')
|
|
return (False);
|
|
|
|
/*
|
|
* Now get the password value - this should be 32 hex digits
|
|
* which are the ascii representations of a 16 byte string.
|
|
* Get two at a time and put them into the password.
|
|
*/
|
|
p++;
|
|
*pwd_seekpos += PTR_DIFF(p, linebuf); /* Save exact position
|
|
* of passwd in file -
|
|
* this is used by
|
|
* smbpasswd.c */
|
|
if (*p == '*' || *p == 'X') {
|
|
/* Password deliberately invalid - end here. */
|
|
*valid_old_pwd = False;
|
|
*got_valid_nt_entry = False;
|
|
pw_buf.smb_nt_passwd = NULL; /* No NT password (yet)*/
|
|
|
|
/* Now check if the NT compatible password is
|
|
available. */
|
|
p += 33; /* Move to the first character of the line after
|
|
the lanman password. */
|
|
if ((linebuf_len >= (PTR_DIFF(p, linebuf) + 33)) && (p[32] == ':')) {
|
|
/* NT Entry was valid - even if 'X' or '*', can be overwritten */
|
|
*got_valid_nt_entry = True;
|
|
if (*p != '*' && *p != 'X') {
|
|
if (gethexpwd((char *)p,(char *)smbntpwd))
|
|
pw_buf.smb_nt_passwd = smbntpwd;
|
|
}
|
|
}
|
|
pw_buf.smb_name = user_name;
|
|
pw_buf.smb_userid = uidval;
|
|
pw_buf.smb_passwd = NULL; /* No password */
|
|
return (&pw_buf);
|
|
}
|
|
if (linebuf_len < (PTR_DIFF(p, linebuf) + 33))
|
|
return (False);
|
|
|
|
if (p[32] != ':')
|
|
return (False);
|
|
|
|
if (!strncasecmp((char *)p, "NO PASSWORD", 11)) {
|
|
pw_buf.smb_passwd = NULL; /* No password */
|
|
} else {
|
|
if(!gethexpwd((char *)p,(char *)smbpwd))
|
|
return False;
|
|
pw_buf.smb_passwd = smbpwd;
|
|
}
|
|
|
|
pw_buf.smb_name = user_name;
|
|
pw_buf.smb_userid = uidval;
|
|
pw_buf.smb_nt_passwd = NULL;
|
|
*got_valid_nt_entry = False;
|
|
*valid_old_pwd = True;
|
|
|
|
/* Now check if the NT compatible password is
|
|
available. */
|
|
p += 33; /* Move to the first character of the line after
|
|
the lanman password. */
|
|
if ((linebuf_len >= (PTR_DIFF(p, linebuf) + 33)) && (p[32] == ':')) {
|
|
/* NT Entry was valid - even if 'X' or '*', can be overwritten */
|
|
*got_valid_nt_entry = True;
|
|
if (*p != '*' && *p != 'X') {
|
|
if (gethexpwd((char *)p,(char *)smbntpwd))
|
|
pw_buf.smb_nt_passwd = smbntpwd;
|
|
}
|
|
}
|
|
return &pw_buf;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Print command usage on stderr and die.
|
|
*/
|
|
static void usage(char *name)
|
|
{
|
|
fprintf(stderr, "Usage is : %s [username]\n", name);
|
|
exit(1);
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
int real_uid;
|
|
struct passwd *pwd;
|
|
fstring old_passwd;
|
|
uchar old_p16[16];
|
|
uchar old_nt_p16[16];
|
|
fstring new_passwd;
|
|
uchar new_p16[16];
|
|
uchar new_nt_p16[16];
|
|
char *p;
|
|
struct smb_passwd *smb_pwent;
|
|
FILE *fp;
|
|
BOOL valid_old_pwd = False;
|
|
BOOL got_valid_nt_entry = False;
|
|
long seekpos;
|
|
int pwfd;
|
|
char ascii_p16[66];
|
|
char c;
|
|
int ret, i, err, writelen;
|
|
int lockfd = -1;
|
|
char *pfile = SMB_PASSWD_FILE;
|
|
char readbuf[16 * 1024];
|
|
|
|
TimeInit();
|
|
|
|
setup_logging(argv[0],True);
|
|
|
|
charset_initialise();
|
|
|
|
#ifndef DEBUG_PASSWORD
|
|
/* Check the effective uid */
|
|
if (geteuid() != 0) {
|
|
fprintf(stderr, "%s: Must be setuid root.\n", argv[0]);
|
|
exit(1);
|
|
}
|
|
#endif
|
|
|
|
/* Get the real uid */
|
|
real_uid = getuid();
|
|
|
|
/* Deal with usage problems */
|
|
if (real_uid == 0) {
|
|
/* As root we can change anothers password. */
|
|
if (argc != 1 && argc != 2)
|
|
usage(argv[0]);
|
|
} else if (argc != 1)
|
|
usage(argv[0]);
|
|
|
|
|
|
if (real_uid == 0 && argc == 2) {
|
|
/* If we are root we can change anothers password. */
|
|
strncpy(user_name, argv[1], sizeof(user_name) - 1);
|
|
user_name[sizeof(user_name) - 1] = '\0';
|
|
pwd = getpwnam(user_name);
|
|
} else {
|
|
pwd = getpwuid(real_uid);
|
|
}
|
|
|
|
if (pwd == 0) {
|
|
fprintf(stderr, "%s: Unable to get UNIX password entry for user.\n", argv[0]);
|
|
exit(1);
|
|
}
|
|
/* If we are root we don't ask for the old password. */
|
|
old_passwd[0] = '\0';
|
|
if (real_uid != 0) {
|
|
p = getpass("Old SMB password:");
|
|
strncpy(old_passwd, p, sizeof(fstring));
|
|
old_passwd[sizeof(fstring)-1] = '\0';
|
|
}
|
|
new_passwd[0] = '\0';
|
|
p = getpass("New SMB password:");
|
|
strncpy(new_passwd, p, sizeof(fstring));
|
|
new_passwd[sizeof(fstring)-1] = '\0';
|
|
p = getpass("Retype new SMB password:");
|
|
if (strcmp(p, new_passwd)) {
|
|
fprintf(stderr, "%s: Mismatch - password unchanged.\n", argv[0]);
|
|
exit(1);
|
|
}
|
|
|
|
if (new_passwd[0] == '\0') {
|
|
printf("Password not set\n");
|
|
exit(0);
|
|
}
|
|
|
|
/* Calculate the MD4 hash (NT compatible) of the old and new passwords */
|
|
memset(old_nt_p16, '\0', 16);
|
|
E_md4hash((uchar *)old_passwd, old_nt_p16);
|
|
|
|
memset(new_nt_p16, '\0', 16);
|
|
E_md4hash((uchar *) new_passwd, new_nt_p16);
|
|
|
|
/* Mangle the passwords into Lanman format */
|
|
old_passwd[14] = '\0';
|
|
strupper(old_passwd);
|
|
new_passwd[14] = '\0';
|
|
strupper(new_passwd);
|
|
|
|
/*
|
|
* Calculate the SMB (lanman) hash functions of both old and new passwords.
|
|
*/
|
|
|
|
memset(old_p16, '\0', 16);
|
|
E_P16((uchar *) old_passwd, old_p16);
|
|
|
|
memset(new_p16, '\0', 16);
|
|
E_P16((uchar *) new_passwd, new_p16);
|
|
|
|
/*
|
|
* Open the smbpaswd file XXXX - we need to parse smb.conf to get the
|
|
* filename
|
|
*/
|
|
if ((fp = fopen(pfile, "r+")) == NULL) {
|
|
err = errno;
|
|
fprintf(stderr, "%s: Failed to open password file %s.\n",
|
|
argv[0], pfile);
|
|
errno = err;
|
|
perror(argv[0]);
|
|
exit(err);
|
|
}
|
|
/* Set read buffer to 16k for effiecient reads */
|
|
setvbuf(fp, readbuf, _IOFBF, sizeof(readbuf));
|
|
|
|
/* make sure it is only rw by the owner */
|
|
chmod(pfile, 0600);
|
|
|
|
/* Lock the smbpasswd file for write. */
|
|
if ((lockfd = pw_file_lock(pfile, F_WRLCK, 5)) < 0) {
|
|
err = errno;
|
|
fprintf(stderr, "%s: Failed to lock password file %s.\n",
|
|
argv[0], pfile);
|
|
fclose(fp);
|
|
errno = err;
|
|
perror(argv[0]);
|
|
exit(err);
|
|
}
|
|
/* Get the smb passwd entry for this user */
|
|
smb_pwent = _my_get_smbpwnam(fp, pwd->pw_name, &valid_old_pwd,
|
|
&got_valid_nt_entry, &seekpos);
|
|
if (smb_pwent == NULL) {
|
|
fprintf(stderr, "%s: Failed to find entry for user %s in file %s.\n",
|
|
argv[0], pwd->pw_name, pfile);
|
|
fclose(fp);
|
|
pw_file_unlock(lockfd);
|
|
exit(1);
|
|
}
|
|
/* If we are root we don't need to check the old password. */
|
|
if (real_uid != 0) {
|
|
if ((valid_old_pwd == False) || (smb_pwent->smb_passwd == NULL)) {
|
|
fprintf(stderr, "%s: User %s is disabled, plase contact your administrator to enable it.\n", argv[0], pwd->pw_name);
|
|
fclose(fp);
|
|
pw_file_unlock(lockfd);
|
|
exit(1);
|
|
}
|
|
/* Check the old Lanman password */
|
|
if (memcmp(old_p16, smb_pwent->smb_passwd, 16)) {
|
|
fprintf(stderr, "%s: Couldn't change password.\n", argv[0]);
|
|
fclose(fp);
|
|
pw_file_unlock(lockfd);
|
|
exit(1);
|
|
}
|
|
/* Check the NT password if it exists */
|
|
if (smb_pwent->smb_nt_passwd != NULL) {
|
|
if (memcmp(old_nt_p16, smb_pwent->smb_nt_passwd, 16)) {
|
|
fprintf(stderr, "%s: Couldn't change password.\n", argv[0]);
|
|
fclose(fp);
|
|
pw_file_unlock(lockfd);
|
|
exit(1);
|
|
}
|
|
}
|
|
}
|
|
/*
|
|
* If we get here either we were root or the old password checked out
|
|
* ok.
|
|
*/
|
|
/* Create the 32 byte representation of the new p16 */
|
|
for (i = 0; i < 16; i++) {
|
|
sprintf(&ascii_p16[i * 2], "%02X", (uchar) new_p16[i]);
|
|
}
|
|
if(got_valid_nt_entry) {
|
|
/* Add on the NT md4 hash */
|
|
ascii_p16[32] = ':';
|
|
for (i = 0; i < 16; i++) {
|
|
sprintf(&ascii_p16[(i * 2)+33], "%02X", (uchar) new_nt_p16[i]);
|
|
}
|
|
}
|
|
/*
|
|
* Do an atomic write into the file at the position defined by
|
|
* seekpos.
|
|
*/
|
|
pwfd = fileno(fp);
|
|
ret = lseek(pwfd, seekpos - 1, SEEK_SET);
|
|
if (ret != seekpos - 1) {
|
|
err = errno;
|
|
fprintf(stderr, "%s: seek fail on file %s.\n",
|
|
argv[0], pfile);
|
|
fclose(fp);
|
|
errno = err;
|
|
perror(argv[0]);
|
|
pw_file_unlock(lockfd);
|
|
exit(1);
|
|
}
|
|
/* Sanity check - ensure the character is a ':' */
|
|
if (read(pwfd, &c, 1) != 1) {
|
|
err = errno;
|
|
fprintf(stderr, "%s: read fail on file %s.\n",
|
|
argv[0], pfile);
|
|
fclose(fp);
|
|
errno = err;
|
|
perror(argv[0]);
|
|
pw_file_unlock(lockfd);
|
|
exit(1);
|
|
}
|
|
if (c != ':') {
|
|
fprintf(stderr, "%s: sanity check on passwd file %s failed.\n",
|
|
argv[0], pfile);
|
|
fclose(fp);
|
|
pw_file_unlock(lockfd);
|
|
exit(1);
|
|
}
|
|
writelen = (got_valid_nt_entry) ? 65 : 32;
|
|
if (write(pwfd, ascii_p16, writelen) != writelen) {
|
|
err = errno;
|
|
fprintf(stderr, "%s: write fail in file %s.\n",
|
|
argv[0], pfile);
|
|
fclose(fp);
|
|
errno = err;
|
|
perror(argv[0]);
|
|
pw_file_unlock(lockfd);
|
|
exit(err);
|
|
}
|
|
fclose(fp);
|
|
pw_file_unlock(lockfd);
|
|
printf("Password changed\n");
|
|
return 0;
|
|
}
|
|
|
|
#else
|
|
|
|
#include "includes.h"
|
|
|
|
int
|
|
main(int argc, char **argv)
|
|
{
|
|
printf("smb password encryption not selected in Makefile\n");
|
|
return 0;
|
|
}
|
|
#endif
|