/** * Processes an incoming REG_CONF message. * Expected in response to a REGISTER when encryption is disabled. */ void handle_regconf(struct group_list_t *group, const unsigned char *message, unsigned meslen) { const struct regconf_h *regconf; const uint32_t *addrlist; int addrcnt; regconf = (const struct regconf_h *)message; addrlist = (const uint32_t *)(message + (regconf->hlen * 4)); if ((meslen < (regconf->hlen * 4U)) || ((regconf->hlen * 4U) < sizeof(struct regconf_h))) { glog1(group, "Rejecting REG_CONF from server: invalid message size"); return; } addrcnt = (meslen - (regconf->hlen * 4)) / 4; if (uid_in_list(addrlist, addrcnt)) { glog2(group, "Registration confirmed"); group->phase = PHASE_MIDGROUP; set_timeout(group, 0); } if (group->restart) { read_restart_file(group); } }
/** * Set argument defaults, read and validate command line options */ void process_args(int argc, char *argv[]) { int c, i, listidx, read_restart, rval; long tmpval; char line[1000], *dest, *destname, filename[MAXPATHNAME], *fingerprint, *p; char keylenstr[50]; struct addrinfo ai_hints, *ai_rval; FILE *destfile, *excludefile, *listfile; const char opts[] = "x:R:L:B:g:n:m:Y:h:w:e:ck:K:lTb:t:Q:" "zZI:p:u:j:qfyU:H:F:X:M:P:C:D:oE:S:r:s:i:W:N:"; set_defaults(); memset(keylenstr, 0, sizeof(keylenstr)); read_restart = 0; // read lettered arguments while ((c = getopt(argc, argv, opts)) != EOF) { switch (c) { case 'x': log_level = atoi(optarg); if (log_level < 0) { fprintf(stderr,"Invalid log level\n"); exit(ERR_PARAM); } break; case 'R': // Expecting rate as Kbps, translate to B/s rate = atoi(optarg); if ((rate <= 0) && (rate != -1)) { fprintf(stderr,"Invalid rate\n"); exit(ERR_PARAM); } if (rate != -1) { rate = rate * 1024 / 8; } break; case 'L': strncpy(logfile, optarg, sizeof(logfile)-1); logfile[sizeof(logfile)-1] = '\x0'; break; case 'B': rcvbuf = atoi(optarg); if ((rcvbuf < 65536) || (rcvbuf > 104857600)) { fprintf(stderr, "Invalid receive buffer size\n"); exit(ERR_PARAM); } break; case 'g': max_log_size = atoi(optarg); if ((max_log_size < 1) || (max_log_size > 1024)) { fprintf(stderr, "Invalid max log size\n"); exit(ERR_PARAM); } max_log_size *= 1000000; break; case 'n': max_log_count = atoi(optarg); if ((max_log_count < 1) || (max_log_count > 1000)) { fprintf(stderr, "Invalid max log count\n"); exit(ERR_PARAM); } break; case 'm': max_nak_cnt = atoi(optarg); if ((max_nak_cnt < 1) || (max_nak_cnt > 1000)) { fprintf(stderr, "Invalid max nak count\n"); exit(ERR_PARAM); } break; case 'Y': if ((keytype = get_keytype(optarg)) == -1) { fprintf(stderr, "Invalid keytype\n"); exit(ERR_PARAM); } if (keytype != KEY_NONE && !cipher_supported(keytype)) { fprintf(stderr, "Keytype not supported\n"); exit(ERR_PARAM); } break; case 'h': if ((hashtype = get_hashtype(optarg)) == -1) { fprintf(stderr, "Invalid hashtype\n"); exit(ERR_PARAM); } if (!hash_supported(hashtype)) { fprintf(stderr, "Hashtype not supported\n"); exit(ERR_PARAM); } break; case 'w': if (!strcmp(optarg, "hmac")) { sigtype = SIG_HMAC; } else if (!strcmp(optarg, "keyex")) { sigtype = SIG_KEYEX; } else { fprintf(stderr, "Invalid sigtype\n"); exit(ERR_PARAM); } break; case 'e': p = strtok(optarg, ":"); if (!p) { fprintf(stderr, "Error reading keyextype\n"); exit(ERR_PARAM); } if (!strcmp(p, "rsa")) { keyextype = KEYEX_RSA; } else if (!strcmp(p, "ecdh_rsa")) { keyextype = KEYEX_ECDH_RSA; } else if (!strcmp(p, "ecdh_ecdsa")) { keyextype = KEYEX_ECDH_ECDSA; } else { fprintf(stderr, "Invalid keyextype\n"); exit(ERR_PARAM); } if ((keyextype == KEYEX_ECDH_RSA) || (keyextype == KEYEX_ECDH_ECDSA)) { p = strtok(NULL, ":"); if (p) { ecdh_curve = get_curve(p); if (ecdh_curve == 0) { fprintf(stderr, "Invalid curve\n"); exit(ERR_PARAM); } } else { ecdh_curve = DEF_CURVE; } } break; case 'c': client_auth = 1; break; case 'k': strncpy(keyfile, optarg, sizeof(keyfile)-1); keyfile[sizeof(keyfile)-1] = '\x0'; break; case 'K': strncpy(keylenstr, optarg, sizeof(keylenstr)-1); keylenstr[sizeof(keylenstr)-1] = '\x0'; break; case 'l': follow_links = 1; break; case 'T': showtime = 1; break; case 'b': blocksize = atoi(optarg); if ((blocksize < 512) || (blocksize > (MAXMTU - 200))) { fprintf(stderr, "Invalid blocksize\n"); exit(ERR_PARAM); } break; case 't': tmpval = atoi(optarg); if ((tmpval <= 0) || (tmpval > 255)) { fprintf(stderr, "Invalid ttl\n"); exit(ERR_PARAM); } ttl = (char)tmpval; break; case 'Q': tmpval = strtol(optarg, NULL, 0); if ((tmpval < 0) || (tmpval > 63)) { fprintf(stderr, "Invalid dscp\n"); exit(ERR_PARAM); } dscp = (tmpval & 0xFF) << 2; break; case 'I': if ((listidx = getifbyname(optarg, ifl, ifl_len)) != -1) { out_if = ifl[listidx]; break; } memset(&ai_hints, 0, sizeof(ai_hints)); ai_hints.ai_family = AF_UNSPEC; ai_hints.ai_socktype = SOCK_DGRAM; ai_hints.ai_protocol = 0; ai_hints.ai_flags = 0; if ((rval = getaddrinfo(optarg, NULL, &ai_hints, &ai_rval)) != 0) { fprintf(stderr, "Invalid name/address %s: %s\n", optarg, gai_strerror(rval)); exit(ERR_PARAM); } // Just use the first addrinfo entry if ((listidx = getifbyaddr((union sockaddr_u *)ai_rval->ai_addr, ifl, ifl_len)) == -1) { fprintf(stderr, "Interface %s not found", optarg); exit(ERR_PARAM); } out_if = ifl[listidx]; freeaddrinfo(ai_rval); break; case 'z': sync_mode = 1; break; case 'Z': sync_preview = 1; sync_mode = 1; break; case 'p': strncpy(port, optarg, sizeof(port)-1); port[sizeof(port)-1] = '\x0'; break; case 'u': strncpy(srcport, optarg, sizeof(srcport)-1); srcport[sizeof(srcport)-1] = '\x0'; break; case 'j': if (read_restart) { fprintf(stderr,"Can't specify both -j and -F\n"); exit(ERR_PARAM); } if ((destfile = fopen(optarg, "rt")) == NULL) { fprintf(stderr,"Couldn't open proxy list %s: %s\n", optarg, strerror(errno)); exit(ERR_PARAM); } while (fgets(line, sizeof(line), destfile)) { while ((strlen(line) > 0) && ((line[strlen(line)-1] == '\r') || (line[strlen(line)-1] == '\n'))) { line[strlen(line)-1] = '\x0'; } destname = strtok(line, "|"); if (!destname) continue; if (destname[0] == '#') continue; if (strlen(destname) >= DESTNAME_LEN) { fprintf(stderr, "Proxylist: name too long\n"); exit(ERR_PARAM); } fingerprint = strtok(NULL, " \t"); add_dest_by_name(destname, fingerprint, 1); } if (!feof(destfile) && ferror(destfile)) { perror("Failed to read from proxylist file"); exit(ERR_PARAM); } fclose(destfile); break; case 'q': quit_on_error = 1; break; case 'f': save_fail = 1; break; case 'y': sys_keys = 1; break; case 'U': errno = 0; server_id = strtoul(optarg, NULL, 16); if (errno) { perror("Invalid UID\n"); exit(ERR_PARAM); } server_id = htonl(server_id); break; case 'H': if (read_restart) { fprintf(stderr,"Can't specify both -H and -F\n"); exit(ERR_PARAM); } if (optarg[0] == '@') { dest = &optarg[1]; if ((destfile = fopen(dest, "rt")) == NULL) { fprintf(stderr,"Couldn't open destination list %s: %s\n", dest, strerror(errno)); exit(ERR_PARAM); } while (fgets(line, sizeof(line), destfile)) { while ((strlen(line) > 0) && ((line[strlen(line)-1] == '\r') || (line[strlen(line)-1] == '\n'))) { line[strlen(line)-1] = '\x0'; } destname = strtok(line, "|"); if (!destname) continue; if (destname[0] == '#') continue; if (strlen(destname) >= DESTNAME_LEN) { fprintf(stderr, "Hostlist: name too long\n"); exit(ERR_PARAM); } fingerprint = strtok(NULL, " \t"); add_dest_by_name(destname, fingerprint, 0); } if (!feof(destfile) && ferror(destfile)) { perror("Failed to read from hostlist file"); exit(ERR_PARAM); } fclose(destfile); } else { dest = strtok(optarg, ","); while (dest != NULL) { add_dest_by_name(dest, NULL, 0); dest = strtok(NULL, ","); } } break; case 'F': if (destcount != 0) { fprintf(stderr,"Can't specify both -H and -F\n"); exit(ERR_PARAM); } read_restart = 1; save_fail = 1; read_restart_file(optarg); break; case 'X': if ((excludefile = fopen(optarg, "rt")) == NULL) { fprintf(stderr,"Couldn't open exclude list %s: %s\n", optarg, strerror(errno)); exit(ERR_PARAM); } while (fgets(filename, sizeof(filename), excludefile)) { while ((strlen(filename) > 0) && ((filename[strlen(filename)-1] == '\r') || (filename[strlen(filename)-1] == '\n'))) { filename[strlen(filename)-1] = '\x0'; } if (strlen(filename) == 0) continue; if (excludecount == MAXEXCLUDE) { fprintf(stderr,"Exceeded maximum exclude file count\n"); exit(ERR_PARAM); } strncpy(exclude[excludecount], filename, sizeof(exclude[0])); exclude[excludecount][sizeof(exclude[0])-1] = '\x0'; excludecount++; } if (!feof(excludefile) && ferror(excludefile)) { perror("Failed to read from exclude file"); exit(ERR_PARAM); } fclose(excludefile); break; case 'M': strncpy(pub_multi, optarg, sizeof(pub_multi)-1); pub_multi[sizeof(pub_multi)-1] = '\x0'; break; case 'P': strncpy(priv_multi, optarg, sizeof(priv_multi)-1); priv_multi[sizeof(priv_multi)-1] = '\x0'; break; case 'C': p = strtok(optarg, ":"); if (!p) { fprintf(stderr, "Error reading cc_type\n"); exit(ERR_PARAM); } if (!strcmp(p, "none")) { cc_type = CC_NONE; } else if (!strcmp(p, "tfmcc")) { cc_type = CC_TFMCC; p = strtok(NULL, ":"); if (p) { max_rate = atoi(p); if (max_rate <= 0) { fprintf(stderr,"Invalid max rate\n"); exit(ERR_PARAM); } max_rate = max_rate * 1024 / 8; } } else { // PGMCC not currently supported fprintf(stderr, "Invalid congestion control type\n"); exit(ERR_PARAM); } break; case 'D': strncpy(destfname, optarg, sizeof(destfname)-1); destfname[sizeof(destfname)-1] = '\x0'; while (destfname[strlen(destfname)-1] == PATH_SEP) { destfname[strlen(destfname)-1] = '\x0'; } break; case 'o': dest_is_dir = 1; break; case 'E': p = strtok(optarg, ","); while (p != NULL) { strncpy(basedir[basedircount], p, sizeof(basedir[basedircount])-1); basedir[basedircount][sizeof(basedir[basedircount])-1] = '\x0'; basedircount++; p = strtok(NULL, ","); } break; case 'S': strncpy(statusfilename, optarg, sizeof(statusfilename)-1); statusfilename[sizeof(statusfilename)-1] = '\x0'; break; case 'r': p = strtok(optarg, ":"); if (!p) { fprintf(stderr, "Error reading cc_type\n"); exit(ERR_PARAM); } errno = 0; grtt = atof(p); if (errno) { perror("Invalid grtt"); exit(ERR_PARAM); } else if ((grtt < CLIENT_RTT_MIN) || (grtt > 1000)) { fprintf(stderr, "Invalid grtt\n"); exit(ERR_PARAM); } p = strtok(NULL, ":"); if (p) { errno = 0; min_grtt = atof(p); if (errno) { perror("Invalid min_grtt"); exit(ERR_PARAM); } else if ((min_grtt < CLIENT_RTT_MIN) || (min_grtt > 1000)) { fprintf(stderr, "Invalid min_grtt\n"); exit(ERR_PARAM); } p = strtok(NULL, ":"); if (!p) { fprintf(stderr, "Missing max_grtt\n"); exit(ERR_PARAM); } errno = 0; max_grtt = atof(p); if (errno) { perror("Invalid max_grtt"); exit(ERR_PARAM); } else if ((max_grtt < CLIENT_RTT_MIN) || (max_grtt > 1000)) { fprintf(stderr, "Invalid max_grtt\n"); exit(ERR_PARAM); } if (min_grtt > max_grtt) { fprintf(stderr, "Invalid min_grtt/max_grtt\n"); exit(ERR_PARAM); } else if ((grtt > max_grtt) || (grtt < min_grtt)) { fprintf(stderr, "Invalid grtt\n"); exit(ERR_PARAM); } } break; case 's': robust = atoi(optarg); if ((robust < 10) || (robust > 50)) { fprintf(stderr,"Invalid robustness factor\n"); exit(ERR_PARAM); } break; case 'i': if (filecount != 0) { fprintf(stderr,"Can't specify both -i and -F\n"); exit(ERR_PARAM); } if (strcmp(optarg, "-") == 0) { listfile = stdin; } else if ((listfile = fopen(optarg, "rt")) == NULL) { fprintf(stderr,"Couldn't open file list %s: %s\n", optarg, strerror(errno)); exit(ERR_PARAM); } while (fgets(filename, sizeof(filename), listfile)) { if (filecount == MAXFILES) { fprintf(stderr, "Exceeded maximum file count\n"); exit(ERR_PARAM); } while ((strlen(filename) > 0) && ((filename[strlen(filename)-1] == '\r') || (filename[strlen(filename)-1] == '\n'))) { filename[strlen(filename)-1] = '\x0'; } if (strlen(filename) == 0) continue; strncpy(filelist[filecount], filename, sizeof(filelist[0])-1); filelist[filecount][sizeof(filelist[0])-1] = '\x0'; filecount++; } if (!feof(listfile) && ferror(listfile)) { perror("Failed to read from file list"); exit(ERR_PARAM); } fclose(listfile); break; case 'W': txweight = atoi(optarg); if ((txweight < 110) || (txweight > 10000)) { fprintf(stderr, "Invalid txweight\n"); exit(ERR_PARAM); } break; case 'N': max_nak_pct = atoi(optarg); if ((max_nak_pct < 0) || (max_nak_pct > 100)) { fprintf(stderr, "Invalid max_nak_pct\n"); exit(ERR_PARAM); } break; case '?': fprintf(stderr, USAGE); exit(ERR_PARAM); } } argc -= optind; argv += optind; if ((argc == 0) && (filecount == 0)) { fprintf(stderr, USAGE); exit(ERR_PARAM); } if (save_fail && sync_mode) { fprintf(stderr, "Error: Cannot use restart mode " "and sync mode together\n"); exit(ERR_PARAM); } if (keytype == KEY_NONE) { hashtype = HASH_NONE; sigtype = SIG_NONE; keyextype = KEYEX_NONE; } if (is_auth_enc(keytype)) { sigtype = SIG_AUTHENC; } if (strcmp(keylenstr, "")) { if (keyextype == KEYEX_ECDH_ECDSA) { ecdsa_curve = get_curve(keylenstr); if (ecdsa_curve == 0) { fprintf(stderr, "Invalid curve\n"); exit(ERR_PARAM); } } else if ((keyextype == KEYEX_RSA) || (keyextype == KEYEX_ECDH_RSA)) { newkeylen = atoi(keylenstr); if ((newkeylen < 512) || (newkeylen > 2048)) { fprintf(stderr, "Invalid new key length\n"); exit(ERR_PARAM); } } } if (filecount != 0) { if (argc > 0) { fprintf(stderr, "Warning: ignoring paths " "specified on command line\n"); } return; } // Read list of files. for (i = 0; i < argc; i++) { if (filecount == MAXFILES) { fprintf(stderr, "Exceeded maximum file count\n"); exit(ERR_PARAM); } strncpy(filelist[filecount], argv[i], sizeof(filelist[0])-1); filelist[filecount][sizeof(filelist[0])-1] = '\x0'; filecount++; } }
/** * Process an incoming KEYINFO message. * Expected in response to a REGISTER when encryption is enabled. */ void handle_keyinfo(struct group_list_t *group, unsigned char *message, unsigned meslen, uint32_t src_id) { struct keyinfo_h *keyinfo_hdr; struct destkey *keylist; int i, keyidx, len, destkeycnt, unauth_keytype, unauth_keylen, unauth_ivlen; unsigned explen, declen; uint8_t decgroupmaster[MASTER_LEN], *prf_buf, *iv; uint64_t ivctr; keyinfo_hdr = (struct keyinfo_h *)message; keylist = (struct destkey *)(message + (keyinfo_hdr->hlen * 4)); if ((meslen < (keyinfo_hdr->hlen * 4U)) || ((keyinfo_hdr->hlen * 4U) < sizeof(struct keyinfo_h))) { glog1(group, "Rejecting KEYINFO from server: invalid message size"); return; } destkeycnt = (meslen - (keyinfo_hdr->hlen * 4)) / sizeof(struct destkey); // This duplicates uid_in_list, but here it's addressed in a struct array for (i = 0, keyidx = -1; (i < destkeycnt) && (keyidx == -1); i++) { if (uid == keylist[i].dest_id) { keyidx = i; } } // Don't use a cipher in an authentication mode to decrypt the group master unauth_keytype = unauth_key(group->keytype); get_key_info(unauth_keytype, &unauth_keylen, &unauth_ivlen); if (keyidx != -1) { glog2(group, "Received KEYINFO"); if (group->phase == PHASE_MIDGROUP) { // We already got the KEYINFO, so no need to reprocess. // Just resend the KEYINFO_ACK and reset the timeout send_keyinfo_ack(group); set_timeout(group, 0); return; } iv = safe_calloc(unauth_ivlen, 1); ivctr = ntohl(keyinfo_hdr->iv_ctr_lo); ivctr |= (uint64_t)ntohl(keyinfo_hdr->iv_ctr_hi) << 32; build_iv(iv, group->salt, unauth_ivlen, uftp_htonll(ivctr), src_id); if (!decrypt_block(unauth_keytype, iv, group->key, NULL, 0, keylist[keyidx].groupmaster, MASTER_LEN, decgroupmaster, &declen) || (declen != MASTER_LEN - 1)) { glog1(group, "Decrypt failed for group master"); send_abort(group, "Decrypt failed for group master"); free(iv); return; } free(iv); group->groupmaster[0] = group->version; memcpy(&group->groupmaster[1], decgroupmaster, declen); explen = group->keylen + SALT_LEN + group->hmaclen; prf_buf = safe_calloc(explen + group->hmaclen, 1); PRF(group->hashtype, explen, group->groupmaster, sizeof(group->groupmaster), "key expansion", group->rand1, sizeof(group->rand1), prf_buf, &len); memcpy(group->grouphmackey, prf_buf, group->hmaclen); memcpy(group->groupkey, prf_buf + group->hmaclen, group->keylen); memcpy(group->groupsalt, prf_buf + group->hmaclen + group->keylen, SALT_LEN); free(prf_buf); group->phase = PHASE_MIDGROUP; send_keyinfo_ack(group); set_timeout(group, 0); if (group->restart) { read_restart_file(group); } } }