Magellan Linux

Diff of /trunk/mkinitrd-magellan/busybox/loginutils/passwd.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 532 by niro, Sat Sep 1 22:45:15 2007 UTC revision 816 by niro, Fri Apr 24 18:33:46 2009 UTC
# Line 3  Line 3 
3   * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.   * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
4   */   */
5    
6  #include "busybox.h"  #include "libbb.h"
7  #include <syslog.h>  #include <syslog.h>
8    
   
9  static void nuke_str(char *str)  static void nuke_str(char *str)
10  {  {
11   if (str) memset(str, 0, strlen(str));   if (str) memset(str, 0, strlen(str));
12  }  }
13    
   
 static int i64c(int i)  
 {  
  i &= 0x3f;  
  if (i == 0)  
  return '.';  
  if (i == 1)  
  return '/';  
  if (i < 12)  
  return ('0' - 2 + i);  
  if (i < 38)  
  return ('A' - 12 + i);  
  return ('a' - 38 + i);  
 }  
   
   
 static void crypt_make_salt(char *p, int cnt)  
 {  
  unsigned x = x; /* it's pointless to initialize it anyway :) */  
   
  x += getpid() + time(NULL) + clock();  
  do {  
  /* x = (x*1664525 + 1013904223) % 2^32 generator is lame  
  * (low-order bit is not "random", etc...),  
  * but for our purposes it is good enough */  
  x = x*1664525 + 1013904223;  
  /* BTW, Park and Miller's "minimal standard generator" is  
  * x = x*16807 % ((2^31)-1)  
  * It has no problem with visibly alternating lowest bit  
  * but is also weak in cryptographic sense + needs div,  
  * which needs more code (and slower) on many CPUs */  
  *p++ = i64c(x >> 16);  
  *p++ = i64c(x >> 22);  
  } while (--cnt);  
  *p = '\0';  
 }  
   
   
14  static char* new_password(const struct passwd *pw, uid_t myuid, int algo)  static char* new_password(const struct passwd *pw, uid_t myuid, int algo)
15  {  {
16   char salt[sizeof("$N$XXXXXXXX")]; /* "$N$XXXXXXXX" or "XX" */   char salt[sizeof("$N$XXXXXXXX")]; /* "$N$XXXXXXXX" or "XX" */
17   char *orig = "";   char *orig = (char*)"";
18   char *newp = NULL;   char *newp = NULL;
  char *cipher = NULL;  
19   char *cp = NULL;   char *cp = NULL;
20   char *ret = NULL; /* failure so far */   char *ret = NULL; /* failure so far */
21    
22   if (myuid && pw->pw_passwd[0]) {   if (myuid && pw->pw_passwd[0]) {
23     char *encrypted;
24    
25   orig = bb_askpass(0, "Old password:"); /* returns ptr to static */   orig = bb_askpass(0, "Old password:"); /* returns ptr to static */
26   if (!orig)   if (!orig)
27   goto err_ret;   goto err_ret;
28   cipher = pw_encrypt(orig, pw->pw_passwd); /* returns ptr to static */   encrypted = pw_encrypt(orig, pw->pw_passwd, 1); /* returns malloced str */
29   if (strcmp(cipher, pw->pw_passwd) != 0) {   if (strcmp(encrypted, pw->pw_passwd) != 0) {
30   syslog(LOG_WARNING, "incorrect password for '%s'",   syslog(LOG_WARNING, "incorrect password for %s",
31   pw->pw_name);   pw->pw_name);
32   bb_do_delay(FAIL_DELAY);   bb_do_delay(FAIL_DELAY);
33   puts("Incorrect password");   puts("Incorrect password");
34   goto err_ret;   goto err_ret;
35   }   }
36     if (ENABLE_FEATURE_CLEAN_UP) free(encrypted);
37   }   }
38   orig = xstrdup(orig); /* or else bb_askpass() will destroy it */   orig = xstrdup(orig); /* or else bb_askpass() will destroy it */
39   newp = bb_askpass(0, "New password:"); /* returns ptr to static */   newp = bb_askpass(0, "New password:"); /* returns ptr to static */
# Line 89  static char* new_password(const struct p Line 52  static char* new_password(const struct p
52   goto err_ret;   goto err_ret;
53   }   }
54    
55   /*memset(salt, 0, sizeof(salt)); - why?*/   crypt_make_salt(salt, 1, 0); /* des */
  crypt_make_salt(salt, 1); /* des */  
56   if (algo) { /* MD5 */   if (algo) { /* MD5 */
57   strcpy(salt, "$1$");   strcpy(salt, "$1$");
58   crypt_make_salt(salt + 3, 4);   crypt_make_salt(salt + 3, 4, 0);
59   }   }
60   ret = xstrdup(pw_encrypt(newp, salt)); /* returns ptr to static */   /* pw_encrypt returns malloced str */
61     ret = pw_encrypt(newp, salt, 1);
62   /* whee, success! */   /* whee, success! */
63    
64   err_ret:   err_ret:
# Line 103  static char* new_password(const struct p Line 66  static char* new_password(const struct p
66   if (ENABLE_FEATURE_CLEAN_UP) free(orig);   if (ENABLE_FEATURE_CLEAN_UP) free(orig);
67   nuke_str(newp);   nuke_str(newp);
68   if (ENABLE_FEATURE_CLEAN_UP) free(newp);   if (ENABLE_FEATURE_CLEAN_UP) free(newp);
  nuke_str(cipher);  
69   nuke_str(cp);   nuke_str(cp);
70   return ret;   return ret;
71  }  }
72    
73    int passwd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
74  #if 0  int passwd_main(int argc UNUSED_PARAM, char **argv)
 static int get_algo(char *a)  
 {  
  /* standard: MD5 */  
  int x = 1;  
  if (strcasecmp(a, "des") == 0)  
  x = 0;  
  return x;  
 }  
 #endif  
   
   
 static int update_passwd(const char *filename, const char *username,  
  const char *new_pw)  
 {  
  struct stat sb;  
  struct flock lock;  
  FILE *old_fp;  
  FILE *new_fp;  
  char *new_name;  
  char *last_char;  
  unsigned user_len;  
  int old_fd;  
  int new_fd;  
  int i;  
  int ret = 1; /* failure */  
   
  logmode = LOGMODE_STDIO;  
  /* New passwd file, "/etc/passwd+" for now */  
  new_name = xasprintf("%s+", filename);  
  last_char = &new_name[strlen(new_name)-1];  
  username = xasprintf("%s:", username);  
  user_len = strlen(username);  
   
  old_fp = fopen(filename, "r+");  
  if (!old_fp)  
  goto free_mem;  
  old_fd = fileno(old_fp);  
   
  /* Try to create "/etc/passwd+". Wait if it exists. */  
  i = 30;  
  do {  
  // FIXME: on last iteration try w/o O_EXCL but with O_TRUNC?  
  new_fd = open(new_name, O_WRONLY|O_CREAT|O_EXCL,0600);  
  if (new_fd >= 0) goto created;  
  if (errno != EEXIST) break;  
  usleep(100000); /* 0.1 sec */  
  } while (--i);  
  bb_perror_msg("cannot create '%s'", new_name);  
  goto close_old_fp;  
  created:  
  if (!fstat(old_fd, &sb)) {  
  fchmod(new_fd, sb.st_mode & 0777); /* ignore errors */  
  fchown(new_fd, sb.st_uid, sb.st_gid);  
  }  
  new_fp = fdopen(new_fd, "w");  
  if (!new_fp) {  
  close(new_fd);  
  goto unlink_new;  
  }  
   
  /* Backup file is "/etc/passwd-" */  
  last_char[0] = '-';  
  /* Delete old one, create new as a hardlink to current */  
  i = (unlink(new_name) && errno != ENOENT);  
  if (i || link(filename, new_name))  
  bb_perror_msg("warning: cannot create backup copy '%s'", new_name);  
  last_char[0] = '+';  
   
  /* Lock the password file before updating */  
  lock.l_type = F_WRLCK;  
  lock.l_whence = SEEK_SET;  
  lock.l_start = 0;  
  lock.l_len = 0;  
  if (fcntl(old_fd, F_SETLK, &lock) < 0)  
  bb_perror_msg("warning: cannot lock '%s'", filename);  
  lock.l_type = F_UNLCK;  
   
  /* Read current password file, write updated one */  
  while (1) {  
  char *line = xmalloc_fgets(old_fp);  
  if (!line) break; /* EOF/error */  
  if (strncmp(username, line, user_len) == 0) {  
  /* we have a match with "username:"... */  
  const char *cp = line + user_len;  
  /* now cp -> old passwd, skip it: */  
  cp = strchr(cp, ':');  
  if (!cp) cp = "";  
  /* now cp -> ':' after old passwd or -> "" */  
  fprintf(new_fp, "%s%s%s", username, new_pw, cp);  
  /* Erase password in memory */  
  } else  
  fputs(line, new_fp);  
  free(line);  
  }  
  fcntl(old_fd, F_SETLK, &lock);  
   
  /* We do want all of them to execute, thus | instead of || */  
  if ((ferror(old_fp) | fflush(new_fp) | fsync(new_fd) | fclose(new_fp))  
  || rename(new_name, filename)  
  ) {  
  /* At least one of those failed */  
  goto unlink_new;  
  }  
  ret = 0; /* whee, success! */  
   
  unlink_new:  
  if (ret) unlink(new_name);  
   
  close_old_fp:  
  fclose(old_fp);  
   
  free_mem:  
  if (ENABLE_FEATURE_CLEAN_UP) free(new_name);  
  if (ENABLE_FEATURE_CLEAN_UP) free((char*)username);  
  logmode = LOGMODE_BOTH;  
  return ret;  
 }  
   
   
 int passwd_main(int argc, char **argv)  
75  {  {
76   enum {   enum {
77   OPT_algo = 0x1, /* -a - password algorithm */   OPT_algo = 0x1, /* -a - password algorithm */
# Line 238  int passwd_main(int argc, char **argv) Line 80  int passwd_main(int argc, char **argv)
80   OPT_delete = 0x8, /* -d - delete password */   OPT_delete = 0x8, /* -d - delete password */
81   OPT_lud = 0xe,   OPT_lud = 0xe,
82   STATE_ALGO_md5 = 0x10,   STATE_ALGO_md5 = 0x10,
83   /*STATE_ALGO_des = 0x20, not needed yet */   //STATE_ALGO_des = 0x20, not needed yet
84   };   };
85   unsigned opt;   unsigned opt;
86   char *opt_a = "";   int rc;
87     const char *opt_a = "";
88   const char *filename;   const char *filename;
89   char *myname;   char *myname;
90   char *name;   char *name;
# Line 250  int passwd_main(int argc, char **argv) Line 93  int passwd_main(int argc, char **argv)
93   uid_t myuid;   uid_t myuid;
94   struct rlimit rlimit_fsize;   struct rlimit rlimit_fsize;
95   char c;   char c;
96    #if ENABLE_FEATURE_SHADOWPASSWDS
97     /* Using _r function to avoid pulling in static buffers */
98     struct spwd spw;
99     char buffer[256];
100    #endif
101    
102   logmode = LOGMODE_BOTH;   logmode = LOGMODE_BOTH;
103   openlog(applet_name, LOG_NOWAIT, LOG_AUTH);   openlog(applet_name, LOG_NOWAIT, LOG_AUTH);
104   opt = getopt32(argc, argv, "a:lud", &opt_a);   opt = getopt32(argv, "a:lud", &opt_a);
105   argc -= optind;   //argc -= optind;
106   argv += optind;   argv += optind;
107    
108   if (strcasecmp(opt_a, "des") != 0) /* -a */   if (strcasecmp(opt_a, "des") != 0) /* -a */
# Line 262  int passwd_main(int argc, char **argv) Line 110  int passwd_main(int argc, char **argv)
110   //else   //else
111   // opt |= STATE_ALGO_des;   // opt |= STATE_ALGO_des;
112   myuid = getuid();   myuid = getuid();
113   if ((opt & OPT_lud) && (!argc || myuid))   /* -l, -u, -d require root priv and username argument */
114     if ((opt & OPT_lud) && (myuid || !argv[0]))
115   bb_show_usage();   bb_show_usage();
116    
117   myname = xstrdup(bb_getpwuid(NULL, myuid, -1));   /* Will complain and die if username not found */
118   name = argc ? argv[0] : myname;   myname = xstrdup(bb_getpwuid(NULL, -1, myuid));
119     name = argv[0] ? argv[0] : myname;
120    
121   pw = getpwnam(name);   pw = getpwnam(name);
122   if (!pw) bb_error_msg_and_die("unknown user %s", name);   if (!pw)
123     bb_error_msg_and_die("unknown user %s", name);
124   if (myuid && pw->pw_uid != myuid) {   if (myuid && pw->pw_uid != myuid) {
125   /* LOGMODE_BOTH */   /* LOGMODE_BOTH */
126   bb_error_msg_and_die("%s can't change password for %s", myname, name);   bb_error_msg_and_die("%s can't change password for %s", myname, name);
127   }   }
128    
  filename = bb_path_passwd_file;  
129  #if ENABLE_FEATURE_SHADOWPASSWDS  #if ENABLE_FEATURE_SHADOWPASSWDS
130   {   {
131   struct spwd *sp = getspnam(name);   /* getspnam_r may return 0 yet set result to NULL.
132   if (!sp) {   * At least glibc 2.4 does this. Be extra paranoid here. */
133     struct spwd *result = NULL;
134     if (getspnam_r(pw->pw_name, &spw, buffer, sizeof(buffer), &result)
135     || !result || strcmp(result->sp_namp, pw->pw_name) != 0) {
136   /* LOGMODE_BOTH */   /* LOGMODE_BOTH */
137   bb_error_msg("no record of %s in %s, using %s",   bb_error_msg("no record of %s in %s, using %s",
138   name, bb_path_shadow_file,   name, bb_path_shadow_file,
139   bb_path_passwd_file);   bb_path_passwd_file);
140   } else {   } else {
141   filename = bb_path_shadow_file;   pw->pw_passwd = result->sp_pwdp;
  pw->pw_passwd = sp->sp_pwdp;  
142   }   }
143   }   }
144  #endif  #endif
# Line 311  int passwd_main(int argc, char **argv) Line 163  int passwd_main(int argc, char **argv)
163   newp = xasprintf("!%s", pw->pw_passwd);   newp = xasprintf("!%s", pw->pw_passwd);
164   } else if (opt & OPT_unlock) {   } else if (opt & OPT_unlock) {
165   if (c) goto skip; /* not '!' */   if (c) goto skip; /* not '!' */
166     /* pw->pw_passwd points to static storage,
167     * strdup'ing to avoid nasty surprizes */
168   newp = xstrdup(&pw->pw_passwd[1]);   newp = xstrdup(&pw->pw_passwd[1]);
169   } else if (opt & OPT_delete) {   } else if (opt & OPT_delete) {
170   newp = xstrdup("");   //newp = xstrdup("");
171     newp = (char*)"";
172   }   }
173    
174   rlimit_fsize.rlim_cur = rlimit_fsize.rlim_max = 512L * 30000;   rlimit_fsize.rlim_cur = rlimit_fsize.rlim_max = 512L * 30000;
175   setrlimit(RLIMIT_FSIZE, &rlimit_fsize);   setrlimit(RLIMIT_FSIZE, &rlimit_fsize);
176   signal(SIGHUP, SIG_IGN);   bb_signals(0
177   signal(SIGINT, SIG_IGN);   + (1 << SIGHUP)
178   signal(SIGQUIT, SIG_IGN);   + (1 << SIGINT)
179     + (1 << SIGQUIT)
180     , SIG_IGN);
181   umask(077);   umask(077);
182   xsetuid(0);   xsetuid(0);
183   if (update_passwd(filename, name, newp) != 0) {  
184   /* LOGMODE_BOTH */  #if ENABLE_FEATURE_SHADOWPASSWDS
185   bb_error_msg_and_die("cannot update password file %s",   filename = bb_path_shadow_file;
186   filename);   rc = update_passwd(bb_path_shadow_file, name, newp);
187     if (rc == 0) /* no lines updated, no errors detected */
188    #endif
189     {
190     filename = bb_path_passwd_file;
191     rc = update_passwd(bb_path_passwd_file, name, newp);
192   }   }
193   /* LOGMODE_BOTH */   /* LOGMODE_BOTH */
194     if (rc < 0)
195     bb_error_msg_and_die("cannot update password file %s",
196     filename);
197   bb_info_msg("Password for %s changed by %s", name, myname);   bb_info_msg("Password for %s changed by %s", name, myname);
198    
199   if (ENABLE_FEATURE_CLEAN_UP) free(newp);   //if (ENABLE_FEATURE_CLEAN_UP) free(newp);
200  skip:   skip:
201   if (!newp) {   if (!newp) {
202   bb_error_msg_and_die("password for %s is already %slocked",   bb_error_msg_and_die("password for %s is already %slocked",
203   name, (opt & OPT_unlock) ? "un" : "");   name, (opt & OPT_unlock) ? "un" : "");

Legend:
Removed from v.532  
changed lines
  Added in v.816