- if the user already exists then ignore the -add command
[kai/samba.git] / source / utils / smbpasswd.c
index c820ad673a000a2c7f98bcb5a3c9932487f527b2..d20ff42c0e8a3f5bff275d79dba1943ffd5b0aa3 100644 (file)
@@ -1,5 +1,3 @@
-#ifdef SMB_PASSWD
-
 /*
  * Unix SMB/Netbios implementation. Version 1.9. smbpasswd module. Copyright
  * (C) Jeremy Allison 1995-1997.
@@ -20,7 +18,6 @@
  */
 
 #include "includes.h"
-#include "des.h"
 
 /* Static buffers we will return. */
 static struct smb_passwd pw_buf;
@@ -203,7 +200,7 @@ _my_get_smbpwnam(FILE * fp, char *name, BOOL * valid_old_pwd,
  */
 static void usage(char *name)
 {
-       fprintf(stderr, "Usage is : %s [username]\n", name);
+       fprintf(stderr, "Usage is : %s [-add] [username] [password]\n", name);
        exit(1);
 }
 
@@ -222,6 +219,8 @@ static void usage(char *name)
   FILE           *fp;
   BOOL            valid_old_pwd = False;
   BOOL                         got_valid_nt_entry = False;
+  BOOL            add_user = False;
+  int             add_pass = 0;
   long            seekpos;
   int             pwfd;
   char            ascii_p16[66];
@@ -249,20 +248,51 @@ static void usage(char *name)
   real_uid = getuid();
   
   /* Deal with usage problems */
-  if (real_uid == 0) {
-    /* As root we can change anothers password. */
-    if (argc != 1 && argc != 2)
+  if (real_uid == 0)
+  {
+    /* As root we can change anothers password and add a user. */
+    if (argc > 4 )
       usage(argv[0]);
-  } else if (argc != 1)
+  }
+  else if (argc == 2 || argc > 3)
+  {
+    fprintf(stderr, "%s: Only root can set anothers password.\n", argv[0]);
     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);
+  if (real_uid == 0 && (argc > 1))
+  {
+    /* We are root - check if we should add the user */
+    if ((argv[1][0] == '-') && (argv[1][1] == 'a'))
+      add_user = True;
+
+    if(add_user && (argc <= 2 || argc > 4))
+      usage(argv[0]);
+
+    /* root can specify password on command-line */
+    if (argc == (add_user ? 4 : 3))
+    {
+      /* -a argument (add_user): new password is 3rd argument. */
+      /* no -a argument (add_user): new password is 2nd argument */
+
+      add_pass = add_user ? 3 : 2;
+    }
+
+    /* If we are root we can change another's password. */
+    strncpy(user_name, add_user ? argv[2] : argv[1], sizeof(user_name) - 1);
     user_name[sizeof(user_name) - 1] = '\0';
+
     pwd = getpwnam(user_name);
-  } else {
+  }
+  else
+  {
+    /* non-root can specify old pass / new pass on command-line */
+    if (argc == 3)
+    {
+       /* non-root specifies new password as 2nd argument */
+       add_pass = 2;
+    }
+
     pwd = getpwuid(real_uid);
   }
   
@@ -270,24 +300,50 @@ static void usage(char *name)
     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));
+  if (real_uid != 0)
+  {
+    if (add_pass)
+    {
+      /* old password, as non-root, is 1st argument */
+      strncpy(old_passwd, argv[1], sizeof(fstring));
+    }
+    else
+    {
+      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 (add_pass)
+  {
+    /* new password is specified on the command line */
+    strncpy(new_passwd, argv[add_user ? 3 : 2], sizeof(new_passwd) - 1);
+    new_passwd[sizeof(new_passwd) - 1] = '\0';
+  }
+  else
+  {
+    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 (strncmp(p, new_passwd, sizeof(fstring)-1))
+    {
+      fprintf(stderr, "%s: Mismatch - password unchanged.\n", argv[0]);
+      exit(1);
+    }
   }
   
-  if (new_passwd[0] == '\0') {
+  if (new_passwd[0] == '\0')
+  {
     printf("Password not set\n");
     exit(0);
   }
@@ -319,14 +375,24 @@ static void usage(char *name)
    * 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);
+  fp = fopen(pfile, "r+");
+  if (!fp && errno == ENOENT) {
+         fp = fopen(pfile, "w");
+         if (fp) {
+                 fprintf(fp, "# Samba SMB password file\n");
+                 fclose(fp);
+                 fp = fopen(pfile, "r+");
+         }
   }
+  if (!fp) {
+         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));
   
@@ -347,26 +413,95 @@ static void usage(char *name)
   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);
+    if(add_user == False) {
+      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);
     }
-    /* 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);
+
+    /* Create a new smb passwd entry and set it to the given password. */
+    {
+      int fd;
+      int new_entry_length;
+      char *new_entry;
+      long offpos;
+
+      /* The add user write needs to be atomic - so get the fd from 
+         the fp and do a raw write() call.
+       */
+      fd = fileno(fp);
+
+      if((offpos = lseek(fd, 0, SEEK_END)) == -1) {
+        fprintf(stderr, "%s: Failed to add entry for user %s to file %s. \
+Error was %s\n", argv[0], pwd->pw_name, pfile, strerror(errno));
+        fclose(fp);
+        pw_file_unlock(lockfd);
+        exit(1);
+      }
+
+      new_entry_length = strlen(pwd->pw_name) + 1 + 15 + 1 + 
+                         32 + 1 + 32 + 1 + strlen(pwd->pw_gecos) + 
+                         1 + strlen(pwd->pw_dir) + 1 + 
+                         strlen(pwd->pw_shell) + 1;
+      if((new_entry = (char *)malloc( new_entry_length )) == 0) {
+        fprintf(stderr, "%s: Failed to add entry for user %s to file %s. \
+Error was %s\n", argv[0], pwd->pw_name, pfile, strerror(errno));
+        fclose(fp);
+        pw_file_unlock(lockfd);
+        exit(1);
+      }
+
+      sprintf(new_entry, "%s:%u:", pwd->pw_name, pwd->pw_uid);
+      p = &new_entry[strlen(new_entry)];
+      for( i = 0; i < 16; i++)
+        sprintf(&p[i*2], "%02X", new_p16[i]);
+      p += 32;
+      *p++ = ':';
+      for( i = 0; i < 16; i++)
+        sprintf(&p[i*2], "%02X", new_nt_p16[i]);
+      p += 32;
+      *p++ = ':';
+      sprintf(p, "%s:%s:%s\n", pwd->pw_gecos, 
+              pwd->pw_dir, pwd->pw_shell);
+      if(write(fd, new_entry, strlen(new_entry)) != strlen(new_entry)) {
+        fprintf(stderr, "%s: Failed to add entry for user %s to file %s. \
+Error was %s\n", argv[0], pwd->pw_name, pfile, strerror(errno));
+        /* Remove the entry we just wrote. */
+        if(ftruncate(fd, offpos) == -1) {
+          fprintf(stderr, "%s: ERROR failed to ftruncate file %s. \
+Error was %s. Password file may be corrupt ! Please examine by hand !\n", 
+                   argv[0], pwd->pw_name, strerror(errno));
+        }
+        fclose(fp);
+        pw_file_unlock(lockfd);
+        exit(1);
+      }
+      
+      fclose(fp);  
+      pw_file_unlock(lockfd);  
+      exit(0);
+    }
+  } else {
+         /* the entry already existed */
+         add_user = False;
+  }
+
+  /* If we are root or the password is 'NO PASSWORD' then
+     we don't need to check the old password. */
+  if (real_uid != 0) {
+    if (valid_old_pwd == False) {
+      fprintf(stderr, "%s: User %s has no old SMB password.\n", argv[0], pwd->pw_name);
+    }
+    /* Check the old Lanman password - NULL means 'NO PASSWORD' */
+    if (smb_pwent->smb_passwd != NULL) {
+      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) {
@@ -444,14 +579,3 @@ static void usage(char *name)
   return 0;
 }
 
-#else
-
-#include "includes.h"
-
-int 
-main(int argc, char **argv)
-{
-  printf("smb password encryption not selected in Makefile\n");
-  return 0;
-}
-#endif