Coverage Report

Created: 2025-10-24 06:29

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