master xplshn/aruu / cmd / xsi / passwd.c
  1/* see LICENSE file for copyright and license details */
  2#include "passwd.h"
  3#include "util.h"
  4
  5#include <crypt.h>
  6#include <pwd.h>
  7#include <stdio.h>
  8#include <stdlib.h>
  9#include <string.h>
 10#include <termios.h>
 11#include <unistd.h>
 12
 13static void
 14usage(void)
 15{
 16  eprintf("usage: %s [-l | -u | -d] [name]\n", argv0);
 17}
 18
 19/* reads one line with echo off into a reusable static buffer */
 20static char *
 21readpass(const char *prompt)
 22{
 23  static char    buf[128];
 24  struct termios old, raw;
 25  int            have_old;
 26  size_t         n;
 27
 28  fputs(prompt, stdout);
 29  fflush(stdout);
 30
 31  have_old = tcgetattr(STDIN_FILENO, &old) == 0;
 32  if (have_old) {
 33    raw = old;
 34    raw.c_lflag &= ~ECHO;
 35    tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw);
 36  }
 37
 38  if (!fgets(buf, sizeof(buf), stdin)) {
 39    if (have_old)
 40      tcsetattr(STDIN_FILENO, TCSAFLUSH, &old);
 41    return NULL;
 42  }
 43
 44  if (have_old) {
 45    tcsetattr(STDIN_FILENO, TCSAFLUSH, &old);
 46    fputc('\n', stdout);
 47  }
 48
 49  n = strlen(buf);
 50  if (n && buf[n - 1] == '\n')
 51    buf[--n] = '\0';
 52
 53  return buf;
 54}
 55
 56// ?man passwd: change a user's password
 57// ?man arguments: [-l | -u | -d] [name]
 58// ?man change name's password (default: the caller's own), or with -l/-u/-d
 59// ?man lock, unlock, or clear it instead. only root may target another
 60// ?man user, or use -l/-u/-d at all
 61int
 62main(int argc, char *argv[])
 63{
 64  struct pwdb_entry  ent;
 65  struct passwd     *self, *target;
 66  char              *name, *cur, *new1, *new2, *hash;
 67  char               salt[PW_SALT_MAX];
 68  int                lflag = 0, uflag = 0, dflag = 0;
 69  uid_t              uid;
 70
 71  ARGBEGIN
 72  {
 73    // ?man -l: lock the account by prefixing its hash with '!'
 74    case 'l':
 75      lflag = 1;
 76      break;
 77    // ?man -u: unlock an account previously locked with -l
 78    case 'u':
 79      uflag = 1;
 80      break;
 81    // ?man -d: clear the password, allowing a blank password login
 82    case 'd':
 83      dflag = 1;
 84      break;
 85    default:
 86      usage();
 87  }
 88  ARGEND
 89
 90  if (lflag + uflag + dflag > 1)
 91    usage();
 92
 93  uid  = getuid();
 94  self = getpwuid(uid);
 95  if (!self)
 96    eprintf("passwd: who are you?\n");
 97
 98  name = argc > 0 ? argv[0] : self->pw_name;
 99  if (uid != 0 && (strcmp(name, self->pw_name) != 0 || lflag || uflag || dflag))
100    eprintf("passwd: permission denied\n");
101
102  target = getpwnam(name);
103  if (!target)
104    eprintf("passwd: unknown user: %s\n", name);
105
106  pw_init();
107
108  if (lflag || uflag || dflag) {
109    if (pwdb_lookup(&ent, name) < 0)
110      eprintf("passwd: cannot look up %s\n", name);
111
112    if (lflag) {
113      if (ent.hash[0] == '!') {
114        hash = ent.hash;
115      } else {
116        hash    = emalloc(strlen(ent.hash) + 2);
117        hash[0] = '!';
118        strcpy(hash + 1, ent.hash);
119      }
120      if (pwdb_update(name, hash) < 0)
121        eprintf("passwd: update failed\n");
122      printf("passwd: password for %s locked\n", name);
123    } else if (uflag) {
124      hash = ent.hash[0] == '!' ? ent.hash + 1 : ent.hash;
125      if (pwdb_update(name, hash) < 0)
126        eprintf("passwd: update failed\n");
127      printf("passwd: password for %s unlocked\n", name);
128    } else {
129      if (pwdb_update(name, "") < 0)
130        eprintf("passwd: update failed\n");
131      printf("passwd: password for %s cleared\n", name);
132    }
133    return 0;
134  }
135
136  if (uid != 0) {
137    cur = readpass("Current password: ");
138    if (!cur || pw_check(self, cur) != 1)
139      eprintf("passwd: authentication failed\n");
140  }
141
142  new1 = readpass("New password: ");
143  if (!new1)
144    exit(1);
145  new1 = estrdup(new1);
146
147  new2 = readpass("Retype new password: ");
148  if (!new2 || strcmp(new1, new2) != 0) {
149    free(new1);
150    eprintf("passwd: passwords do not match\n");
151  }
152  if (new1[0] == '\0')
153    weprintf("passwd: warning: empty password\n");
154
155  if (pw_gensalt(salt, sizeof(salt)) < 0)
156    eprintf("passwd: cannot generate salt\n");
157
158  hash = crypt(new1, salt);
159  free(new1);
160  if (!hash)
161    eprintf("passwd: crypt:");
162
163  if (pwdb_update(name, hash) < 0)
164    eprintf("passwd: update failed\n");
165
166  printf("passwd: password for %s updated\n", name);
167  return 0;
168}