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 }