/mandos/release

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

« back to all changes in this revision

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

  • Committer: Teddy Hogeborn
  • Date: 2011-07-25 18:52:11 UTC
  • mfrom: (237.7.37 trunk)
  • Revision ID: teddy@fukt.bsnet.se-20110725185211-wlitr9jvs70e1xh8
MergeĀ fromĀ trunk.

Show diffs side-by-side

added added

removed removed

Lines of Context:
26
26
 * along with this program.  If not, see
27
27
 * <http://www.gnu.org/licenses/>.
28
28
 * 
29
 
 * Contact the authors at <mandos@recompile.se>.
 
29
 * Contact the authors at <mandos@fukt.bsnet.se>.
30
30
 */
31
31
 
32
32
/* Needed by GPGME, specifically gpgme_data_seek() */
127
127
bool debug = false;
128
128
static const char mandos_protocol_version[] = "1";
129
129
const char *argp_program_version = "mandos-client " VERSION;
130
 
const char *argp_program_bug_address = "<mandos@recompile.se>";
 
130
const char *argp_program_bug_address = "<mandos@fukt.bsnet.se>";
131
131
static const char sys_class_net[] = "/sys/class/net";
132
132
char *connect_to = NULL;
133
133
 
187
187
  return buffer_capacity;
188
188
}
189
189
 
190
 
/* Add server to set of servers to retry periodically */
191
190
int add_server(const char *ip, uint16_t port,
192
191
                 AvahiIfIndex if_index,
193
192
                 int af){
205
204
    perror_plus("strdup");
206
205
    return -1;
207
206
  }
208
 
  /* Special case of first server */
 
207
  /* unique case of first server */
209
208
  if (mc.current_server == NULL){
210
209
    new_server->next = new_server;
211
210
    new_server->prev = new_server;
212
211
    mc.current_server = new_server;
213
 
  /* Place the new server last in the list */
 
212
  /* Placing the new server last in the list */
214
213
  } else {
215
214
    new_server->next = mc.current_server;
216
215
    new_server->prev = mc.current_server->prev;
283
282
    return false;
284
283
  }
285
284
  
286
 
  /* Set GPGME home directory for the OpenPGP engine only */
 
285
    /* Set GPGME home directory for the OpenPGP engine only */
287
286
  rc = gpgme_get_engine_info(&engine_info);
288
287
  if(rc != GPG_ERR_NO_ERROR){
289
288
    fprintf(stderr, "bad gpgme_get_engine_info: %s: %s\n",
1085
1084
  errno = old_errno;
1086
1085
}
1087
1086
 
1088
 
bool get_flags(const char *ifname, struct ifreq *ifr){
1089
 
  int ret;
1090
 
  
1091
 
  int s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP);
1092
 
  if(s < 0){
1093
 
    perror_plus("socket");
1094
 
    return false;
1095
 
  }
1096
 
  strcpy(ifr->ifr_name, ifname);
1097
 
  ret = ioctl(s, SIOCGIFFLAGS, ifr);
1098
 
  if(ret == -1){
 
1087
/* 
 
1088
 * This function determines if a directory entry in /sys/class/net
 
1089
 * corresponds to an acceptable network device.
 
1090
 * (This function is passed to scandir(3) as a filter function.)
 
1091
 */
 
1092
int good_interface(const struct dirent *if_entry){
 
1093
  ssize_t ssret;
 
1094
  char *flagname = NULL;
 
1095
  if(if_entry->d_name[0] == '.'){
 
1096
    return 0;
 
1097
  }
 
1098
  int ret = asprintf(&flagname, "%s/%s/flags", sys_class_net,
 
1099
                     if_entry->d_name);
 
1100
  if(ret < 0){
 
1101
    perror_plus("asprintf");
 
1102
    return 0;
 
1103
  }
 
1104
  int flags_fd = (int)TEMP_FAILURE_RETRY(open(flagname, O_RDONLY));
 
1105
  if(flags_fd == -1){
 
1106
    perror_plus("open");
 
1107
    free(flagname);
 
1108
    return 0;
 
1109
  }
 
1110
  free(flagname);
 
1111
  typedef short ifreq_flags;    /* ifreq.ifr_flags in netdevice(7) */
 
1112
  /* read line from flags_fd */
 
1113
  ssize_t to_read = 2+(sizeof(ifreq_flags)*2)+1; /* "0x1003\n" */
 
1114
  char *flagstring = malloc((size_t)to_read+1); /* +1 for final \0 */
 
1115
  flagstring[(size_t)to_read] = '\0';
 
1116
  if(flagstring == NULL){
 
1117
    perror_plus("malloc");
 
1118
    close(flags_fd);
 
1119
    return 0;
 
1120
  }
 
1121
  while(to_read > 0){
 
1122
    ssret = (ssize_t)TEMP_FAILURE_RETRY(read(flags_fd, flagstring,
 
1123
                                             (size_t)to_read));
 
1124
    if(ssret == -1){
 
1125
      perror_plus("read");
 
1126
      free(flagstring);
 
1127
      close(flags_fd);
 
1128
      return 0;
 
1129
    }
 
1130
    to_read -= ssret;
 
1131
    if(ssret == 0){
 
1132
      break;
 
1133
    }
 
1134
  }
 
1135
  close(flags_fd);
 
1136
  intmax_t tmpmax;
 
1137
  char *tmp;
 
1138
  errno = 0;
 
1139
  tmpmax = strtoimax(flagstring, &tmp, 0);
 
1140
  if(errno != 0 or tmp == flagstring or (*tmp != '\0'
 
1141
                                         and not (isspace(*tmp)))
 
1142
     or tmpmax != (ifreq_flags)tmpmax){
1099
1143
    if(debug){
1100
 
      perror_plus("ioctl SIOCGIFFLAGS");
 
1144
      fprintf(stderr, "Invalid flags \"%s\" for interface \"%s\"\n",
 
1145
              flagstring, if_entry->d_name);
1101
1146
    }
1102
 
    return false;
 
1147
    free(flagstring);
 
1148
    return 0;
1103
1149
  }
1104
 
  return true;
1105
 
}
1106
 
 
1107
 
bool good_flags(const char *ifname, const struct ifreq *ifr){
1108
 
  
 
1150
  free(flagstring);
 
1151
  ifreq_flags flags = (ifreq_flags)tmpmax;
1109
1152
  /* Reject the loopback device */
1110
 
  if(ifr->ifr_flags & IFF_LOOPBACK){
 
1153
  if(flags & IFF_LOOPBACK){
1111
1154
    if(debug){
1112
1155
      fprintf(stderr, "Rejecting loopback interface \"%s\"\n",
1113
 
              ifname);
 
1156
              if_entry->d_name);
1114
1157
    }
1115
 
    return false;
 
1158
    return 0;
1116
1159
  }
1117
1160
  /* Accept point-to-point devices only if connect_to is specified */
1118
 
  if(connect_to != NULL and (ifr->ifr_flags & IFF_POINTOPOINT)){
 
1161
  if(connect_to != NULL and (flags & IFF_POINTOPOINT)){
1119
1162
    if(debug){
1120
1163
      fprintf(stderr, "Accepting point-to-point interface \"%s\"\n",
1121
 
              ifname);
 
1164
              if_entry->d_name);
1122
1165
    }
1123
 
    return true;
 
1166
    return 1;
1124
1167
  }
1125
1168
  /* Otherwise, reject non-broadcast-capable devices */
1126
 
  if(not (ifr->ifr_flags & IFF_BROADCAST)){
 
1169
  if(not (flags & IFF_BROADCAST)){
1127
1170
    if(debug){
1128
1171
      fprintf(stderr, "Rejecting non-broadcast interface \"%s\"\n",
1129
 
              ifname);
 
1172
              if_entry->d_name);
1130
1173
    }
1131
 
    return false;
 
1174
    return 0;
1132
1175
  }
1133
1176
  /* Reject non-ARP interfaces (including dummy interfaces) */
1134
 
  if(ifr->ifr_flags & IFF_NOARP){
 
1177
  if(flags & IFF_NOARP){
1135
1178
    if(debug){
1136
 
      fprintf(stderr, "Rejecting non-ARP interface \"%s\"\n", ifname);
 
1179
      fprintf(stderr, "Rejecting non-ARP interface \"%s\"\n",
 
1180
              if_entry->d_name);
1137
1181
    }
1138
 
    return false;
 
1182
    return 0;
1139
1183
  }
1140
 
  
1141
1184
  /* Accept this device */
1142
1185
  if(debug){
1143
 
    fprintf(stderr, "Interface \"%s\" is good\n", ifname);
1144
 
  }
1145
 
  return true;
1146
 
}
1147
 
 
1148
 
/* 
1149
 
 * This function determines if a directory entry in /sys/class/net
1150
 
 * corresponds to an acceptable network device.
1151
 
 * (This function is passed to scandir(3) as a filter function.)
1152
 
 */
1153
 
int good_interface(const struct dirent *if_entry){
1154
 
  int ret;
1155
 
  if(if_entry->d_name[0] == '.'){
1156
 
    return 0;
1157
 
  }
1158
 
  struct ifreq ifr;
1159
 
 
1160
 
  if(not get_flags(if_entry->d_name, &ifr)){
1161
 
    return 0;
1162
 
  }
1163
 
  
1164
 
  if(not good_flags(if_entry->d_name, &ifr)){
1165
 
    return 0;
 
1186
    fprintf(stderr, "Interface \"%s\" is acceptable\n",
 
1187
            if_entry->d_name);
1166
1188
  }
1167
1189
  return 1;
1168
1190
}
1183
1205
  struct timespec now;
1184
1206
  struct timespec waited_time;
1185
1207
  intmax_t block_time;
1186
 
  
 
1208
 
1187
1209
  while(true){
1188
1210
    if(mc.current_server == NULL){
1189
1211
      if (debug){
1214
1236
      block_time = ((retry_interval
1215
1237
                     - ((intmax_t)waited_time.tv_sec * 1000))
1216
1238
                    - ((intmax_t)waited_time.tv_nsec / 1000000));
1217
 
      
 
1239
 
1218
1240
      if (debug){
1219
 
        fprintf(stderr, "Blocking for %" PRIdMAX " ms\n", block_time);
 
1241
        fprintf(stderr, "Blocking for %ld ms\n", block_time);
1220
1242
      }
1221
 
      
 
1243
 
1222
1244
      if(block_time <= 0){
1223
1245
        ret = start_mandos_communication(mc.current_server->ip,
1224
1246
                                         mc.current_server->port,
1389
1411
        errno = 0;
1390
1412
        retry_interval = strtod(arg, &tmp);
1391
1413
        if(errno != 0 or tmp == arg or *tmp != '\0'
1392
 
           or (retry_interval * 1000) > INT_MAX
1393
 
           or retry_interval < 0){
 
1414
           or (retry_interval * 1000) > INT_MAX){
1394
1415
          argp_error(state, "Bad retry interval");
1395
1416
        }
1396
1417
        break;
1447
1468
      perror_plus("seteuid");
1448
1469
    }
1449
1470
    
1450
 
    if(strcmp(seckey, PATHDIR "/" SECKEY) == 0){
1451
 
      int seckey_fd = open(seckey, O_RDONLY);
1452
 
      if(seckey_fd == -1){
1453
 
        perror_plus("open");
 
1471
    int seckey_fd = open(PATHDIR "/" SECKEY, O_RDONLY);
 
1472
    if(seckey_fd == -1){
 
1473
      perror_plus("open");
 
1474
    } else {
 
1475
      ret = (int)TEMP_FAILURE_RETRY(fstat(seckey_fd, &st));
 
1476
      if(ret == -1){
 
1477
        perror_plus("fstat");
1454
1478
      } else {
1455
 
        ret = (int)TEMP_FAILURE_RETRY(fstat(seckey_fd, &st));
1456
 
        if(ret == -1){
1457
 
          perror_plus("fstat");
1458
 
        } else {
1459
 
          if(S_ISREG(st.st_mode) and st.st_uid == 0 and st.st_gid == 0){
1460
 
            ret = fchown(seckey_fd, uid, gid);
1461
 
            if(ret == -1){
1462
 
              perror_plus("fchown");
1463
 
            }
 
1479
        if(S_ISREG(st.st_mode) and st.st_uid == 0 and st.st_gid == 0){
 
1480
          ret = fchown(seckey_fd, uid, gid);
 
1481
          if(ret == -1){
 
1482
            perror_plus("fchown");
1464
1483
          }
1465
1484
        }
1466
 
        TEMP_FAILURE_RETRY(close(seckey_fd));
1467
1485
      }
 
1486
      TEMP_FAILURE_RETRY(close(seckey_fd));
1468
1487
    }
1469
1488
    
1470
 
    if(strcmp(pubkey, PATHDIR "/" PUBKEY) == 0){
1471
 
      int pubkey_fd = open(pubkey, O_RDONLY);
1472
 
      if(pubkey_fd == -1){
1473
 
        perror_plus("open");
 
1489
    int pubkey_fd = open(PATHDIR "/" PUBKEY, O_RDONLY);
 
1490
    if(pubkey_fd == -1){
 
1491
      perror_plus("open");
 
1492
    } else {
 
1493
      ret = (int)TEMP_FAILURE_RETRY(fstat(pubkey_fd, &st));
 
1494
      if(ret == -1){
 
1495
        perror_plus("fstat");
1474
1496
      } else {
1475
 
        ret = (int)TEMP_FAILURE_RETRY(fstat(pubkey_fd, &st));
1476
 
        if(ret == -1){
1477
 
          perror_plus("fstat");
1478
 
        } else {
1479
 
          if(S_ISREG(st.st_mode) and st.st_uid == 0 and st.st_gid == 0){
1480
 
            ret = fchown(pubkey_fd, uid, gid);
1481
 
            if(ret == -1){
1482
 
              perror_plus("fchown");
1483
 
            }
 
1497
        if(S_ISREG(st.st_mode) and st.st_uid == 0 and st.st_gid == 0){
 
1498
          ret = fchown(pubkey_fd, uid, gid);
 
1499
          if(ret == -1){
 
1500
            perror_plus("fchown");
1484
1501
          }
1485
1502
        }
1486
 
        TEMP_FAILURE_RETRY(close(pubkey_fd));
1487
1503
      }
 
1504
      TEMP_FAILURE_RETRY(close(pubkey_fd));
1488
1505
    }
1489
1506
    
1490
1507
    /* Lower privileges */
1812
1829
    
1813
1830
    port = (uint16_t)tmpmax;
1814
1831
    *address = '\0';
 
1832
    address = connect_to;
1815
1833
    /* Colon in address indicates IPv6 */
1816
1834
    int af;
1817
 
    if(strchr(connect_to, ':') != NULL){
 
1835
    if(strchr(address, ':') != NULL){
1818
1836
      af = AF_INET6;
1819
 
      /* Accept [] around IPv6 address - see RFC 5952 */
1820
 
      if(connect_to[0] == '[' and address[-1] == ']')
1821
 
        {
1822
 
          connect_to++;
1823
 
          address[-1] = '\0';
1824
 
        }
1825
1837
    } else {
1826
1838
      af = AF_INET;
1827
1839
    }
1828
 
    address = connect_to;
1829
1840
    
1830
1841
    if(quit_now){
1831
1842
      goto end;
1832
1843
    }
1833
 
    
 
1844
 
1834
1845
    while(not quit_now){
1835
1846
      ret = start_mandos_communication(address, port, if_index, af);
1836
1847
      if(quit_now or ret == 0){
1837
1848
        break;
1838
1849
      }
1839
 
      if(debug){
1840
 
        fprintf(stderr, "Retrying in %d seconds\n",
1841
 
                (int)retry_interval);
1842
 
      }
1843
 
      sleep((int)retry_interval);
1844
 
    }
1845
 
    
 
1850
      sleep((int)retry_interval or 1);
 
1851
    };
 
1852
 
1846
1853
    if (not quit_now){
1847
1854
      exitcode = EXIT_SUCCESS;
1848
1855
    }
1985
1992
  if(tempdir_created){
1986
1993
    struct dirent **direntries = NULL;
1987
1994
    struct dirent *direntry = NULL;
1988
 
    int numentries = scandir(tempdir, &direntries, notdotentries,
1989
 
                             alphasort);
1990
 
    if (numentries > 0){
1991
 
      for(int i = 0; i < numentries; i++){
 
1995
    ret = scandir(tempdir, &direntries, notdotentries, alphasort);
 
1996
    if (ret > 0){
 
1997
      for(int i = 0; i < ret; i++){
1992
1998
        direntry = direntries[i];
1993
1999
        char *fullname = NULL;
1994
2000
        ret = asprintf(&fullname, "%s/%s", tempdir,
2006
2012
      }
2007
2013
    }
2008
2014
 
2009
 
    /* need to clean even if 0 because man page doesn't specify */
 
2015
    /* need to be cleaned even if ret == 0 because man page doesn't
 
2016
       specify */
2010
2017
    free(direntries);
2011
 
    if (numentries == -1){
 
2018
    if (ret == -1){
2012
2019
      perror_plus("scandir");
2013
2020
    }
2014
2021
    ret = rmdir(tempdir);