URI: 
       passwd.c - ubase - suckless linux base utils
  HTML git clone git://git.suckless.org/ubase
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       passwd.c (5780B)
       ---
            1 /* See LICENSE file for copyright and license details. */
            2 #include <sys/ioctl.h>
            3 #include <sys/stat.h>
            4 #include <sys/types.h>
            5 #include <sys/syscall.h>
            6 
            7 #include <errno.h>
            8 #include <fcntl.h>
            9 #include <limits.h>
           10 #include <pwd.h>
           11 #include <shadow.h>
           12 #include <stdint.h>
           13 #include <stdio.h>
           14 #include <stdlib.h>
           15 #include <string.h>
           16 #include <unistd.h>
           17 
           18 #include "config.h"
           19 #include "passwd.h"
           20 #include "text.h"
           21 #include "util.h"
           22 
           23 static FILE *
           24 spw_get_file(const char *user)
           25 {
           26         FILE *fp = NULL;
           27         char file[PATH_MAX];
           28         int r;
           29 
           30         r = snprintf(file, sizeof(file), "/etc/tcb/%s/shadow", user);
           31         if (r < 0 || (size_t)r >= sizeof(file))
           32                 eprintf("snprintf:");
           33         fp = fopen(file, "r+");
           34         if (!fp)
           35                 fp = fopen("/etc/shadow", "r+");
           36         return fp;
           37 }
           38 
           39 static int
           40 spw_write_file(FILE *fp, const struct spwd *spw, char *pwhash)
           41 {
           42         struct spwd *spwent;
           43         int r = -1, w = 0;
           44         FILE *tfp = NULL;
           45 
           46         /* write to temporary file. */
           47         tfp = tmpfile();
           48         if (!tfp) {
           49                 weprintf("tmpfile:");
           50                 goto cleanup;
           51         }
           52         while ((spwent = fgetspent(fp))) {
           53                 /* update entry on name match */
           54                 if (strcmp(spwent->sp_namp, spw->sp_namp) == 0) {
           55                         spwent->sp_pwdp = pwhash;
           56                         w++;
           57                 }
           58                 errno = 0;
           59                 if (putspent(spwent, tfp) == -1) {
           60                         weprintf("putspent:");
           61                         goto cleanup;
           62                 }
           63         }
           64         if (!w) {
           65                 weprintf("shadow: no matching entry to write to\n");
           66                 goto cleanup;
           67         }
           68         fflush(tfp);
           69 
           70         if (fseek(fp, 0, SEEK_SET) == -1 || fseek(tfp, 0, SEEK_SET) == -1) {
           71                 weprintf("fseek:");
           72                 goto cleanup;
           73         }
           74 
           75         /* write temporary file to (tcb) shadow file */
           76         concat(tfp, "tmpfile", fp, "shadow");
           77         ftruncate(fileno(fp), ftell(tfp));
           78 
           79         r = 0; /* success */
           80 cleanup:
           81         if (tfp)
           82                 fclose(tfp);
           83         return r;
           84 }
           85 
           86 static int
           87 pw_write_file(FILE *fp, const struct passwd *pw, char *pwhash) {
           88         struct passwd *pwent;
           89         int r = -1, w = 0;
           90         FILE *tfp = NULL;
           91 
           92         /* write to temporary file. */
           93         tfp = tmpfile();
           94         if (!tfp) {
           95                 weprintf("tmpfile:");
           96                 goto cleanup;
           97         }
           98         while ((pwent = fgetpwent(fp))) {
           99                 /* update entry on name match */
          100                 if (strcmp(pwent->pw_name, pw->pw_name) == 0) {
          101                         pwent->pw_passwd = pwhash;
          102                         w++;
          103                 }
          104                 errno = 0;
          105                 if (putpwent(pwent, tfp) == -1) {
          106                         weprintf("putpwent:");
          107                         goto cleanup;
          108                 }
          109         }
          110         if (!w) {
          111                 weprintf("passwd: no matching entry to write to\n");
          112                 goto cleanup;
          113         }
          114         fflush(tfp);
          115 
          116         if (fseek(fp, 0, SEEK_SET) == -1 || fseek(tfp, 0, SEEK_SET) == -1) {
          117                 weprintf("fseek:");
          118                 goto cleanup;
          119         }
          120 
          121         /* write to passwd file. */
          122         concat(tfp, "tmpfile", fp, "passwd");
          123         ftruncate(fileno(fp), ftell(tfp));
          124 
          125         r = 0; /* success */
          126 cleanup:
          127         if (tfp)
          128                 fclose(tfp);
          129         return r;
          130 }
          131 
          132 /* generates a random base64-encoded salt string of length 16 */
          133 static void
          134 gensalt(char *s)
          135 {
          136         static const char b64[] = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
          137         uint8_t buf[12];
          138         uint32_t n;
          139         int i;
          140 
          141         if (syscall(SYS_getrandom, buf, sizeof(buf), 0) < 0)
          142                 eprintf("getrandom:");
          143         for (i = 0; i < 12; i += 3) {
          144                 n = buf[i] << 16 | buf[i+1] << 8 | buf[i+2];
          145                 *s++ = b64[n%64]; n /= 64;
          146                 *s++ = b64[n%64]; n /= 64;
          147                 *s++ = b64[n%64]; n /= 64;
          148                 *s++ = b64[n];
          149         }
          150         *s++ = '\0';
          151 }
          152 
          153 static void
          154 usage(void)
          155 {
          156         eprintf("usage: %s [username]\n", argv0);
          157 }
          158 
          159 int
          160 main(int argc, char *argv[])
          161 {
          162         char *cryptpass1 = NULL, *cryptpass2 = NULL, *cryptpass3 = NULL;
          163         char *inpass, *p, *prevhash = NULL, salt[sizeof(PW_CIPHER) + 16] = PW_CIPHER;
          164         struct passwd *pw;
          165         struct spwd *spw = NULL;
          166         FILE *fp = NULL;
          167         int r = -1, status = 1;
          168 
          169         ARGBEGIN {
          170         default:
          171                 usage();
          172         } ARGEND;
          173 
          174         pw_init();
          175         umask(077);
          176 
          177         errno = 0;
          178         if (argc == 0)
          179                 pw = getpwuid(getuid());
          180         else
          181                 pw = getpwnam(argv[0]);
          182         if (!pw) {
          183                 if (errno)
          184                         eprintf("getpwnam: %s:", argv[0]);
          185                 else
          186                         eprintf("who are you?\n");
          187         }
          188 
          189         /* is using shadow entry ? */
          190         if (pw->pw_passwd[0] == 'x' && pw->pw_passwd[1] == '\0') {
          191                 errno = 0;
          192                 spw = getspnam(pw->pw_name);
          193                 if (!spw) {
          194                         if (errno)
          195                                 eprintf("getspnam: %s:", pw->pw_name);
          196                         else
          197                                 eprintf("who are you?\n");
          198                 }
          199         }
          200 
          201         /* Flush pending input */
          202         ioctl(0, TCFLSH, (void *)0);
          203 
          204         if (getuid() == 0) {
          205                 goto newpass;
          206         } else {
          207                 if (pw->pw_passwd[0] == '!' ||
          208                     pw->pw_passwd[0] == '*')
          209                         eprintf("denied\n");
          210                 if (pw->pw_passwd[0] == '\0') {
          211                         goto newpass;
          212                 }
          213                 if (pw->pw_passwd[0] == 'x' &&
          214                     pw->pw_passwd[1] == '\0')
          215                         prevhash = spw->sp_pwdp;
          216                 else
          217                         prevhash = pw->pw_passwd;
          218         }
          219 
          220         printf("Changing password for %s\n", pw->pw_name);
          221         inpass = getpass("Old password: ");
          222         if (!inpass)
          223                 eprintf("getpass:");
          224         if (inpass[0] == '\0')
          225                 eprintf("no password supplied\n");
          226         p = crypt(inpass, prevhash);
          227         if (!p)
          228                 eprintf("crypt:");
          229         cryptpass1 = estrdup(p);
          230         if (strcmp(cryptpass1, prevhash) != 0)
          231                 eprintf("incorrect password\n");
          232 
          233 newpass:
          234         inpass = getpass("Enter new password: ");
          235         if (!inpass)
          236                 eprintf("getpass:");
          237         if (inpass[0] == '\0')
          238                 eprintf("no password supplied\n");
          239 
          240         if(prevhash) {
          241                 p = crypt(inpass, prevhash);
          242                 if (!p)
          243                         eprintf("crypt:");
          244                 if (cryptpass1 && strcmp(cryptpass1, p) == 0)
          245                         eprintf("password left unchanged\n");
          246         }
          247         gensalt(salt + strlen(salt));
          248         p = crypt(inpass, salt);
          249         if (!p)
          250                 eprintf("crypt:");
          251         cryptpass2 = estrdup(p);
          252 
          253         /* Flush pending input */
          254         ioctl(0, TCFLSH, (void *)0);
          255 
          256         inpass = getpass("Retype new password: ");
          257         if (!inpass)
          258                 eprintf("getpass:");
          259         if (inpass[0] == '\0')
          260                 eprintf("no password supplied\n");
          261         p = crypt(inpass, salt);
          262         if (!p)
          263                 eprintf("crypt:");
          264         cryptpass3 = estrdup(p);
          265         if (strcmp(cryptpass2, cryptpass3) != 0)
          266                 eprintf("passwords don't match\n");
          267 
          268         fp = spw_get_file(pw->pw_name);
          269         if (fp) {
          270                 r = spw_write_file(fp, spw, cryptpass3);
          271         } else {
          272                 fp = fopen("/etc/passwd", "r+");
          273                 if (fp)
          274                         r = pw_write_file(fp, pw, cryptpass3);
          275                 else
          276                         weprintf("fopen:");
          277         }
          278         if (!r)
          279                 status = 0;
          280 
          281         if (fp)
          282                 fclose(fp);
          283         free(cryptpass3);
          284         free(cryptpass2);
          285         free(cryptpass1);
          286 
          287         return status;
          288 }