/mandos/trunk

To get this branch, use:
bzr branch http://bzr.recompile.se/loggerhead/mandos/trunk

« back to all changes in this revision

Viewing changes to plugins.d/mandos-client.c

  • Committer: Teddy Hogeborn
  • Date: 2016-03-17 20:40:55 UTC
  • Revision ID: teddy@recompile.se-20160317204055-bhsh5xsidq7w5cxu
Client: Fix plymouth agent; broken since 1.7.2.

Fix an very old memory bug in the plymouth agent (which has been
present since its apperance in version 1.2), but which was only
recently detected at run time due to the new -fsanitize=address
compile- time flag, which has been used since version 1.7.2.  This
detection of a memory access violation causes the program to abort,
making the Plymouth graphical boot system unable to accept interactive
input of passwords when using the Mandos client.

* plugins.d/plymouth.c (exec_and_wait): Fix memory allocation bug when
  allocating new_argv.  Also tolerate a zero-length argv.

Show diffs side-by-side

added added

removed removed

Lines of Context:
9
9
 * "browse_callback", and parts of "main".
10
10
 * 
11
11
 * Everything else is
12
 
 * Copyright © 2008-2018 Teddy Hogeborn
13
 
 * Copyright © 2008-2018 Björn Påhlsson
14
 
 * 
15
 
 * This file is part of Mandos.
16
 
 * 
17
 
 * Mandos is free software: you can redistribute it and/or modify it
18
 
 * under the terms of the GNU General Public License as published by
19
 
 * the Free Software Foundation, either version 3 of the License, or
20
 
 * (at your option) any later version.
21
 
 * 
22
 
 * Mandos is distributed in the hope that it will be useful, but
 
12
 * Copyright © 2008-2016 Teddy Hogeborn
 
13
 * Copyright © 2008-2016 Björn Påhlsson
 
14
 * 
 
15
 * This program is free software: you can redistribute it and/or
 
16
 * modify it under the terms of the GNU General Public License as
 
17
 * published by the Free Software Foundation, either version 3 of the
 
18
 * License, or (at your option) any later version.
 
19
 * 
 
20
 * This program is distributed in the hope that it will be useful, but
23
21
 * WITHOUT ANY WARRANTY; without even the implied warranty of
24
22
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
25
23
 * General Public License for more details.
26
24
 * 
27
25
 * You should have received a copy of the GNU General Public License
28
 
 * along with Mandos.  If not, see <http://www.gnu.org/licenses/>.
 
26
 * along with this program.  If not, see
 
27
 * <http://www.gnu.org/licenses/>.
29
28
 * 
30
29
 * Contact the authors at <mandos@recompile.se>.
31
30
 */
48
47
                                   strtof(), abort() */
49
48
#include <stdbool.h>            /* bool, false, true */
50
49
#include <string.h>             /* strcmp(), strlen(), strerror(),
51
 
                                   asprintf(), strncpy(), strsignal()
52
 
                                */
 
50
                                   asprintf(), strncpy() */
53
51
#include <sys/ioctl.h>          /* ioctl */
54
52
#include <sys/types.h>          /* socket(), inet_pton(), sockaddr,
55
53
                                   sockaddr_in6, PF_INET6,
272
270
  return true;
273
271
}
274
272
 
275
 
/* Set effective uid to 0, return errno */
276
 
__attribute__((warn_unused_result))
277
 
int raise_privileges(void){
278
 
  int old_errno = errno;
279
 
  int ret = 0;
280
 
  if(seteuid(0) == -1){
281
 
    ret = errno;
282
 
  }
283
 
  errno = old_errno;
284
 
  return ret;
285
 
}
286
 
 
287
 
/* Set effective and real user ID to 0.  Return errno. */
288
 
__attribute__((warn_unused_result))
289
 
int raise_privileges_permanently(void){
290
 
  int old_errno = errno;
291
 
  int ret = raise_privileges();
292
 
  if(ret != 0){
293
 
    errno = old_errno;
294
 
    return ret;
295
 
  }
296
 
  if(setuid(0) == -1){
297
 
    ret = errno;
298
 
  }
299
 
  errno = old_errno;
300
 
  return ret;
301
 
}
302
 
 
303
 
/* Set effective user ID to unprivileged saved user ID */
304
 
__attribute__((warn_unused_result))
305
 
int lower_privileges(void){
306
 
  int old_errno = errno;
307
 
  int ret = 0;
308
 
  if(seteuid(uid) == -1){
309
 
    ret = errno;
310
 
  }
311
 
  errno = old_errno;
312
 
  return ret;
313
 
}
314
 
 
315
 
/* Lower privileges permanently */
316
 
__attribute__((warn_unused_result))
317
 
int lower_privileges_permanently(void){
318
 
  int old_errno = errno;
319
 
  int ret = 0;
320
 
  if(setuid(uid) == -1){
321
 
    ret = errno;
322
 
  }
323
 
  errno = old_errno;
324
 
  return ret;
325
 
}
326
 
 
327
273
/* 
328
274
 * Initialize GPGME.
329
275
 */
349
295
      return false;
350
296
    }
351
297
    
352
 
    /* Workaround for systems without a real-time clock; see also
353
 
       Debian bug #894495: <https://bugs.debian.org/894495> */
354
 
    do {
355
 
      {
356
 
        time_t currtime = time(NULL);
357
 
        if(currtime != (time_t)-1){
358
 
          struct tm tm;
359
 
          if(gmtime_r(&currtime, &tm) == NULL) {
360
 
            perror_plus("gmtime_r");
361
 
            break;
362
 
          }
363
 
          if(tm.tm_year != 70 or tm.tm_mon != 0){
364
 
            break;
365
 
          }
366
 
          if(debug){
367
 
            fprintf_plus(stderr, "System clock is January 1970");
368
 
          }
369
 
        } else {
370
 
          if(debug){
371
 
            fprintf_plus(stderr, "System clock is invalid");
372
 
          }
373
 
        }
374
 
      }
375
 
      struct stat keystat;
376
 
      ret = fstat(fd, &keystat);
377
 
      if(ret != 0){
378
 
        perror_plus("fstat");
379
 
        break;
380
 
      }
381
 
      ret = raise_privileges();
382
 
      if(ret != 0){
383
 
        errno = ret;
384
 
        perror_plus("Failed to raise privileges");
385
 
        break;
386
 
      }
387
 
      if(debug){
388
 
        fprintf_plus(stderr,
389
 
                     "Setting system clock to key file mtime");
390
 
      }
391
 
      time_t keytime = keystat.st_mtim.tv_sec;
392
 
      if(stime(&keytime) != 0){
393
 
        perror_plus("stime");
394
 
      }
395
 
      ret = lower_privileges();
396
 
      if(ret != 0){
397
 
        errno = ret;
398
 
        perror_plus("Failed to lower privileges");
399
 
      }
400
 
    } while(false);
401
 
 
402
298
    rc = gpgme_data_new_from_fd(&pgp_data, fd);
403
299
    if(rc != GPG_ERR_NO_ERROR){
404
300
      fprintf_plus(stderr, "bad gpgme_data_new_from_fd: %s: %s\n",
412
308
                   gpgme_strsource(rc), gpgme_strerror(rc));
413
309
      return false;
414
310
    }
415
 
    {
416
 
      gpgme_import_result_t import_result
417
 
        = gpgme_op_import_result(mc->ctx);
418
 
      if((import_result->imported < 1
419
 
          or import_result->not_imported > 0)
420
 
         and import_result->unchanged == 0){
421
 
        fprintf_plus(stderr, "bad gpgme_op_import_results:\n");
422
 
        fprintf_plus(stderr,
423
 
                     "The total number of considered keys: %d\n",
424
 
                     import_result->considered);
425
 
        fprintf_plus(stderr,
426
 
                     "The number of keys without user ID: %d\n",
427
 
                     import_result->no_user_id);
428
 
        fprintf_plus(stderr,
429
 
                     "The total number of imported keys: %d\n",
430
 
                     import_result->imported);
431
 
        fprintf_plus(stderr, "The number of imported RSA keys: %d\n",
432
 
                     import_result->imported_rsa);
433
 
        fprintf_plus(stderr, "The number of unchanged keys: %d\n",
434
 
                     import_result->unchanged);
435
 
        fprintf_plus(stderr, "The number of new user IDs: %d\n",
436
 
                     import_result->new_user_ids);
437
 
        fprintf_plus(stderr, "The number of new sub keys: %d\n",
438
 
                     import_result->new_sub_keys);
439
 
        fprintf_plus(stderr, "The number of new signatures: %d\n",
440
 
                     import_result->new_signatures);
441
 
        fprintf_plus(stderr, "The number of new revocations: %d\n",
442
 
                     import_result->new_revocations);
443
 
        fprintf_plus(stderr,
444
 
                     "The total number of secret keys read: %d\n",
445
 
                     import_result->secret_read);
446
 
        fprintf_plus(stderr,
447
 
                     "The number of imported secret keys: %d\n",
448
 
                     import_result->secret_imported);
449
 
        fprintf_plus(stderr,
450
 
                     "The number of unchanged secret keys: %d\n",
451
 
                     import_result->secret_unchanged);
452
 
        fprintf_plus(stderr, "The number of keys not imported: %d\n",
453
 
                     import_result->not_imported);
454
 
        for(gpgme_import_status_t import_status
455
 
              = import_result->imports;
456
 
            import_status != NULL;
457
 
            import_status = import_status->next){
458
 
          fprintf_plus(stderr, "Import status for key: %s\n",
459
 
                       import_status->fpr);
460
 
          if(import_status->result != GPG_ERR_NO_ERROR){
461
 
            fprintf_plus(stderr, "Import result: %s: %s\n",
462
 
                         gpgme_strsource(import_status->result),
463
 
                         gpgme_strerror(import_status->result));
464
 
          }
465
 
          fprintf_plus(stderr, "Key status:\n");
466
 
          fprintf_plus(stderr,
467
 
                       import_status->status & GPGME_IMPORT_NEW
468
 
                       ? "The key was new.\n"
469
 
                       : "The key was not new.\n");
470
 
          fprintf_plus(stderr,
471
 
                       import_status->status & GPGME_IMPORT_UID
472
 
                       ? "The key contained new user IDs.\n"
473
 
                       : "The key did not contain new user IDs.\n");
474
 
          fprintf_plus(stderr,
475
 
                       import_status->status & GPGME_IMPORT_SIG
476
 
                       ? "The key contained new signatures.\n"
477
 
                       : "The key did not contain new signatures.\n");
478
 
          fprintf_plus(stderr,
479
 
                       import_status->status & GPGME_IMPORT_SUBKEY
480
 
                       ? "The key contained new sub keys.\n"
481
 
                       : "The key did not contain new sub keys.\n");
482
 
          fprintf_plus(stderr,
483
 
                       import_status->status & GPGME_IMPORT_SECRET
484
 
                       ? "The key contained a secret key.\n"
485
 
                       : "The key did not contain a secret key.\n");
486
 
        }
487
 
        return false;
488
 
      }
489
 
    }
490
311
    
491
312
    ret = close(fd);
492
313
    if(ret == -1){
533
354
  /* Create new GPGME "context" */
534
355
  rc = gpgme_new(&(mc->ctx));
535
356
  if(rc != GPG_ERR_NO_ERROR){
536
 
    fprintf_plus(stderr, "bad gpgme_new: %s: %s\n",
537
 
                 gpgme_strsource(rc), gpgme_strerror(rc));
 
357
    fprintf_plus(stderr, "Mandos plugin mandos-client: "
 
358
                 "bad gpgme_new: %s: %s\n", gpgme_strsource(rc),
 
359
                 gpgme_strerror(rc));
538
360
    return false;
539
361
  }
540
362
  
576
398
  /* Create new empty GPGME data buffer for the plaintext */
577
399
  rc = gpgme_data_new(&dh_plain);
578
400
  if(rc != GPG_ERR_NO_ERROR){
579
 
    fprintf_plus(stderr, "bad gpgme_data_new: %s: %s\n",
 
401
    fprintf_plus(stderr, "Mandos plugin mandos-client: "
 
402
                 "bad gpgme_data_new: %s: %s\n",
580
403
                 gpgme_strsource(rc), gpgme_strerror(rc));
581
404
    gpgme_data_release(dh_crypto);
582
405
    return -1;
595
418
      if(result == NULL){
596
419
        fprintf_plus(stderr, "gpgme_op_decrypt_result failed\n");
597
420
      } else {
598
 
        if(result->unsupported_algorithm != NULL) {
599
 
          fprintf_plus(stderr, "Unsupported algorithm: %s\n",
600
 
                       result->unsupported_algorithm);
601
 
        }
602
 
        fprintf_plus(stderr, "Wrong key usage: %s\n",
603
 
                     result->wrong_key_usage ? "Yes" : "No");
 
421
        fprintf_plus(stderr, "Unsupported algorithm: %s\n",
 
422
                     result->unsupported_algorithm);
 
423
        fprintf_plus(stderr, "Wrong key usage: %u\n",
 
424
                     result->wrong_key_usage);
604
425
        if(result->file_name != NULL){
605
426
          fprintf_plus(stderr, "File name: %s\n", result->file_name);
606
427
        }
607
 
 
608
 
        for(gpgme_recipient_t r = result->recipients; r != NULL;
609
 
            r = r->next){
 
428
        gpgme_recipient_t recipient;
 
429
        recipient = result->recipients;
 
430
        while(recipient != NULL){
610
431
          fprintf_plus(stderr, "Public key algorithm: %s\n",
611
 
                       gpgme_pubkey_algo_name(r->pubkey_algo));
612
 
          fprintf_plus(stderr, "Key ID: %s\n", r->keyid);
 
432
                       gpgme_pubkey_algo_name
 
433
                       (recipient->pubkey_algo));
 
434
          fprintf_plus(stderr, "Key ID: %s\n", recipient->keyid);
613
435
          fprintf_plus(stderr, "Secret key available: %s\n",
614
 
                       r->status == GPG_ERR_NO_SECKEY ? "No" : "Yes");
 
436
                       recipient->status == GPG_ERR_NO_SECKEY
 
437
                       ? "No" : "Yes");
 
438
          recipient = recipient->next;
615
439
        }
616
440
      }
617
441
    }
787
611
        }
788
612
        params.size += (unsigned int)bytes_read;
789
613
      }
790
 
      ret = close(dhpfile);
791
 
      if(ret == -1){
792
 
        perror_plus("close");
793
 
      }
794
614
      if(params.data == NULL){
795
615
        dhparamsfilename = NULL;
796
616
      }
805
625
                     safer_gnutls_strerror(ret));
806
626
        dhparamsfilename = NULL;
807
627
      }
808
 
      free(params.data);
809
628
    } while(false);
810
629
  }
811
630
  if(dhparamsfilename == NULL){
996
815
static void empty_log(__attribute__((unused)) AvahiLogLevel level,
997
816
                      __attribute__((unused)) const char *txt){}
998
817
 
 
818
/* Set effective uid to 0, return errno */
 
819
__attribute__((warn_unused_result))
 
820
int raise_privileges(void){
 
821
  int old_errno = errno;
 
822
  int ret = 0;
 
823
  if(seteuid(0) == -1){
 
824
    ret = errno;
 
825
  }
 
826
  errno = old_errno;
 
827
  return ret;
 
828
}
 
829
 
 
830
/* Set effective and real user ID to 0.  Return errno. */
 
831
__attribute__((warn_unused_result))
 
832
int raise_privileges_permanently(void){
 
833
  int old_errno = errno;
 
834
  int ret = raise_privileges();
 
835
  if(ret != 0){
 
836
    errno = old_errno;
 
837
    return ret;
 
838
  }
 
839
  if(setuid(0) == -1){
 
840
    ret = errno;
 
841
  }
 
842
  errno = old_errno;
 
843
  return ret;
 
844
}
 
845
 
 
846
/* Set effective user ID to unprivileged saved user ID */
 
847
__attribute__((warn_unused_result))
 
848
int lower_privileges(void){
 
849
  int old_errno = errno;
 
850
  int ret = 0;
 
851
  if(seteuid(uid) == -1){
 
852
    ret = errno;
 
853
  }
 
854
  errno = old_errno;
 
855
  return ret;
 
856
}
 
857
 
 
858
/* Lower privileges permanently */
 
859
__attribute__((warn_unused_result))
 
860
int lower_privileges_permanently(void){
 
861
  int old_errno = errno;
 
862
  int ret = 0;
 
863
  if(setuid(uid) == -1){
 
864
    ret = errno;
 
865
  }
 
866
  errno = old_errno;
 
867
  return ret;
 
868
}
 
869
 
999
870
/* Helper function to add_local_route() and delete_local_route() */
1000
871
__attribute__((nonnull, warn_unused_result))
1001
872
static bool add_delete_local_route(const bool add,
1206
1077
    bool match = false;
1207
1078
    {
1208
1079
      char *interface = NULL;
1209
 
      while((interface = argz_next(mc->interfaces,
1210
 
                                   mc->interfaces_size,
1211
 
                                   interface))){
 
1080
      while((interface=argz_next(mc->interfaces, mc->interfaces_size,
 
1081
                                 interface))){
1212
1082
        if(if_nametoindex(interface) == (unsigned int)if_index){
1213
1083
          match = true;
1214
1084
          break;
1367
1237
           with an explicit route added with the server's address.
1368
1238
           
1369
1239
           Avahi bug reference:
1370
 
           https://lists.freedesktop.org/archives/avahi/2010-February/001833.html
 
1240
           http://lists.freedesktop.org/archives/avahi/2010-February/001833.html
1371
1241
           https://bugs.debian.org/587961
1372
1242
        */
1373
1243
        if(debug){
1553
1423
                                               &decrypted_buffer, mc);
1554
1424
    if(decrypted_buffer_size >= 0){
1555
1425
      
1556
 
      clearerr(stdout);
1557
1426
      written = 0;
1558
1427
      while(written < (size_t) decrypted_buffer_size){
1559
1428
        if(quit_now){
1575
1444
        }
1576
1445
        written += (size_t)ret;
1577
1446
      }
1578
 
      ret = fflush(stdout);
1579
 
      if(ret != 0){
1580
 
        int e = errno;
1581
 
        if(debug){
1582
 
          fprintf_plus(stderr, "Error writing encrypted data: %s\n",
1583
 
                       strerror(errno));
1584
 
        }
1585
 
        errno = e;
1586
 
        goto mandos_end;
1587
 
      }
1588
1447
      retval = 0;
1589
1448
    }
1590
1449
  }
1621
1480
  return retval;
1622
1481
}
1623
1482
 
 
1483
__attribute__((nonnull))
1624
1484
static void resolve_callback(AvahiSServiceResolver *r,
1625
1485
                             AvahiIfIndex interface,
1626
1486
                             AvahiProtocol proto,
1781
1641
      perror_plus("ioctl SIOCGIFFLAGS");
1782
1642
      errno = old_errno;
1783
1643
    }
1784
 
    if((close(s) == -1) and debug){
1785
 
      old_errno = errno;
1786
 
      perror_plus("close");
1787
 
      errno = old_errno;
1788
 
    }
1789
1644
    return false;
1790
1645
  }
1791
 
  if((close(s) == -1) and debug){
1792
 
    old_errno = errno;
1793
 
    perror_plus("close");
1794
 
    errno = old_errno;
1795
 
  }
1796
1646
  return true;
1797
1647
}
1798
1648
 
2059
1909
      return;
2060
1910
    }
2061
1911
  }
2062
 
  int devnull = (int)TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY));
2063
 
  if(devnull == -1){
2064
 
    perror_plus("open(\"/dev/null\", O_RDONLY)");
2065
 
    return;
2066
 
  }
2067
1912
  int numhooks = scandirat(hookdir_fd, ".", &direntries,
2068
1913
                           runnable_hook, alphasort);
2069
1914
  if(numhooks == -1){
2070
1915
    perror_plus("scandir");
2071
 
    close(devnull);
2072
1916
    return;
2073
1917
  }
2074
1918
  struct dirent *direntry;
2075
1919
  int ret;
 
1920
  int devnull = (int)TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY));
 
1921
  if(devnull == -1){
 
1922
    perror_plus("open(\"/dev/null\", O_RDONLY)");
 
1923
    return;
 
1924
  }
2076
1925
  for(int i = 0; i < numhooks; i++){
2077
1926
    direntry = direntries[i];
2078
1927
    if(debug){
2334
2183
  
2335
2184
  /* Sleep checking until interface is running.
2336
2185
     Check every 0.25s, up to total time of delay */
2337
 
  for(int i = 0; i < delay * 4; i++){
 
2186
  for(int i=0; i < delay * 4; i++){
2338
2187
    if(interface_is_running(interface)){
2339
2188
      break;
2340
2189
    }
2636
2485
  
2637
2486
  {
2638
2487
    /* Work around Debian bug #633582:
2639
 
       <https://bugs.debian.org/633582> */
 
2488
       <http://bugs.debian.org/633582> */
2640
2489
    
2641
2490
    /* Re-raise privileges */
2642
2491
    ret = raise_privileges();
3097
2946
 end:
3098
2947
  
3099
2948
  if(debug){
3100
 
    if(signal_received){
3101
 
      fprintf_plus(stderr, "%s exiting due to signal %d: %s\n",
3102
 
                   argv[0], signal_received,
3103
 
                   strsignal(signal_received));
3104
 
    } else {
3105
 
      fprintf_plus(stderr, "%s exiting\n", argv[0]);
3106
 
    }
 
2949
    fprintf_plus(stderr, "%s exiting\n", argv[0]);
3107
2950
  }
3108
2951
  
3109
2952
  /* Cleanup things */
3161
3004
      /* Take down the network interfaces which were brought up */
3162
3005
      {
3163
3006
        char *interface = NULL;
3164
 
        while((interface = argz_next(interfaces_to_take_down,
3165
 
                                     interfaces_to_take_down_size,
3166
 
                                     interface))){
 
3007
        while((interface=argz_next(interfaces_to_take_down,
 
3008
                                   interfaces_to_take_down_size,
 
3009
                                   interface))){
3167
3010
          ret = take_down_interface(interface);
3168
3011
          if(ret != 0){
3169
3012
            errno = ret;
3198
3041
                                                | O_PATH));
3199
3042
    if(dir_fd == -1){
3200
3043
      perror_plus("open");
3201
 
      return;
3202
3044
    }
3203
3045
    int numentries = scandirat(dir_fd, ".", &direntries,
3204
3046
                               notdotentries, alphasort);
3221
3063
            clean_dir_at(dir_fd, direntries[i]->d_name, level+1);
3222
3064
            dret = 0;
3223
3065
          }
3224
 
          if((dret == -1) and (errno != ENOENT)){
 
3066
          if(dret == -1){
3225
3067
            fprintf_plus(stderr, "unlink(\"%s/%s\"): %s\n", dirname,
3226
3068
                         direntries[i]->d_name, strerror(errno));
3227
3069
          }
3231
3073
      
3232
3074
      /* need to clean even if 0 because man page doesn't specify */
3233
3075
      free(direntries);
 
3076
      if(numentries == -1){
 
3077
        perror_plus("scandirat");
 
3078
      }
3234
3079
      dret = unlinkat(base, dirname, AT_REMOVEDIR);
3235
3080
      if(dret == -1 and errno != ENOENT){
3236
3081
        perror_plus("rmdir");