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