Coverage Report

Created: 2023-09-19 06:58

/src/mosquitto/apps/mosquitto_passwd/mosquitto_passwd.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
Copyright (c) 2012-2021 Roger Light <roger@atchoo.org>
3
4
All rights reserved. This program and the accompanying materials
5
are made available under the terms of the Eclipse Public License 2.0
6
and Eclipse Distribution License v1.0 which accompany this distribution.
7
8
The Eclipse Public License is available at
9
   https://www.eclipse.org/legal/epl-2.0/
10
and the Eclipse Distribution License is available at
11
  http://www.eclipse.org/org/documents/edl-v10.php.
12
13
SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
14
15
Contributors:
16
   Roger Light - initial implementation and documentation.
17
*/
18
19
#include "config.h"
20
21
#include <ctype.h>
22
#include <errno.h>
23
#include <openssl/evp.h>
24
#include <openssl/rand.h>
25
#include <signal.h>
26
#include <stdio.h>
27
#include <stdlib.h>
28
#include <string.h>
29
30
#include "get_password.h"
31
#include "base64_mosq.h"
32
#include "password_mosq.h"
33
34
#ifdef WIN32
35
#  include <windows.h>
36
#  include <process.h>
37
# ifndef __cplusplus
38
#   if defined(_MSC_VER) && _MSC_VER < 1900
39
#     define bool char
40
#     define true 1
41
#     define false 0
42
#   else
43
#     include <stdbool.h>
44
#   endif
45
# endif
46
#   define snprintf sprintf_s
47
# include <io.h>
48
# include <windows.h>
49
#else
50
#  include <stdbool.h>
51
#  include <unistd.h>
52
#  include <termios.h>
53
#  include <sys/stat.h>
54
#endif
55
56
543
#define MAX_BUFFER_LEN 65500
57
58
#include "misc_mosq.h"
59
60
struct cb_helper {
61
  const char *line;
62
  const char *username;
63
  const char *password;
64
  int iterations;
65
  bool found;
66
};
67
68
static enum mosquitto_pwhash_type hashtype = pw_sha512_pbkdf2;
69
70
#ifdef WIN32
71
static FILE *mpw_tmpfile(void)
72
{
73
  return tmpfile();
74
}
75
#else
76
77
static char unsigned alphanum[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
78
79
static unsigned char tmpfile_path[36];
80
static FILE *mpw_tmpfile(void)
81
221
{
82
221
  int fd;
83
221
  size_t i;
84
85
221
  if(RAND_bytes(tmpfile_path, sizeof(tmpfile_path)) != 1){
86
0
    return NULL;
87
0
  }
88
89
221
  strcpy((char *)tmpfile_path, "/tmp/");
90
91
5.30k
  for(i=strlen((char *)tmpfile_path); i<sizeof(tmpfile_path)-8; i++){
92
5.08k
    tmpfile_path[i] = alphanum[tmpfile_path[i]%(sizeof(alphanum)-1)];
93
5.08k
  }
94
221
  tmpfile_path[sizeof(tmpfile_path)-8] = '-';
95
1.54k
  for(i=sizeof(tmpfile_path)-7; i<sizeof(tmpfile_path)-1; i++){
96
1.32k
    tmpfile_path[i] = 'X';
97
1.32k
  }
98
221
  tmpfile_path[sizeof(tmpfile_path)-1] = '\0';
99
100
221
  umask(077);
101
221
  fd = mkstemp((char *)tmpfile_path);
102
221
  if(fd < 0) return NULL;
103
221
  unlink((char *)tmpfile_path);
104
105
221
  return fdopen(fd, "w+");
106
221
}
107
#endif
108
109
int log__printf(void *mosq, unsigned int level, const char *fmt, ...)
110
3.51M
{
111
  /* Stub for misc_mosq.c */
112
3.51M
  UNUSED(mosq); UNUSED(level); UNUSED(fmt); return 0;
113
3.51M
}
114
115
116
static void print_usage(void)
117
0
{
118
0
  printf("mosquitto_passwd is a tool for managing password files for mosquitto.\n\n");
119
0
  printf("Usage: mosquitto_passwd [-H sha512 | -H sha512-pbkdf2] [-c | -D] passwordfile username\n");
120
0
  printf("       mosquitto_passwd [-H sha512 | -H sha512-pbkdf2] [-c] -b passwordfile username password\n");
121
0
  printf("       mosquitto_passwd -U passwordfile\n");
122
0
  printf(" -b : run in batch mode to allow passing passwords on the command line.\n");
123
0
  printf(" -c : create a new password file. This will overwrite existing files.\n");
124
0
  printf(" -D : delete the username rather than adding/updating its password.\n");
125
0
  printf(" -H : specify the hashing algorithm. Defaults to sha512-pbkdf2, which is recommended.\n");
126
0
  printf("      Mosquitto 1.6 and earlier defaulted to sha512.\n");
127
0
  printf(" -U : update a plain text password file to use hashed passwords.\n");
128
0
  printf("\nSee https://mosquitto.org/ for more information.\n\n");
129
0
}
130
131
static int output_new_password(FILE *fptr, const char *username, const char *password, int iterations)
132
18.7k
{
133
18.7k
  int rc;
134
18.7k
  char *salt64 = NULL, *hash64 = NULL;
135
18.7k
  struct mosquitto_pw pw;
136
137
18.7k
  if(password == NULL){
138
0
    fprintf(stderr, "Error: Internal error, no password given.\n");
139
0
    return 1;
140
0
  }
141
18.7k
  memset(&pw, 0, sizeof(pw));
142
143
18.7k
  pw.hashtype = hashtype;
144
145
18.7k
  rc = pw__hash(password, &pw, true, iterations);
146
18.7k
  if(rc){
147
0
    fprintf(stderr, "Error: Unable to hash password.\n");
148
18.7k
  }else{
149
18.7k
    rc = base64__encode(pw.salt, pw.salt_len, &salt64);
150
18.7k
    if(rc){
151
0
      fprintf(stderr, "Error: Unable to encode salt.\n");
152
18.7k
    }else{
153
18.7k
      rc = base64__encode(pw.password_hash, sizeof(pw.password_hash), &hash64);
154
18.7k
      if(rc){
155
0
        fprintf(stderr, "Error: Unable to encode hash.\n");
156
18.7k
      }else{
157
18.7k
        if(pw.hashtype == pw_sha512_pbkdf2){
158
18.7k
          fprintf(fptr, "%s:$%d$%d$%s$%s\n", username, hashtype, iterations, salt64, hash64);
159
18.7k
        }else{
160
0
          fprintf(fptr, "%s:$%d$%s$%s\n", username, hashtype, salt64, hash64);
161
0
        }
162
18.7k
      }
163
18.7k
    }
164
18.7k
  }
165
18.7k
  free(salt64);
166
18.7k
  free(hash64);
167
168
18.7k
  return rc;
169
18.7k
}
170
171
172
static int pwfile_iterate(FILE *fptr, FILE *ftmp,
173
    int (*cb)(FILE *, FILE *, const char *, const char *, const char *, struct cb_helper *),
174
    struct cb_helper *helper)
175
221
{
176
221
  char *buf;
177
221
  int buflen = 1024;
178
221
  char *lbuf;
179
221
  int lbuflen;
180
221
  int rc = 1;
181
221
  int line = 0;
182
221
  char *username, *password;
183
184
221
  buf = malloc((size_t)buflen);
185
221
  if(buf == NULL){
186
0
    fprintf(stderr, "Error: Out of memory.\n");
187
0
    return 1;
188
0
  }
189
221
  lbuflen = buflen;
190
221
  lbuf = malloc((size_t)lbuflen);
191
221
  if(lbuf == NULL){
192
0
    fprintf(stderr, "Error: Out of memory.\n");
193
0
    free(buf);
194
0
    return 1;
195
0
  }
196
197
19.7k
  while(!feof(fptr) && fgets_extending(&buf, &buflen, fptr)){
198
19.6k
    if(lbuflen != buflen){
199
57
      free(lbuf);
200
57
      lbuflen = buflen;
201
57
      lbuf = malloc((size_t)lbuflen);
202
57
      if(lbuf == NULL){
203
0
        fprintf(stderr, "Error: Out of memory.\n");
204
0
        free(buf);
205
0
        return 1;
206
0
      }
207
57
    }
208
19.6k
    memcpy(lbuf, buf, (size_t)buflen);
209
19.6k
    line++;
210
19.6k
    username = strtok(buf, ":");
211
19.6k
    password = strtok(NULL, ":");
212
19.6k
    if(username == NULL || password == NULL){
213
39
      fprintf(stderr, "Error: Corrupt password file at line %d.\n", line);
214
39
      free(lbuf);
215
39
      free(buf);
216
39
      return 1;
217
39
    }
218
19.5k
    username = misc__trimblanks(username);
219
19.5k
    password = misc__trimblanks(password);
220
221
19.5k
    if(strlen(username) == 0 || strlen(password) == 0){
222
12
      fprintf(stderr, "Error: Corrupt password file at line %d.\n", line);
223
12
      free(lbuf);
224
12
      free(buf);
225
12
      return 1;
226
12
    }
227
228
19.5k
    rc = cb(fptr, ftmp, username, password, lbuf, helper);
229
19.5k
    if(rc){
230
0
      break;
231
0
    }
232
19.5k
  }
233
170
  free(lbuf);
234
170
  free(buf);
235
236
170
  return rc;
237
221
}
238
239
240
/* ======================================================================
241
 * Delete a user from the password file
242
 * ====================================================================== */
243
static int delete_pwuser_cb(FILE *fptr, FILE *ftmp, const char *username, const char *password, const char *line, struct cb_helper *helper)
244
0
{
245
0
  UNUSED(fptr);
246
0
  UNUSED(password);
247
0
  UNUSED(line);
248
249
0
  if(strcmp(username, helper->username)){
250
    /* If this isn't the username to delete, write it to the new file */
251
0
    fprintf(ftmp, "%s", line);
252
0
  }else{
253
    /* Don't write the matching username to the file. */
254
0
    helper->found = true;
255
0
  }
256
0
  return 0;
257
0
}
258
259
static int delete_pwuser(FILE *fptr, FILE *ftmp, const char *username)
260
0
{
261
0
  struct cb_helper helper;
262
0
  int rc;
263
264
0
  memset(&helper, 0, sizeof(helper));
265
0
  helper.username = username;
266
0
  rc = pwfile_iterate(fptr, ftmp, delete_pwuser_cb, &helper);
267
268
0
  if(helper.found == false){
269
0
    fprintf(stderr, "Warning: User %s not found in password file.\n", username);
270
0
    return 1;
271
0
  }
272
0
  return rc;
273
0
}
274
275
276
277
/* ======================================================================
278
 * Update a plain text password file to use hashes
279
 * ====================================================================== */
280
static int update_file_cb(FILE *fptr, FILE *ftmp, const char *username, const char *password, const char *line, struct cb_helper *helper)
281
0
{
282
0
  UNUSED(fptr);
283
0
  UNUSED(line);
284
285
0
  if(helper){
286
0
    return output_new_password(ftmp, username, password, helper->iterations);
287
0
  }else{
288
0
    return output_new_password(ftmp, username, password, PW_DEFAULT_ITERATIONS);
289
0
  }
290
0
}
291
292
static int update_file(FILE *fptr, FILE *ftmp)
293
0
{
294
0
  return pwfile_iterate(fptr, ftmp, update_file_cb, NULL);
295
0
}
296
297
298
/* ======================================================================
299
 * Update an existing user password / create a new password
300
 * ====================================================================== */
301
static int update_pwuser_cb(FILE *fptr, FILE *ftmp, const char *username, const char *password, const char *line, struct cb_helper *helper)
302
19.5k
{
303
19.5k
  int rc = 0;
304
305
19.5k
  UNUSED(fptr);
306
19.5k
  UNUSED(password);
307
308
19.5k
  if(strcmp(username, helper->username)){
309
    /* If this isn't the matching user, then writing out the exiting line */
310
1.04k
    fprintf(ftmp, "%s", line);
311
18.5k
  }else{
312
    /* Write out a new line for our matching username */
313
18.5k
    helper->found = true;
314
18.5k
    rc = output_new_password(ftmp, username, helper->password, helper->iterations);
315
18.5k
  }
316
19.5k
  return rc;
317
19.5k
}
318
319
static int update_pwuser(FILE *fptr, FILE *ftmp, const char *username, const char *password, int iterations)
320
221
{
321
221
  struct cb_helper helper;
322
221
  int rc;
323
324
221
  memset(&helper, 0, sizeof(helper));
325
221
  helper.username = username;
326
221
  helper.password = password;
327
221
  helper.iterations = iterations;
328
221
  rc = pwfile_iterate(fptr, ftmp, update_pwuser_cb, &helper);
329
330
221
  if(helper.found){
331
28
    printf("Updating password for user %s\n", username);
332
28
    return rc;
333
193
  }else{
334
193
    printf("Adding password for user %s\n", username);
335
193
    return output_new_password(ftmp, username, password, iterations);
336
193
  }
337
221
}
338
339
340
static int copy_contents(FILE *src, FILE *dest)
341
441
{
342
441
  char buf[MAX_BUFFER_LEN];
343
441
  size_t len;
344
345
441
  rewind(src);
346
441
  rewind(dest);
347
348
#ifdef WIN32
349
  _chsize(fileno(dest), 0);
350
#else
351
441
  if(ftruncate(fileno(dest), 0)) return 1;
352
441
#endif
353
354
981
  while(!feof(src)){
355
543
    len = fread(buf, 1, MAX_BUFFER_LEN, src);
356
543
    if(len > 0){
357
540
      if(fwrite(buf, 1, len, dest) != len){
358
0
        return 1;
359
0
      }
360
540
    }else{
361
3
      return !feof(src);
362
3
    }
363
543
  }
364
438
  return 0;
365
441
}
366
367
static int create_backup(char *backup_file, FILE *fptr)
368
221
{
369
221
  FILE *fbackup;
370
371
#ifdef WIN32
372
  fbackup = mosquitto__fopen(backup_file, "wt", true);
373
#else
374
221
  int fd;
375
221
  umask(077);
376
221
  fd = mkstemp(backup_file);
377
221
  if(fd < 0){
378
0
    fprintf(stderr, "Error creating backup password file \"%s\", not continuing.\n", backup_file);
379
0
    return 1;
380
0
  }
381
221
  fbackup = fdopen(fd, "wt");
382
221
#endif
383
221
  if(!fbackup){
384
0
    fprintf(stderr, "Error creating backup password file \"%s\", not continuing.\n", backup_file);
385
0
    return 1;
386
0
  }
387
388
221
  if(copy_contents(fptr, fbackup)){
389
0
    fprintf(stderr, "Error copying data to backup password file \"%s\", not continuing.\n", backup_file);
390
0
    fclose(fbackup);
391
0
    return 1;
392
0
  }
393
221
  fclose(fbackup);
394
221
  rewind(fptr);
395
221
  return 0;
396
221
}
397
398
static void handle_sigint(int signal)
399
0
{
400
0
  get_password__reset_term();
401
402
0
  UNUSED(signal);
403
404
0
  exit(0);
405
0
}
406
407
408
static bool is_username_valid(const char *username)
409
221
{
410
221
  size_t i;
411
221
  size_t slen;
412
413
221
  if(username){
414
221
    slen = strlen(username);
415
221
    if(slen > 65535){
416
0
      fprintf(stderr, "Error: Username must be less than 65536 characters long.\n");
417
0
      return false;
418
0
    }
419
1.98k
    for(i=0; i<slen; i++){
420
1.76k
      if(iscntrl(username[i])){
421
0
        fprintf(stderr, "Error: Username must not contain control characters.\n");
422
0
        return false;
423
0
      }
424
1.76k
    }
425
221
    if(strchr(username, ':')){
426
0
      fprintf(stderr, "Error: Username must not contain the ':' character.\n");
427
0
      return false;
428
0
    }
429
221
  }
430
221
  return true;
431
221
}
432
433
#ifdef WITH_FUZZING
434
int mosquitto_passwd_fuzz_main(int argc, char *argv[])
435
#else
436
int main(int argc, char *argv[])
437
#endif
438
221
{
439
221
  char *password_file_tmp = NULL;
440
221
  char *password_file = NULL;
441
221
  char *username = NULL;
442
221
  char *password_cmd = NULL;
443
221
  bool batch_mode = false;
444
221
  bool create_new = false;
445
221
  bool delete_user = false;
446
221
  FILE *fptr, *ftmp;
447
221
  char password[MAX_BUFFER_LEN];
448
221
  int rc;
449
221
  bool do_update_file = false;
450
221
  char *backup_file;
451
221
  int idx;
452
221
  int iterations = PW_DEFAULT_ITERATIONS;
453
454
221
  signal(SIGINT, handle_sigint);
455
221
  signal(SIGTERM, handle_sigint);
456
457
221
#if OPENSSL_VERSION_NUMBER < 0x10100000L || OPENSSL_API_COMPAT < 0x10100000L
458
221
  OpenSSL_add_all_digests();
459
#else
460
  OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS \
461
      | OPENSSL_INIT_ADD_ALL_DIGESTS \
462
      | OPENSSL_INIT_LOAD_CONFIG, NULL);
463
#endif
464
465
221
  if(argc == 1){
466
0
    print_usage();
467
0
    return 1;
468
0
  }
469
470
221
  idx = 1;
471
442
  for(idx = 1; idx < argc; idx++){
472
442
    if(!strcmp(argv[idx], "-H")){
473
0
      if(idx+1 == argc){
474
0
        fprintf(stderr, "Error: -H argument given but not enough other arguments.\n");
475
0
        return 1;
476
0
      }
477
0
      if(!strcmp(argv[idx+1], "sha512")){
478
0
        hashtype = pw_sha512;
479
0
      }else if(!strcmp(argv[idx+1], "sha512-pbkdf2")){
480
0
        hashtype = pw_sha512_pbkdf2;
481
0
      }else{
482
0
        fprintf(stderr, "Error: Unknown hash type '%s'\n", argv[idx+1]);
483
0
        return 1;
484
0
      }
485
0
      idx++;
486
442
    }else if(!strcmp(argv[idx], "-b")){
487
221
      batch_mode = true;
488
221
    }else if(!strcmp(argv[idx], "-c")){
489
0
      create_new = true;
490
221
    }else if(!strcmp(argv[idx], "-D")){
491
0
      delete_user = true;
492
221
    }else if(!strcmp(argv[idx], "-I")){
493
0
      if(idx+1 == argc){
494
0
        fprintf(stderr, "Error: -I argument given but not enough other arguments.\n");
495
0
        return 1;
496
0
      }
497
0
      iterations = atoi(argv[idx+1]);
498
0
      idx++;
499
0
      if(iterations < 1){
500
0
        fprintf(stderr, "Error: Number of iterations must be > 0.\n");
501
0
        return 1;
502
0
      }
503
221
    }else if(!strcmp(argv[idx], "-U")){
504
0
      do_update_file = true;
505
221
    }else{
506
221
      break;
507
221
    }
508
442
  }
509
510
221
  if(create_new && delete_user){
511
0
    fprintf(stderr, "Error: -c and -D cannot be used together.\n");
512
0
    return 1;
513
0
  }
514
221
  if(create_new && do_update_file){
515
0
    fprintf(stderr, "Error: -c and -U cannot be used together.\n");
516
0
    return 1;
517
0
  }
518
221
  if(delete_user && do_update_file){
519
0
    fprintf(stderr, "Error: -D and -U cannot be used together.\n");
520
0
    return 1;
521
0
  }
522
221
  if(delete_user && batch_mode){
523
0
    fprintf(stderr, "Error: -b and -D cannot be used together.\n");
524
0
    return 1;
525
0
  }
526
527
221
  if(create_new){
528
0
    if(batch_mode){
529
0
      if(idx+2 >= argc){
530
0
        fprintf(stderr, "Error: -c argument given but password file, username, or password missing.\n");
531
0
        return 1;
532
0
      }else{
533
0
        password_file_tmp = argv[idx];
534
0
        username = argv[idx+1];
535
0
        password_cmd = argv[idx+2];
536
0
      }
537
0
    }else{
538
0
      if(idx+1 >= argc){
539
0
        fprintf(stderr, "Error: -c argument given but password file or username missing.\n");
540
0
        return 1;
541
0
      }else{
542
0
        password_file_tmp = argv[idx];
543
0
        username = argv[idx+1];
544
0
      }
545
0
    }
546
221
  }else if(delete_user){
547
0
    if(idx+1 >= argc){
548
0
      fprintf(stderr, "Error: -D argument given but password file or username missing.\n");
549
0
      return 1;
550
0
    }else{
551
0
      password_file_tmp = argv[idx];
552
0
      username = argv[idx+1];
553
0
    }
554
221
  }else if(do_update_file){
555
0
    if(idx+1 != argc){
556
0
      fprintf(stderr, "Error: -U argument given but password file missing.\n");
557
0
      return 1;
558
0
    }else{
559
0
      password_file_tmp = argv[idx];
560
0
    }
561
221
  }else if(batch_mode == true && idx+3 == argc){
562
221
    password_file_tmp = argv[idx];
563
221
    username = argv[idx+1];
564
221
    password_cmd = argv[idx+2];
565
221
  }else if(batch_mode == false && idx+2 == argc){
566
0
    password_file_tmp = argv[idx];
567
0
    username = argv[idx+1];
568
0
  }else{
569
0
    print_usage();
570
0
    return 1;
571
0
  }
572
573
221
  if(!is_username_valid(username)){
574
0
    return 1;
575
0
  }
576
221
  if(password_cmd && strlen(password_cmd) > 65535){
577
0
    fprintf(stderr, "Error: Password must be less than 65536 characters long.\n");
578
0
    return 1;
579
0
  }
580
581
#ifdef WIN32
582
  password_file = _fullpath(NULL, password_file_tmp, 0);
583
  if(!password_file){
584
    fprintf(stderr, "Error getting full path for password file.\n");
585
    return 1;
586
  }
587
#else
588
221
  password_file = realpath(password_file_tmp, NULL);
589
221
  if(!password_file){
590
0
    if(errno == ENOENT){
591
0
      password_file = strdup(password_file_tmp);
592
0
      if(!password_file){
593
0
        fprintf(stderr, "Error: Out of memory.\n");
594
0
        return 1;
595
0
      }
596
0
    }else{
597
0
      fprintf(stderr, "Error reading password file: %s\n", strerror(errno));
598
0
      return 1;
599
0
    }
600
0
  }
601
221
#endif
602
603
221
  if(create_new){
604
0
    if(batch_mode == false){
605
0
      rc = get_password("Password: ", "Reenter password: ", false, password, MAX_BUFFER_LEN);
606
0
      if(rc){
607
0
        free(password_file);
608
0
        return rc;
609
0
      }
610
0
      password_cmd = password;
611
0
    }
612
0
    fptr = mosquitto__fopen(password_file, "wt", true);
613
0
    if(!fptr){
614
0
      fprintf(stderr, "Error: Unable to open file %s for writing. %s.\n", password_file, strerror(errno));
615
0
      free(password_file);
616
0
      return 1;
617
0
    }
618
0
    free(password_file);
619
0
    printf("Adding password for user %s\n", username);
620
0
    rc = output_new_password(fptr, username, password_cmd, iterations);
621
0
    fclose(fptr);
622
0
    return rc;
623
221
  }else{
624
221
    fptr = mosquitto__fopen(password_file, "r+t", true);
625
221
    if(!fptr){
626
0
      fprintf(stderr, "Error: Unable to open password file %s. %s.\n", password_file, strerror(errno));
627
0
      free(password_file);
628
0
      return 1;
629
0
    }
630
631
221
    size_t len = strlen(password_file) + strlen(".backup.XXXXXX") + 1;
632
221
    backup_file = malloc(len);
633
221
    if(!backup_file){
634
0
      fprintf(stderr, "Error: Out of memory.\n");
635
0
      free(password_file);
636
0
      return 1;
637
0
    }
638
221
    snprintf(backup_file, len, "%s.backup.XXXXXX", password_file);
639
221
    free(password_file);
640
221
    password_file = NULL;
641
642
221
    if(create_backup(backup_file, fptr)){
643
0
      fclose(fptr);
644
0
      free(backup_file);
645
0
      return 1;
646
0
    }
647
648
221
    ftmp = mpw_tmpfile();
649
221
    if(!ftmp){
650
0
      fprintf(stderr, "Error: Unable to open temporary file. %s.\n", strerror(errno));
651
0
      fclose(fptr);
652
0
      free(backup_file);
653
0
      return 1;
654
0
    }
655
221
    if(delete_user){
656
0
      rc = delete_pwuser(fptr, ftmp, username);
657
221
    }else if(do_update_file){
658
0
      rc = update_file(fptr, ftmp);
659
221
    }else{
660
221
      if(batch_mode){
661
        /* Update password for individual user */
662
221
        rc = update_pwuser(fptr, ftmp, username, password_cmd, iterations);
663
221
      }else{
664
0
        rc = get_password("Password: ", "Reenter password: ", false, password, MAX_BUFFER_LEN);
665
0
        if(rc){
666
0
          fclose(fptr);
667
0
          fclose(ftmp);
668
0
          unlink(backup_file);
669
0
          free(backup_file);
670
0
          return rc;
671
0
        }
672
        /* Update password for individual user */
673
0
        rc = update_pwuser(fptr, ftmp, username, password, iterations);
674
0
      }
675
221
    }
676
221
    if(rc){
677
1
      fclose(fptr);
678
1
      fclose(ftmp);
679
1
      unlink(backup_file);
680
1
      free(backup_file);
681
1
      return rc;
682
1
    }
683
684
220
    if(copy_contents(ftmp, fptr)){
685
0
      fclose(fptr);
686
0
      fclose(ftmp);
687
0
      fprintf(stderr, "Error occurred updating password file.\n");
688
0
      fprintf(stderr, "Password file may be corrupt, check the backup file: %s.\n", backup_file);
689
0
      free(backup_file);
690
0
      return 1;
691
0
    }
692
220
    fclose(fptr);
693
220
    fclose(ftmp);
694
695
    /* Everything was ok so backup no longer needed. May contain old
696
     * passwords so shouldn't be kept around. */
697
220
    unlink(backup_file);
698
220
    free(backup_file);
699
220
  }
700
701
220
  return 0;
702
221
}