static bool init_load_conn(struct ub_ctx *dnsctx, struct starter_config *cfg, struct config_parsed *cfgp, struct section_list *sconn, bool defaultconn, bool resolvip, err_t *perr) { bool connerr; struct starter_conn *conn; starter_log(LOG_LEVEL_DEBUG, "Loading conn %s", sconn->name); conn = alloc_add_conn(cfg, sconn->name); connerr = load_conn(dnsctx, conn, cfgp, sconn, TRUE, defaultconn, resolvip, perr); if (connerr) { starter_log(LOG_LEVEL_INFO, "while loading '%s': %s", sconn->name, *perr); } else { conn->state = STATE_LOADED; } return connerr; }
int starter_stop_pluto (void) { pid_t pid; int i; pid = _pluto_pid; if (pid) { _stop_requested = 1; if (starter_whack_shutdown()==0) { for (i=0; i<20; i++) { usleep(20000); if (_pluto_pid == 0) return 0; } } /** * Be more and more aggressive */ for (i=0; (i<20) && ((pid=_pluto_pid)!=0); i++) { if (i<10) kill(pid, SIGTERM); else kill(pid, SIGKILL); usleep(20000); } if (_pluto_pid == 0) return 0; starter_log(LOG_LEVEL_ERR, "stater_stop_pluto(): can't stop pluto !!!"); return -1; } else { starter_log(LOG_LEVEL_ERR, "stater_stop_pluto(): pluto is not started..."); } return -1; }
int init_load_conn(struct starter_config *cfg , struct config_parsed *cfgp , struct section_list *sconn , bool alsoprocessing , bool defaultconn , bool resolvip , err_t *perr) { int connerr; struct starter_conn *conn; starter_log(LOG_LEVEL_DEBUG, "Loading conn %s", sconn->name); conn = alloc_add_conn(cfg, sconn->name, perr); if(conn == NULL) { return -1; } connerr = load_conn (cfg, conn, cfgp, sconn, TRUE, defaultconn, resolvip, perr); if(connerr != 0) { starter_log(LOG_LEVEL_INFO, "while loading '%s': %s\n", sconn->name, *perr); } if(connerr == 0) { conn->state = STATE_LOADED; } return connerr; }
int starter_ifaces_load (char **ifaces, unsigned int omtu, int nat_t) { char *tmp_phys, *phys; int n; char **i; int sock; int j, found; int ret = 0; starter_log(LOG_LEVEL_DEBUG, "starter_ifaces_load()"); sock = socket(AF_INET, SOCK_DGRAM, 0); if (sock < 0) return -1; for (j=0; j<N_IPSEC_IF; j++) { found = 0; for (i=ifaces; i && *i; i++) { if ((valid_str(*i, &n, &tmp_phys)) && (tmp_phys) && (n>=0) && (n<N_IPSEC_IF)) { if (n==j) { if (found) { starter_log(LOG_LEVEL_ERR, "ignoring duplicate entry for interface ipsec%d", j); } else { found++; phys = _find_physical_iface(sock, tmp_phys); if (phys) { ret += _iface_up (sock, &(_ipsec_if[n]), phys, omtu, nat_t); } else { ret += _iface_down (sock, &(_ipsec_if[n])); } } } } else if (j==0) { /** * Only log in the first loop */ starter_log(LOG_LEVEL_ERR, "ignoring invalid interface '%s'", *i); } } if (!found) ret += _iface_down (sock, &(_ipsec_if[j])); } close(sock); return ret; /* = number of changes - 'whack --listen' if > 0 */ }
static int _iface_down (int sock, struct st_ipsec_if *iface) { struct ifreq req; int ret = 0; iface->up = 0; strncpy(req.ifr_name, iface->name, IFNAMSIZ); if (ioctl(sock, SIOCGIFFLAGS, &req)!=0) { return ret; } if (req.ifr_flags & IFF_UP) { starter_log(LOG_LEVEL_INFO, "shutting down interface %s/%s", iface->name, iface->phys); req.ifr_flags &= ~IFF_UP; ioctl(sock, SIOCSIFFLAGS, &req); ret = 1; } /* unset addr */ memset(&req.ifr_addr, 0, sizeof(req.ifr_addr)); req.ifr_addr.sa_family = AF_INET; ioctl(sock, SIOCSIFADDR, &req); /* tncfg --detach */ ioctl(sock, IPSEC_DEL_DEV, &req); memset(iface->phys, 0, sizeof(iface->phys)); return ret; }
static void _sysflags (char *name, int value) { if (starter_exec( "echo %d >%s/%s 2>/dev/null", value?1:0, PROC_SYSFLAGS, name )!=0) { starter_log(LOG_LEVEL_ERR, "can't set sysflag %s to %d", name, value ? 1 : 0); } }
void starter_pluto_sigchild (pid_t pid) { if (pid == _pluto_pid) { _pluto_pid = 0; if (!_stop_requested) { starter_log(LOG_LEVEL_ERR, "pluto has died -- restart scheduled (%dsec)", PLUTO_RESTART_DELAY); alarm(PLUTO_RESTART_DELAY); // restart in 5 sec } unlink(PID_FILE); } }
void starter_use_log(bool debug, bool console, bool mysyslog) { log_debugging = debug; log_to_console = console; if (mysyslog != log_to_syslog) { if (mysyslog) openlog("ipsec_starter", LOG_PID, LOG_USER); else closelog(); log_to_syslog = mysyslog; } if (log_debugging) starter_log(LOG_LEVEL_ERR, "debugging mode enabled"); }
int starter_exec (const char *fmt, ...) { va_list args; static char buff[BUFF_SIZE]; int r; va_start (args, fmt); vsnprintf(buff, BUFF_SIZE-1, fmt, args); buff[BUFF_SIZE-1] = '\0'; va_end(args); if(showonly) { starter_log(LOG_LEVEL_INFO, "showonly: invoking %s", buff); r = 0; } else { r = system(buff); starter_log(LOG_LEVEL_DEBUG, "starter_exec(%s) = %d", buff, r); } return r; }
int starter_klips_init (void) { struct stat stb; if (stat(PROC_IPSECVERSION,&stb)!=0) { if (stat(PROC_MODULES,&stb)==0) { unsetenv("MODPATH"); unsetenv("MODULECONF"); system("depmod -a >/dev/null 2>&1 && modprobe ipsec"); } if (stat(PROC_IPSECVERSION,&stb)==0) { _klips_module_loaded = 1; } else { starter_log(LOG_LEVEL_ERR, "kernel appears to lack KLIPS"); return 1; } } starter_klips_clear(); return 0; }
static bool load_conn(struct ub_ctx *dnsctx, struct starter_conn *conn, struct config_parsed *cfgp, struct section_list *sl, bool alsoprocessing, bool defaultconn, bool resolvip, err_t *perr) { bool err = FALSE; err |= load_conn_basic(conn, sl, defaultconn ? k_default : k_set, perr); move_comment_list(&conn->comments, &sl->comments); if (err) return err; if (conn->strings[KSF_ALSO] != NULL && !alsoprocessing) { starter_log(LOG_LEVEL_INFO, "also= is not valid in section '%s'", sl->name); return TRUE; /* error */ } /* now, process the also's */ if (conn->alsos) FREE_LIST(conn->alsos); conn->alsos = new_list(conn->strings[KSF_ALSO]); if (alsoprocessing && conn->alsos != NULL) { struct section_list *sl1; /* note: for the duration of this loop body * conn->alsos is migrated to local variable alsos. */ char **alsos = conn->alsos; int alsosize; int alsoplace; conn->alsos = NULL; /* reset all of the "beenhere" flags */ for (sl1 = cfgp->sections.tqh_first; sl1 != NULL; sl1 = sl1->link.tqe_next) sl1->beenhere = FALSE; sl->beenhere = TRUE; /* count them */ for (alsosize = 0; alsos[alsosize] != NULL; alsosize++) ; alsoplace = 0; while (alsoplace < alsosize && alsos[alsoplace] != NULL && alsoplace < ALSO_LIMIT) { /* * for each also= listed, go find this section's keyword list, and * load it as well. This may extend the also= list (and the end), * which we handle by zeroing the also list, and adding to it after * checking for duplicates. */ for (sl1 = cfgp->sections.tqh_first; sl1 != NULL && !streq(alsos[alsoplace], sl1->name); sl1 = sl1->link.tqe_next) ; starter_log(LOG_LEVEL_DEBUG, "\twhile loading conn '%s' also including '%s'", conn->name, alsos[alsoplace]); /* * if we found something that matches by name, and we haven't be * there, then process it. */ if (sl1 && !sl1->beenhere) { conn->strings_set[KSF_ALSO] = FALSE; pfreeany(conn->strings[KSF_ALSO]); conn->strings[KSF_ALSO] = NULL; sl1->beenhere = TRUE; /* translate things, but do not replace earlier settings!*/ err |= translate_conn(conn, sl1, k_set, perr); if (conn->strings[KSF_ALSO] != NULL) { /* now, check out the KSF_ALSO, and extend list if we need to */ char **newalsos = new_list( conn->strings[KSF_ALSO]); if (newalsos != NULL) { char **ra; int newalsoplace; /* count them */ for (newalsoplace = 0; newalsos[newalsoplace] != NULL; newalsoplace++) ; /* extend conn->alss */ ra = alloc_bytes((alsosize + newalsoplace + 1) * sizeof(char *), "conn->alsos"); memcpy(ra, alsos, alsosize * sizeof(char *)); pfree(alsos); alsos = ra; for (newalsoplace = 0; newalsos[newalsoplace] != NULL; newalsoplace++) { assert(conn != NULL); assert(conn->name != NULL); starter_log( LOG_LEVEL_DEBUG, "\twhile processing section '%s' added also=%s", sl1->name, newalsos[newalsoplace]); alsos[alsosize++] = clone_str(newalsos[newalsoplace], "alsos"); } alsos[alsosize] = NULL; } FREE_LIST(newalsos); } } alsoplace++; } /* migrate alsos back to conn->alsos */ conn->alsos = alsos; if (alsoplace >= ALSO_LIMIT) { starter_log(LOG_LEVEL_INFO, "while loading conn '%s', too many also= used at section %s. Limit is %d", conn->name, alsos[alsoplace], ALSO_LIMIT); return TRUE; /* error */ } } #ifdef PARSER_TYPE_DEBUG /* translate strings/numbers into conn items */ starter_log(LOG_LEVEL_DEBUG, "#checking options_set[KBF_TYPE,%d]=%d %d", KBF_TYPE, conn->options_set[KBF_TYPE], conn->options[KBF_TYPE]); #endif if (conn->options_set[KBF_TYPE]) { switch ((enum keyword_satype)conn->options[KBF_TYPE]) { case KS_TUNNEL: conn->policy |= POLICY_TUNNEL; conn->policy &= ~POLICY_SHUNT_MASK; break; case KS_TRANSPORT: conn->policy &= ~POLICY_TUNNEL; conn->policy &= ~POLICY_SHUNT_MASK; break; case KS_PASSTHROUGH: conn->policy &= ~(POLICY_ENCRYPT | POLICY_AUTHENTICATE | POLICY_TUNNEL | POLICY_RSASIG); conn->policy &= ~POLICY_SHUNT_MASK; conn->policy |= POLICY_SHUNT_PASS; break; case KS_DROP: conn->policy &= ~(POLICY_ENCRYPT | POLICY_AUTHENTICATE | POLICY_TUNNEL | POLICY_RSASIG); conn->policy &= ~POLICY_SHUNT_MASK; conn->policy |= POLICY_SHUNT_DROP; break; case KS_REJECT: conn->policy &= ~(POLICY_ENCRYPT | POLICY_AUTHENTICATE | POLICY_TUNNEL | POLICY_RSASIG); conn->policy &= ~POLICY_SHUNT_MASK; conn->policy |= POLICY_SHUNT_REJECT; break; } } if (conn->options_set[KBF_FAILURESHUNT]) { conn->policy &= ~POLICY_FAIL_MASK; switch(conn->options[KBF_FAILURESHUNT]) { case KFS_FAIL_NONE: conn->policy |= POLICY_FAIL_NONE; break; case KFS_FAIL_PASS: conn->policy |= POLICY_FAIL_PASS; break; case KFS_FAIL_DROP: conn->policy |= POLICY_FAIL_DROP; break; case KFS_FAIL_REJECT: conn->policy |= POLICY_FAIL_REJECT; break; } } if (conn->options_set[KBF_NEGOTIATIONSHUNT]) { switch(conn->options[KBF_NEGOTIATIONSHUNT]) { case KNS_FAIL_PASS: conn->policy |= POLICY_NEGO_PASS; break; case KNS_FAIL_DROP: conn->policy &= ~POLICY_NEGO_PASS; break; } } KW_POLICY_FLAG(KBF_COMPRESS, POLICY_COMPRESS); KW_POLICY_FLAG(KBF_PFS, POLICY_PFS); /* reset authby flags */ if (conn->options_set[KBF_AUTHBY]) { conn->policy &= ~(POLICY_ID_AUTH_MASK); #ifdef FIPS_CHECK if (libreswan_fipsmode()) { if (LIN(POLICY_PSK, conn->options[KBF_AUTHBY])) { starter_log(LOG_LEVEL_INFO, "while loading conn '%s', PSK not allowed in FIPS mode with NSS", conn->name); return TRUE; /* error */ } } #endif conn->policy |= conn->options[KBF_AUTHBY]; #ifdef STARTER_POLICY_DEBUG starter_log(LOG_LEVEL_DEBUG, "%s: setting conn->policy=%08x (%08x)", conn->name, (unsigned int)conn->policy, conn->options[KBF_AUTHBY]); #endif } KW_POLICY_NEGATIVE_FLAG(KBF_IKEPAD, POLICY_NO_IKEPAD); KW_POLICY_NEGATIVE_FLAG(KBF_REKEY, POLICY_DONT_REKEY); KW_POLICY_FLAG(KBF_AGGRMODE, POLICY_AGGRESSIVE); KW_POLICY_FLAG(KBF_MODECONFIGPULL, POLICY_MODECFG_PULL); KW_POLICY_FLAG(KBF_OVERLAPIP, POLICY_OVERLAPIP); KW_POLICY_FLAG(KBF_IKEv2_ALLOW_NARROWING, POLICY_IKEV2_ALLOW_NARROWING); KW_POLICY_FLAG(KBF_IKEv2_PAM_AUTHORIZE, POLICY_IKEV2_PAM_AUTHORIZE); if (conn->strings_set[KSF_ESP]) conn->esp = clone_str(conn->strings[KSF_ESP],"KSF_ESP"); #ifdef HAVE_LABELED_IPSEC if (conn->strings_set[KSF_POLICY_LABEL]) conn->policy_label = clone_str(conn->strings[KSF_POLICY_LABEL],"KSF_POLICY_LABEL"); if (conn->policy_label != NULL) starter_log(LOG_LEVEL_DEBUG, "connection's policy label: %s", conn->policy_label); #endif if (conn->strings_set[KSF_IKE]) conn->ike = clone_str(conn->strings[KSF_IKE],"KSF_IKE"); if (conn->strings_set[KSF_MODECFGDNS1]) { conn->modecfg_dns1 = clone_str(conn->strings[KSF_MODECFGDNS1],"KSF_MODECFGDNS1"); } if (conn->strings_set[KSF_MODECFGDNS2]) { conn->modecfg_dns2 = clone_str(conn->strings[KSF_MODECFGDNS2], "KSF_MODECFGDNS2"); } if (conn->strings_set[KSF_MODECFGDOMAIN]) { conn->modecfg_domain = clone_str(conn->strings[KSF_MODECFGDOMAIN],"KSF_MODECFGDOMAIN"); } if (conn->strings_set[KSF_MODECFGBANNER]) { conn->modecfg_banner = clone_str(conn->strings[KSF_MODECFGBANNER],"KSF_MODECFGBANNER"); } if (conn->strings_set[KSF_CONNALIAS]) conn->connalias = clone_str(conn->strings[KSF_CONNALIAS],"KSF_CONNALIAS"); if (conn->options_set[KBF_PHASE2]) { conn->policy &= ~(POLICY_AUTHENTICATE | POLICY_ENCRYPT); conn->policy |= conn->options[KBF_PHASE2]; } if (conn->options_set[KBF_IKEv2]) { lset_t policy = LEMPTY; switch (conn->options[KBF_IKEv2]) { case fo_never: policy = POLICY_IKEV1_ALLOW; break; case fo_permit: /* this is the default for now */ policy = POLICY_IKEV1_ALLOW | POLICY_IKEV2_ALLOW; break; case fo_propose: policy = POLICY_IKEV1_ALLOW | POLICY_IKEV2_ALLOW | POLICY_IKEV2_PROPOSE; break; case fo_insist: policy = POLICY_IKEV2_ALLOW | POLICY_IKEV2_PROPOSE; break; } conn->policy = (conn->policy & ~POLICY_IKEV2_MASK) | policy; } if (conn->options_set[KBF_IKE_FRAG]) { switch (conn->options[KBF_IKE_FRAG]) { case ynf_no: conn->policy &= ~POLICY_IKE_FRAG_ALLOW; conn->policy &= ~POLICY_IKE_FRAG_FORCE; break; case ynf_yes: /* this is the default */ conn->policy |= POLICY_IKE_FRAG_ALLOW; break; case ynf_force: conn->policy |= POLICY_IKE_FRAG_ALLOW | POLICY_IKE_FRAG_FORCE; break; } } if (conn->options_set[KBF_SAREFTRACK]) { switch (conn->options[KBF_SAREFTRACK]) { case sat_yes: /* this is the default */ conn->policy |= POLICY_SAREF_TRACK; break; case sat_conntrack: conn->policy |= POLICY_SAREF_TRACK | POLICY_SAREF_TRACK_CONNTRACK; break; case sat_no: conn->policy &= ~POLICY_SAREF_TRACK; conn->policy &= ~POLICY_SAREF_TRACK_CONNTRACK; break; } } err |= validate_end(dnsctx, conn, &conn->left, "left", resolvip, perr); err |= validate_end(dnsctx, conn, &conn->right, "right", resolvip, perr); /* * TODO: * verify both ends are using the same inet family, if one end * is "%any" or "%defaultroute", then perhaps adjust it. * ensource this for left,leftnexthop,right,rightnexthop * Ideally, phase out connaddrfamily= which now wrongly assumes * left,leftnextop,leftsubnet are the same inet family * Currently, these tests are implicitely done, and wrongly * in case of 6in4 and 4in6 tunnels */ if (conn->options_set[KBF_AUTO]) conn->desired_state = conn->options[KBF_AUTO]; return err; }
/** * Take keywords from ipsec.conf syntax and load into a conn struct * * * @param conn a connection definition * @param sl a section_list * @param assigned_value is set to either k_set, or k_default. * k_default is used when we are loading a conn that should be * considered to be a "default" value, and that replacing this * value is considered acceptable. * @return bool TRUE if unsuccessfull */ static bool translate_conn(struct starter_conn *conn, struct section_list *sl, enum keyword_set assigned_value, err_t *error) { bool err = FALSE; struct kw_list *kw; for (kw = sl->kw; kw; kw = kw->next) { ksf *the_strings = &conn->strings; str_set *set_strings = &conn->strings_set; knf *the_options = &conn->options; int_set *set_options = &conn->options_set; unsigned int field; if ((kw->keyword.keydef->validity & kv_conn) == 0) { /* this isn't valid in a conn! */ *error = (const char *)tmp_err; snprintf(tmp_err, sizeof(tmp_err), "keyword '%s' is not valid in a conn (%s)\n", kw->keyword.keydef->keyname, sl->name); starter_log(LOG_LEVEL_INFO, "%s", tmp_err); continue; } if (kw->keyword.keydef->validity & kv_leftright) { if (kw->keyword.keyleft) { the_strings = &conn->left.strings; the_options = &conn->left.options; set_strings = &conn->left.strings_set; set_options = &conn->left.options_set; } else { the_strings = &conn->right.strings; the_options = &conn->right.options; set_strings = &conn->right.strings_set; set_options = &conn->right.options_set; } } field = kw->keyword.keydef->field; #ifdef PARSER_TYPE_DEBUG starter_log(LOG_LEVEL_DEBUG, "#analyzing %s[%d] kwtype=%d", kw->keyword.keydef->keyname, field, kw->keyword.keydef->type); #endif assert(kw->keyword.keydef != NULL); switch (kw->keyword.keydef->type) { case kt_string: case kt_filename: case kt_dirname: case kt_bitstring: case kt_ipaddr: case kt_range: case kt_subnet: case kt_idtype: /* all treated as strings for now */ assert(kw->keyword.keydef->field < KEY_STRINGS_MAX); if ((*set_strings)[field] == k_set) { *error = tmp_err; snprintf(tmp_err, sizeof(tmp_err), "duplicate key '%s' in conn %s while processing def %s", kw->keyword.keydef->keyname, conn->name, sl->name); starter_log(LOG_LEVEL_INFO, "%s", tmp_err); if (kw->keyword.string == NULL || (*the_strings)[field] == NULL || !streq(kw->keyword.string, (*the_strings)[field])) { err = TRUE; break; } } pfreeany((*the_strings)[field]); if (kw->string == NULL) { *error = tmp_err; snprintf(tmp_err, sizeof(tmp_err), "Invalid %s value", kw->keyword.keydef->keyname); err = TRUE; break; } (*the_strings)[field] = clone_str(kw->string,"kt_idtype kw->string"); (*set_strings)[field] = assigned_value; break; case kt_appendstring: case kt_appendlist: /* implicitly, this field can have multiple values */ assert(kw->keyword.keydef->field < KEY_STRINGS_MAX); if ((*the_strings)[field] == NULL) { (*the_strings)[field] = clone_str(kw->string, "kt_appendlist kw->string"); } else { char *s = (*the_strings)[field]; size_t old_len = strlen(s); /* excludes '\0' */ size_t new_len = strlen(kw->string); char *n; n = alloc_bytes(old_len + 1 + new_len + 1, "kt_appendlist"); memcpy(n, s, old_len); n[old_len] = ' '; memcpy(n + old_len + 1, kw->string, new_len + 1); /* includes '\0' */ (*the_strings)[field] = n; pfree(s); } (*set_strings)[field] = TRUE; break; case kt_rsakey: case kt_loose_enum: assert(field < KEY_STRINGS_MAX); assert(field < KEY_NUMERIC_MAX); if ((*set_options)[field] == k_set) { *error = tmp_err; snprintf(tmp_err, sizeof(tmp_err), "duplicate key '%s' in conn %s while processing def %s", kw->keyword.keydef->keyname, conn->name, sl->name); starter_log(LOG_LEVEL_INFO, "%s", tmp_err); /* only fatal if we try to change values */ if ((*the_options)[field] != (int)kw->number || !((*the_options)[field] == LOOSE_ENUM_OTHER && kw->number == LOOSE_ENUM_OTHER && kw->keyword.string != NULL && (*the_strings)[field] != NULL && streq(kw->keyword.string, (*the_strings)[field]))) { err = TRUE; break; } } (*the_options)[field] = kw->number; if (kw->number == LOOSE_ENUM_OTHER) { assert(kw->keyword.string != NULL); pfreeany((*the_strings)[field]); (*the_strings)[field] = clone_str( kw->keyword.string, "kt_loose_enum kw->keyword.string"); } (*set_options)[field] = assigned_value; break; case kt_list: case kt_bool: case kt_invertbool: case kt_enum: case kt_number: case kt_time: case kt_percent: /* all treated as a number for now */ assert(field < KEY_NUMERIC_MAX); if ((*set_options)[field] == k_set) { *error = tmp_err; snprintf(tmp_err, sizeof(tmp_err), "duplicate key '%s' in conn %s while processing def %s", kw->keyword.keydef->keyname, conn->name, sl->name); starter_log(LOG_LEVEL_INFO, "%s", tmp_err); if ((*the_options)[field] != (int)kw->number) { err = TRUE; break; } } #if 0 starter_log(LOG_LEVEL_DEBUG, "#setting %s[%d]=%u", kw->keyword.keydef->keyname, field, kw->number); #endif (*the_options)[field] = kw->number; (*set_options)[field] = assigned_value; break; case kt_comment: break; case kt_obsolete: starter_log(LOG_LEVEL_INFO, "Warning: obsolete keyword '%s' ignored", kw->keyword.keydef->keyname); break; case kt_obsolete_quiet: starter_log(LOG_LEVEL_DEBUG, "Warning: obsolete keyword '%s' ignored", kw->keyword.keydef->keyname); break; } } return err; }
static bool validate_end(struct ub_ctx *dnsctx , #endif struct starter_conn *conn_st, struct starter_end *end, const char *leftright, bool resolvip UNUSED, err_t *perr) { err_t er = NULL; char *err_str = NULL; int family = conn_st->options[KBF_CONNADDRFAMILY]; bool err = FALSE; # define ERR_FOUND(...) { err |= error_append(&err_str, __VA_ARGS__); } if (!end->options_set[KNCF_IP]) conn_st->state = STATE_INCOMPLETE; end->addrtype = end->options[KNCF_IP]; end->addr_family = family; /* validate the KSCF_IP/KNCF_IP */ switch (end->addrtype) { case KH_ANY: anyaddr(family, &(end->addr)); break; case KH_IFACE: /* generally, this doesn't show up at this stage */ starter_log(LOG_LEVEL_DEBUG, "starter: %s is KH_IFACE", leftright); break; case KH_IPADDR: assert(end->strings[KSCF_IP] != NULL); if (end->strings[KSCF_IP][0] == '%') { pfree(end->iface); end->iface = clone_str(end->strings[KSCF_IP] + 1, "KH_IPADDR end->iface"); if (!starter_iface_find(end->iface, family, &end->addr, &end->nexthop)) conn_st->state = STATE_INVALID; /* not numeric, so set the type to the iface type */ end->addrtype = KH_IFACE; break; } er = ttoaddr_num(end->strings[KNCF_IP], 0, family, &(end->addr)); if (er != NULL) { /* not numeric, so set the type to the string type */ end->addrtype = KH_IPHOSTNAME; } if (end->id == NULL) { ipstr_buf b; end->id = clone_str(ipstr(&end->addr, &b), "end if"); } break; case KH_OPPO: conn_st->policy |= POLICY_OPPORTUNISTIC; break; case KH_OPPOGROUP: conn_st->policy |= POLICY_OPPORTUNISTIC | POLICY_GROUP; break; case KH_GROUP: conn_st->policy |= POLICY_GROUP; break; case KH_IPHOSTNAME: /* generally, this doesn't show up at this stage */ starter_log(LOG_LEVEL_DEBUG, "starter: %s is KH_IPHOSTNAME", leftright); break; case KH_DEFAULTROUTE: starter_log(LOG_LEVEL_DEBUG, "starter: %s is KH_DEFAULTROUTE", leftright); break; case KH_NOTSET: starter_log(LOG_LEVEL_DEBUG, "starter: %s is KH_NOTSET", leftright); break; } /* validate the KSCF_SUBNET */ if (end->strings_set[KSCF_SUBNET]) { char *value = end->strings[KSCF_SUBNET]; if (end->strings_set[KSCF_ADDRESSPOOL]) { ERR_FOUND("cannot specify both %ssubnet= and %saddresspool=", leftright, leftright); } if (startswith(value, "vhost:") || startswith(value, "vnet:")) { er = NULL; end->virt = clone_str(value, "validate_end item"); } else { end->has_client = TRUE; er = ttosubnet(value, 0, family, &(end->subnet)); } if (er != NULL) ERR_FOUND("bad subnet %ssubnet=%s [%s]", leftright, value, er); } /* set nexthop address to something consistent, by default */ anyaddr(family, &end->nexthop); anyaddr(addrtypeof(&end->addr), &end->nexthop); /* validate the KSCF_NEXTHOP */ if (end->strings_set[KSCF_NEXTHOP]) { char *value = end->strings[KSCF_NEXTHOP]; if (strcaseeq(value, "%defaultroute")) { end->nexttype = KH_DEFAULTROUTE; } else { if (tnatoaddr(value, strlen(value), AF_INET, &(end->nexthop)) != NULL && tnatoaddr(value, strlen(value), AF_INET6, &(end->nexthop)) != NULL) { #ifdef DNSSEC starter_log(LOG_LEVEL_DEBUG, "Calling unbound_resolve() for %snexthop value", leftright); if (!unbound_resolve(dnsctx, value, strlen(value), AF_INET, &(end->nexthop)) && !unbound_resolve(dnsctx, value, strlen(value), AF_INET6, &(end->nexthop))) ERR_FOUND("bad value for %snexthop=%s\n", leftright, value); #else er = ttoaddr(value, 0, family, &(end->nexthop)); if (er != NULL) ERR_FOUND("bad value for %snexthop=%s [%s]", leftright, value, er); #endif } end->nexttype = KH_IPADDR; } } else { #if 0 if (conn_st->policy & POLICY_OPPORTUNISTIC) end->nexttype = KH_DEFAULTROUTE; #endif anyaddr(family, &end->nexthop); if (end->addrtype == KH_DEFAULTROUTE) { end->nexttype = KH_DEFAULTROUTE; } } /* validate the KSCF_ID */ if (end->strings_set[KSCF_ID]) { char *value = end->strings[KSCF_ID]; pfreeany(end->id); end->id = clone_str(value, "end->id"); } if (end->options_set[KSCF_RSAKEY1]) { end->rsakey1_type = end->options[KSCF_RSAKEY1]; end->rsakey2_type = end->options[KSCF_RSAKEY2]; switch (end->options[KSCF_RSAKEY1]) { case PUBKEY_DNS: case PUBKEY_DNSONDEMAND: end->key_from_DNS_on_demand = TRUE; break; default: end->key_from_DNS_on_demand = FALSE; /* validate the KSCF_RSAKEY1/RSAKEY2 */ if (end->strings[KSCF_RSAKEY1] != NULL) { char *value = end->strings[KSCF_RSAKEY1]; pfreeany(end->rsakey1); end->rsakey1 = (unsigned char *)clone_str(value,"end->rsakey1"); } if (end->strings[KSCF_RSAKEY2] != NULL) { char *value = end->strings[KSCF_RSAKEY2]; pfreeany(end->rsakey2); end->rsakey2 = (unsigned char *)clone_str(value,"end->rsakey2"); } } } /* validate the KSCF_SOURCEIP, if any, and if set, * set the subnet to same value, if not set. */ if (end->strings_set[KSCF_SOURCEIP]) { char *value = end->strings[KSCF_SOURCEIP]; if (tnatoaddr(value, strlen(value), AF_INET, &(end->sourceip)) != NULL && tnatoaddr(value, strlen(value), AF_INET6, &(end->sourceip)) != NULL) { #ifdef DNSSEC starter_log(LOG_LEVEL_DEBUG, "Calling unbound_resolve() for %ssourceip value", leftright); if (!unbound_resolve(dnsctx, value, strlen(value), AF_INET, &(end->sourceip)) && !unbound_resolve(dnsctx, value, strlen(value), AF_INET6, &(end->sourceip))) ERR_FOUND("bad value for %ssourceip=%s\n", leftright, value); #else er = ttoaddr(value, 0, family, &(end->sourceip)); if (er != NULL) ERR_FOUND("bad addr %ssourceip=%s [%s]", leftright, value, er); #endif } else { er = tnatoaddr(value, 0, family, &(end->sourceip)); if (er != NULL) ERR_FOUND("bad numerical addr %ssourceip=%s [%s]", leftright, value, er); } if (!end->has_client) { starter_log(LOG_LEVEL_INFO, "%ssourceip= used but not %ssubnet= defined, defaulting %ssubnet to %s", leftright, leftright, leftright, value); er = addrtosubnet(&end->sourceip, &end->subnet); if (er != NULL) { ERR_FOUND("attempt to default %ssubnet from %s failed: %s", leftright, value, er); } end->has_client = TRUE; end->has_client_wildcard = FALSE; } } /* copy certificate path name */ if (end->strings_set[KSCF_CERT]) end->cert = clone_str(end->strings[KSCF_CERT], "KSCF_CERT"); if (end->strings_set[KSCF_CA]) end->ca = clone_str(end->strings[KSCF_CA], "KSCF_CA"); if (end->strings_set[KSCF_UPDOWN]) end->updown = clone_str(end->strings[KSCF_UPDOWN], "KSCF_UPDOWN"); if (end->strings_set[KSCF_PROTOPORT]) { err_t ugh; char *value = end->strings[KSCF_PROTOPORT]; ugh = ttoprotoport(value, 0, &end->protocol, &end->port, &end->has_port_wildcard); if (ugh != NULL) ERR_FOUND("bad %sprotoport=%s [%s]", leftright, value, ugh); } if (end->strings_set[KSCF_ADDRESSPOOL]) { char *addresspool = end->strings[KSCF_ADDRESSPOOL]; if (end->strings_set[KSCF_SUBNET]) ERR_FOUND("cannot specify both %ssubnet= and %saddresspool=", leftright, leftright); starter_log(LOG_LEVEL_DEBUG, "connection's %saddresspool set to: %s", leftright, end->strings[KSCF_ADDRESSPOOL] ); er = ttorange(addresspool, 0, AF_INET, &end->pool_range, TRUE); if (er != NULL) ERR_FOUND("bad %saddresspool=%s [%s]", leftright, addresspool, er); } if (end->options_set[KNCF_XAUTHSERVER] || end->options_set[KNCF_XAUTHCLIENT]) conn_st->policy |= POLICY_XAUTH; /* KSCF_SUBNETWITHIN --- not sure what to do with it. KSCF_ESPENCKEY --- todo (manual keying) KSCF_ESPAUTHKEY --- todo (manual keying) KSCF_SOURCEIP = 16, KSCF_MAX = 19 */ if (err) *perr = err_str; return err; # undef ERR_FOUND }
/** * Load a parsed config * * @param cfg starter_config structure * @param cfgp config_parsed (ie: valid) struct * @param perr pointer to store errors in * @return bool TRUE if unsuccessfull */ static bool load_setup(struct starter_config *cfg, struct config_parsed *cfgp) { bool err = FALSE; struct kw_list *kw; for (kw = cfgp->config_setup; kw; kw = kw->next) { /** * the parser already made sure that only config keywords were used, * but we double check! */ assert(kw->keyword.keydef->validity & kv_config); switch (kw->keyword.keydef->type) { case kt_string: case kt_filename: case kt_dirname: case kt_loose_enum: /* all treated as strings for now */ assert(kw->keyword.keydef->field < sizeof(cfg->setup.strings)); pfreeany(cfg->setup.strings[kw->keyword.keydef-> field]); cfg->setup.strings[kw->keyword.keydef->field] = clone_str(kw->string, "kt_loose_enum kw->string"); cfg->setup.strings_set[kw->keyword.keydef->field] = TRUE; break; case kt_list: case kt_bool: case kt_invertbool: case kt_enum: case kt_number: case kt_time: case kt_percent: /* all treated as a number for now */ assert(kw->keyword.keydef->field < sizeof(cfg->setup.options)); cfg->setup.options[kw->keyword.keydef->field] = kw->number; cfg->setup.options_set[kw->keyword.keydef->field] = TRUE; break; case kt_bitstring: case kt_rsakey: case kt_ipaddr: case kt_subnet: case kt_range: case kt_idtype: err = TRUE; break; case kt_comment: break; case kt_obsolete: starter_log(LOG_LEVEL_INFO, "Warning: ignored obsolete keyword '%s'", kw->keyword.keydef->keyname); break; case kt_obsolete_quiet: starter_log(LOG_LEVEL_DEBUG, "Warning: ignored obsolete keyword '%s'", kw->keyword.keydef->keyname); break; default: /* NEVER HAPPENS */ break; } } /* now process some things with specific values */ /* interfaces has to be chopped up */ if (cfg->setup.interfaces) FREE_LIST(cfg->setup.interfaces); cfg->setup.interfaces = new_list(cfg->setup.strings[KSF_INTERFACES]); return err; }
struct starter_config *confread_load(const char *file, err_t *perr, bool resolvip, const char *ctlbase, bool setuponly) { struct starter_config *cfg = NULL; struct config_parsed *cfgp; struct section_list *sconn; bool err = FALSE; bool connerr; #ifdef DNSSEC struct ub_ctx *dnsctx = ub_ctx_create(); unbound_init(dnsctx); #else struct ub_ctx *dnsctx = NULL; #endif /** * Load file */ cfgp = parser_load_conf(file, perr); if (!cfgp) return NULL; cfg = (struct starter_config *)alloc_bytes(sizeof(struct starter_config),"starter_config cfg"); /** * Set default values */ ipsecconf_default_values(cfg); if (ctlbase) { pfree(cfg->ctlbase); cfg->ctlbase = clone_str(ctlbase, "control socket"); } /** * Load setup */ err |= load_setup(cfg, cfgp); if (err) { parser_free_conf(cfgp); confread_free(cfg); return NULL; } if (!setuponly) { /** * Find %default and %oedefault conn * */ for (sconn = cfgp->sections.tqh_first; (!err) && sconn != NULL; sconn = sconn->link.tqe_next) { if (streq(sconn->name, "%default")) { starter_log(LOG_LEVEL_DEBUG, "Loading default conn"); err |= load_conn(dnsctx, &cfg->conn_default, cfgp, sconn, FALSE, /*default conn*/ TRUE, resolvip, perr); } if (streq(sconn->name, "%oedefault")) { starter_log(LOG_LEVEL_DEBUG, "Loading oedefault conn"); err |= load_conn(dnsctx, &cfg->conn_oedefault, cfgp, sconn, FALSE, /*default conn*/ TRUE, resolvip, perr); if (!err) cfg->got_oedefault = TRUE; } } /** * Load other conns */ for (sconn = cfgp->sections.tqh_first; sconn != NULL; sconn = sconn->link.tqe_next) { if (streq(sconn->name, "%default")) continue; if (streq(sconn->name, "%oedefault")) continue; connerr = init_load_conn(dnsctx, cfg, cfgp, sconn, FALSE, resolvip, perr); #if 0 /* ??? the following condition can never be true */ if (connerr == -1) { parser_free_conf(cfgp); confread_free(cfg); return NULL; } #endif err |= connerr; } /* if we have OE on, then create any missing OE conns! */ if (cfg->setup.options[KBF_OPPOENCRYPT]) { starter_log(LOG_LEVEL_DEBUG, "Enabling OE conns"); add_any_oeconns(cfg, cfgp); } } parser_free_conf(cfgp); return cfg; }
/** * Take keywords from ipsec.conf syntax and load into a conn struct * * @param conn a connection definition * @param sl a section_list * @param assigned_value is set to either k_set, or k_default. * k_default is used when we are loading a conn that should be * considered to be a "default" value, and that replacing this * value is considered acceptable. * @return bool TRUE if unsuccessful */ static bool translate_conn(struct starter_conn *conn, const struct section_list *sl, enum keyword_set assigned_value, err_t *error) { /* * tmp_err must be able to carry an error message back to our caller. * Thus it must be static. * Great discipline is required to make sure that at most one error * message needs to persist. */ static char tmp_err[512]; bool err = FALSE; const struct kw_list *kw; for (kw = sl->kw; kw; kw = kw->next) { ksf *the_strings = &conn->strings; str_set *set_strings = &conn->strings_set; knf *the_options = &conn->options; int_set *set_options = &conn->options_set; if ((kw->keyword.keydef->validity & kv_conn) == 0) { /* this isn't valid in a conn! */ /* ??? pray nobody else wants to use tmp_err */ *error = tmp_err; snprintf(tmp_err, sizeof(tmp_err), "keyword '%s' is not valid in a conn (%s)\n", kw->keyword.keydef->keyname, sl->name); starter_log(LOG_LEVEL_INFO, "%s", tmp_err); continue; } if (kw->keyword.keydef->validity & kv_leftright) { struct starter_end *this = kw->keyword.keyleft ? &conn->left : &conn->right; the_strings = &this->strings; the_options = &this->options; set_strings = &this->strings_set; set_options = &this->options_set; } unsigned int field = kw->keyword.keydef->field; assert(kw->keyword.keydef != NULL); switch (kw->keyword.keydef->type) { case kt_string: case kt_filename: case kt_dirname: case kt_bitstring: case kt_ipaddr: case kt_range: case kt_subnet: case kt_idtype: /* all treated as strings for now */ assert(kw->keyword.keydef->field < KEY_STRINGS_MAX); if ((*set_strings)[field] == k_set) { /* ??? pray nobody else wants to use tmp_err */ *error = tmp_err; snprintf(tmp_err, sizeof(tmp_err), "duplicate key '%s' in conn %s while processing def %s", kw->keyword.keydef->keyname, conn->name, sl->name); starter_log(LOG_LEVEL_INFO, "%s", tmp_err); if (kw->keyword.string == NULL || (*the_strings)[field] == NULL || !streq(kw->keyword.string, (*the_strings)[field])) { err = TRUE; break; } } pfreeany((*the_strings)[field]); if (kw->string == NULL) { /* ??? pray nobody else wants to use tmp_err */ *error = tmp_err; snprintf(tmp_err, sizeof(tmp_err), "Invalid %s value", kw->keyword.keydef->keyname); err = TRUE; break; } (*the_strings)[field] = clone_str(kw->string,"kt_idtype kw->string"); (*set_strings)[field] = assigned_value; break; case kt_appendstring: case kt_appendlist: /* implicitly, this field can have multiple values */ assert(kw->keyword.keydef->field < KEY_STRINGS_MAX); if ((*the_strings)[field] == NULL) { (*the_strings)[field] = clone_str(kw->string, "kt_appendlist kw->string"); } else { char *s = (*the_strings)[field]; size_t old_len = strlen(s); /* excludes '\0' */ size_t new_len = strlen(kw->string); char *n; n = alloc_bytes(old_len + 1 + new_len + 1, "kt_appendlist"); memcpy(n, s, old_len); n[old_len] = ' '; memcpy(n + old_len + 1, kw->string, new_len + 1); /* includes '\0' */ (*the_strings)[field] = n; pfree(s); } (*set_strings)[field] = TRUE; break; case kt_rsakey: case kt_loose_enum: assert(field < KEY_STRINGS_MAX); assert(field < KEY_NUMERIC_MAX); if ((*set_options)[field] == k_set) { /* ??? pray nobody else wants to use tmp_err */ *error = tmp_err; snprintf(tmp_err, sizeof(tmp_err), "duplicate key '%s' in conn %s while processing def %s", kw->keyword.keydef->keyname, conn->name, sl->name); starter_log(LOG_LEVEL_INFO, "%s", tmp_err); /* only fatal if we try to change values */ if ((*the_options)[field] != (int)kw->number || !((*the_options)[field] == LOOSE_ENUM_OTHER && kw->number == LOOSE_ENUM_OTHER && kw->keyword.string != NULL && (*the_strings)[field] != NULL && streq(kw->keyword.string, (*the_strings)[field]))) { err = TRUE; break; } /* ??? at this point, we have set *error but not err! */ } (*the_options)[field] = kw->number; if (kw->number == LOOSE_ENUM_OTHER) { assert(kw->keyword.string != NULL); pfreeany((*the_strings)[field]); (*the_strings)[field] = clone_str( kw->keyword.string, "kt_loose_enum kw->keyword.string"); } (*set_options)[field] = assigned_value; break; case kt_list: case kt_bool: case kt_invertbool: case kt_enum: case kt_number: case kt_time: case kt_percent: /* all treated as a number for now */ assert(field < KEY_NUMERIC_MAX); if ((*set_options)[field] == k_set) { /* ??? pray nobody else wants to use tmp_err */ *error = tmp_err; snprintf(tmp_err, sizeof(tmp_err), "duplicate key '%s' in conn %s while processing def %s", kw->keyword.keydef->keyname, conn->name, sl->name); starter_log(LOG_LEVEL_INFO, "%s", tmp_err); if ((*the_options)[field] != (int)kw->number) { err = TRUE; break; } /* ??? at this point, we have set *error but not err! */ } #if 0 starter_log(LOG_LEVEL_DEBUG, "#setting %s[%d]=%u", kw->keyword.keydef->keyname, field, kw->number); #endif (*the_options)[field] = kw->number; (*set_options)[field] = assigned_value; break; case kt_comment: break; case kt_obsolete: starter_log(LOG_LEVEL_INFO, "Warning: obsolete keyword '%s' ignored", kw->keyword.keydef->keyname); break; case kt_obsolete_quiet: starter_log(LOG_LEVEL_DEBUG, "Warning: obsolete keyword '%s' ignored", kw->keyword.keydef->keyname); break; } } return err; }
static int load_conn (struct starter_config *cfg , struct starter_conn *conn , struct config_parsed *cfgp , struct section_list *sl , bool alsoprocessing , bool defaultconn , bool resolvip , err_t *perr) { unsigned int err; err = 0; err += load_conn_basic(conn, sl, defaultconn ? k_default : k_set, perr); move_comment_list(&conn->comments, &sl->comments); if(err) return err; if(conn->strings[KSF_ALSO] != NULL && !alsoprocessing) { starter_log(LOG_LEVEL_INFO , "also= is not valid in section '%s'" , sl->name); return 1; } /* now, process the also's */ if (conn->alsos) free_list(conn->alsos); conn->alsos = new_list(conn->strings[KSF_ALSO]); if(alsoprocessing && conn->alsos) { unsigned int alsosize; char **alsos; struct section_list *sl1; /* reset all of the "beenhere" flags: can not also= and alsoflip= the same conn, btw. */ for(sl1 = cfgp->sections.tqh_first; sl1 != NULL; sl1 = sl1->link.tqe_next) { sl1->beenhere = FALSE; } sl->beenhere = TRUE; /* count them */ alsos = conn->alsos; conn->alsos = NULL; for(alsosize=0; alsos[alsosize]!=NULL; alsosize++); starter_log(LOG_LEVEL_DEBUG, "# conn %s processing alsos", conn->name); conn->alsos = process_alsos(cfg, conn, cfgp, alsos, alsosize, FALSE, perr); if(conn->strings[KSF_ALSOFLIP]) { alsos = new_list(conn->strings[KSF_ALSOFLIP]); for(alsosize=0; alsos[alsosize]!=NULL; alsosize++); starter_log(LOG_LEVEL_DEBUG, "# conn %s processing alsoflips", conn->name); conn->also_flips = process_alsos(cfg, conn, cfgp, alsos, alsosize, TRUE, perr); } } #ifdef PARSER_TYPE_DEBUG /* translate strings/numbers into conn items */ starter_log(LOG_LEVEL_DEBUG, "#checking options_set[KBF_TYPE,%d]=%d %d\n", KBF_TYPE, conn->options_set[KBF_TYPE], conn->options[KBF_TYPE]); #endif if(conn->options_set[KBF_TYPE]) { switch((enum keyword_satype)conn->options[KBF_TYPE]) { case KS_TUNNEL: conn->policy |= POLICY_TUNNEL; conn->policy &= ~POLICY_SHUNT_MASK; break; case KS_TRANSPORT: conn->policy &= ~POLICY_TUNNEL; conn->policy &= ~POLICY_SHUNT_MASK; break; case KS_UDPENCAP: /* no way to specify this yet! */ break; case KS_PASSTHROUGH: conn->policy &= ~(POLICY_ENCRYPT|POLICY_AUTHENTICATE|POLICY_TUNNEL|POLICY_RSASIG); conn->policy &= ~POLICY_SHUNT_MASK; conn->policy |= POLICY_SHUNT_PASS; break; case KS_DROP: conn->policy &= ~(POLICY_ENCRYPT|POLICY_AUTHENTICATE|POLICY_TUNNEL|POLICY_RSASIG); conn->policy &= ~POLICY_SHUNT_MASK; conn->policy |= POLICY_SHUNT_DROP; break; case KS_REJECT: conn->policy &= ~(POLICY_ENCRYPT|POLICY_AUTHENTICATE|POLICY_TUNNEL|POLICY_RSASIG); conn->policy &= ~POLICY_SHUNT_MASK; conn->policy |= POLICY_SHUNT_REJECT; break; } } KW_POLICY_FLAG(KBF_COMPRESS, POLICY_COMPRESS); KW_POLICY_FLAG(KBF_PFS, POLICY_PFS); /* reset authby flags */ if(conn->options_set[KBF_AUTHBY]) { conn->policy &= ~(POLICY_ID_AUTH_MASK); conn->policy |= conn->options[KBF_AUTHBY]; #if STARTER_POLICY_DEBUG starter_log(LOG_LEVEL_DEBUG, "%s: setting conn->policy=%08x (%08x)\n", conn->name, (unsigned int)conn->policy, conn->options[KBF_AUTHBY]); #endif } KW_POLICY_NEGATIVE_FLAG(KBF_REKEY, POLICY_DONT_REKEY); KW_POLICY_FLAG(KBF_AGGRMODE, POLICY_AGGRESSIVE); KW_POLICY_FLAG(KBF_MODECONFIGPULL, POLICY_MODECFG_PULL); KW_POLICY_FLAG(KBF_OVERLAPIP, POLICY_OVERLAPIP); KW_POLICY_FLAG(KBF_IKEv2_ALLOW_NARROWING, POLICY_IKEV2_ALLOW_NARROWING); if(conn->strings_set[KSF_ESP]) { conn->esp = clone_str(conn->strings[KSF_ESP],"KSF_ESP"); } #ifdef HAVE_LABELED_IPSEC if(conn->strings_set[KSF_POLICY_LABEL]) { conn->policy_label = clone_str(conn->strings[KSF_POLICY_LABEL],"KSF_POLICY_LABEL"); } starter_log(LOG_LEVEL_DEBUG,"connection's policy label: %s", conn->policy_label); #endif #if 0 if (conn->strings_set[KSF_MODECFGDNS1]) { conn->modecfg_dns1 = clone_str(conn->strings[KSF_MODECFGDNS1],"KSF_MODECFGDNS1"); } if (conn->strings_set[KSF_MODECFGDNS2]) { conn->modecfg_dns2 = clone_str(conn->strings[KSF_MODECFGDNS2], "KSF_MODECFGDNS2"); } if (conn->strings_set[KSF_MODECFGDOMAIN]) { conn->modecfg_domain = clone_str(conn->strings[KSF_MODECFGDOMAIN],"KSF_MODECFGDOMAIN"); } if (conn->strings_set[KSF_MODECFGBANNER]) { conn->modecfg_banner = clone_str(conn->strings[KSF_MODECFGBANNER],"KSF_MODECFGBANNER"); } #endif if(conn->strings_set[KSF_IKE]) { conn->ike = clone_str(conn->strings[KSF_IKE],"KSF_IKE"); } if(conn->strings_set[KSF_CONNALIAS]) { conn->connalias = clone_str(conn->strings[KSF_CONNALIAS],"KSF_CONNALIAS"); } if(conn->options_set[KBF_PHASE2]) { conn->policy &= ~(POLICY_AUTHENTICATE|POLICY_ENCRYPT); conn->policy |= conn->options[KBF_PHASE2]; } if(conn->options_set[KBF_IKEv2]) { switch(conn->options[KBF_IKEv2]) { case fo_never: conn->policy &= ~POLICY_IKEV2_ALLOW; break; case fo_permit: /* this is the default for now */ conn->policy |= POLICY_IKEV2_ALLOW; break; case fo_propose: conn->policy |= POLICY_IKEV2_ALLOW|POLICY_IKEV2_PROPOSE; break; case fo_insist: conn->policy |= POLICY_IKEV1_DISABLE; conn->policy |= POLICY_IKEV2_ALLOW|POLICY_IKEV2_PROPOSE; break; } } if(conn->options_set[KBF_SAREFTRACK]) { switch(conn->options[KBF_SAREFTRACK]) { case sat_yes: /* this is the default */ conn->policy |= POLICY_SAREF_TRACK; break; case sat_conntrack: conn->policy |= POLICY_SAREF_TRACK|POLICY_SAREF_TRACK_CONNTRACK; break; case sat_no: conn->policy &= ~POLICY_SAREF_TRACK; conn->policy &= ~POLICY_SAREF_TRACK_CONNTRACK; break; } } err += validate_end(conn, &conn->left, TRUE, resolvip, perr); err += validate_end(conn, &conn->right, FALSE, resolvip, perr); /* * TODO: * verify both ends are using the same inet family, if one end * is "%any" or "%defaultroute", then perhaps adjust it. * ensource this for left,leftnexthop,right,rightnexthop * Ideally, phase out connaddrfamily= which now wrongly assumes * left,leftnextop,leftsubnet are the same inet family * Currently, these tests are implicitely done, and wrongly * in case of 6in4 and 4in6 tunnels */ if(conn->options_set[KBF_AUTO]) { conn->desired_state = conn->options[KBF_AUTO]; } return err; }
/** * Take keywords from ipsec.conf syntax and load into a conn struct * * * @param conn a connection definition * @param sl a section_list * @param assigned_value is set to either k_set, or k_default. * k_default is used when we are loading a conn that should be * considered to be a "default" value, and that replacing this * value is considered acceptable. * @return bool 0 if successfull */ bool translate_conn (struct starter_conn *conn , struct section_list *sl , enum keyword_set assigned_value , err_t *error , bool alsoflip) { unsigned int err, field; ksf *the_strings; knf *the_options; str_set *set_strings; int_set *set_options; volatile int i; /* just to keep it around for debugging */ struct kw_list *kw = sl->kw; err = 0; i = 0; for ( ; kw; kw=kw->next) { char keyname[128]; i++; the_strings = &conn->strings; set_strings = &conn->strings_set; the_options = &conn->options; set_options = &conn->options_set; /* initialize with base value */ strcpy(keyname, kw->keyword.keydef->keyname); if((kw->keyword.keydef->validity & kv_conn) == 0) { /* this isn't valid in a conn! */ *error = (const char *)tmp_err; snprintf(tmp_err, sizeof(tmp_err), "keyword '%s' is not valid in a conn (%s) (#%d)\n", keyname, sl->name, i); starter_log(LOG_LEVEL_INFO, "%s", tmp_err); continue; } if(kw->keyword.keydef->validity & kv_obsolete) { starter_log(LOG_LEVEL_DEBUG,"Warning: obsolete keyword %s ignored\n",kw->keyword.keydef->keyname); } if(kw->keyword.keydef->validity & kv_leftright) { struct starter_end *left, *right; left = &conn->left; right = &conn->right; if(alsoflip) { left = &conn->right; right= &conn->left; } if(kw->keyword.keyleft) { snprintf(keyname, sizeof(keyname), "left%s", kw->keyword.keydef->keyname); the_strings = &left->strings; the_options = &left->options; set_strings = &left->strings_set; set_options = &left->options_set; } else { snprintf(keyname, sizeof(keyname), "right%s", kw->keyword.keydef->keyname); the_strings = &right->strings; the_options = &right->options; set_strings = &right->strings_set; set_options = &right->options_set; } } field = kw->keyword.keydef->field; #ifdef PARSER_TYPE_DEBUG starter_log(LOG_LEVEL_DEBUG, "#analyzing %s[%d] kwtype=%d\n", keyname, field, kw->keyword.keydef->type); #endif assert(kw->keyword.keydef != NULL); switch(kw->keyword.keydef->type) { case kt_string: case kt_filename: case kt_dirname: case kt_bitstring: case kt_ipaddr: case kt_subnet: case kt_idtype: /* all treated as strings for now */ assert(kw->keyword.keydef->field < KEY_STRINGS_MAX); if((*set_strings)[field] == k_set) { *error = tmp_err; /* keyname[0] test looks for left=/right= */ snprintf(tmp_err, sizeof(tmp_err) , "duplicate key '%s' in conn %s while processing def %s (ignored)" , keyname , conn->name , sl->name); starter_log(LOG_LEVEL_INFO, "%s", tmp_err); if(kw->keyword.string == NULL || (*the_strings)[field] == NULL || strcmp(kw->keyword.string, (*the_strings)[field])!=0) { err++; break; } } pfreeany((*the_strings)[field]); if(kw->keyword.string == NULL) { *error = tmp_err; snprintf(tmp_err, sizeof(tmp_err) , "Invalid %s value" , keyname); err++; break; } (*the_strings)[field] = clone_str(kw->keyword.string,"kt_idtype kw->keyword.string"); (*set_strings)[field] = assigned_value; break; case kt_appendstring: case kt_appendlist: /* implicitely, this field can have multiple values */ assert(kw->keyword.keydef->field < KEY_STRINGS_MAX); if(!(*the_strings)[field]) { (*the_strings)[field] = clone_str(kw->keyword.string, "kt_appendlist kw->keyword.string"); } else { char *s = (*the_strings)[field]; size_t old_len = strlen(s); /* excludes '\0' */ size_t new_len = strlen(kw->keyword.string); char *n; n = alloc_bytes(old_len + 1 + new_len + 1, "kt_appendlist"); memcpy(n, s, old_len); n[old_len] = ' '; memcpy(n + old_len + 1, kw->keyword.string, new_len + 1); /* includes '\0' */ (*the_strings)[field] = n; pfree(s); } (*set_strings)[field] = TRUE; break; case kt_rsakey: case kt_loose_enum: case kt_loose_enumarg: assert(field < KEY_STRINGS_MAX); assert(field < KEY_NUMERIC_MAX); if((*set_options)[field] == k_set) { *error = tmp_err; snprintf(tmp_err, sizeof(tmp_err) , "duplicate key '%s' in conn %s while processing def %s" , keyname , conn->name , sl->name); starter_log(LOG_LEVEL_INFO, "%s", tmp_err); /* only fatal if we try to change values */ if((*the_options)[field] != kw->number || !((*the_options)[field] == LOOSE_ENUM_OTHER && kw->number == LOOSE_ENUM_OTHER && kw->keyword.string != NULL && (*the_strings)[field] != NULL && strcmp(kw->keyword.string, (*the_strings)[field])==0)) { err++; break; } } (*the_options)[field] = kw->number; if(kw->number == LOOSE_ENUM_OTHER) { assert(kw->keyword.string != NULL); pfreeany((*the_strings)[field]); (*the_strings)[field] = clone_str(kw->keyword.string, "kt_loose_enum kw->keyword.string"); (*set_strings)[field] = TRUE; } else if(kw->keyword.keydef->type == kt_loose_enumarg && kw->argument != NULL) { pfreeany((*the_strings)[field]); (*the_strings)[field] = clone_str(kw->argument, "kt_loose_enum kw->keyword.argument"); (*set_strings)[field] = TRUE; } (*set_options)[field] = assigned_value; break; case kt_list: case kt_bool: case kt_invertbool: case kt_enum: case kt_number: case kt_time: case kt_percent: /* all treated as a number for now */ assert(field < KEY_NUMERIC_MAX); if((*set_options)[field] == k_set) { starter_log(LOG_LEVEL_INFO , "duplicate key '%s' in conn %s while processing def %s" , keyname, conn->name, sl->name); if((*the_options)[field] != kw->number) { err++; break; } } #if 0 starter_log(LOG_LEVEL_DEBUG, "#setting %s[%d]=%u\n", keyname, field, kw->number); #endif (*the_options)[field] = kw->number; (*set_options)[field] = assigned_value; break; case kt_comment: break; } } return err; }
/** * Validate that yes in fact we are one side of the tunnel * * The function checks that IP addresses are valid, nexthops are * present (if needed) as well as policies, and sets the leftID * from the left= if it isn't set. * * @param conn_st a connection definition * @param end a connection end * @param left boolean (are we 'left'? 1 = yes, 0 = no) * @param perr pointer to char containing error value * @return bool TRUE if failed */ static bool validate_end(struct starter_conn *conn_st , struct starter_end *end , bool left , bool resolvip UNUSED , err_t *perr) { err_t er = NULL; char *err_str = NULL; const char *leftright=(left ? "left" : "right"); int family = conn_st->options[KBF_CONNADDRFAMILY]; bool err = FALSE; #define ERR_FOUND(args...) do { err += error_append(&err_str, ##args); } while(0) if(!end->options_set[KNCF_IP]) { conn_st->state = STATE_INCOMPLETE; } end->addrtype=end->options[KNCF_IP]; end->addr_family = family; /* validate the KSCF_IP/KNCF_IP */ switch(end->addrtype) { case KH_ANY: anyaddr(family, &(end->addr)); break; case KH_IFACE: /* generally, this doesn't show up at this stage */ break; case KH_IPADDR: /* right=/left= */ assert(end->strings[KSCF_IP] != NULL); if (end->strings[KSCF_IP][0]=='%') { if (end->iface) pfree(end->iface); end->iface = clone_str(end->strings[KSCF_IP] + 1, "KH_IPADDR end->iface"); if (starter_iface_find(end->iface, family, &(end->addr), &(end->nexthop)) == -1) { conn_st->state = STATE_INVALID; } /* not numeric, so set the type to the iface type */ end->addrtype = KH_IFACE; break; } er = ttoaddr_num(end->strings[KNCF_IP], 0, family, &(end->addr)); if(er) { /* not numeric, so set the type to the string type */ end->addrtype = KH_IPHOSTNAME; } if(end->id == NULL) { char idbuf[ADDRTOT_BUF]; addrtot(&end->addr, 0, idbuf, sizeof(idbuf)); end->id= clone_str(idbuf, "end id"); } break; case KH_OPPO: conn_st->policy |= POLICY_OPPO; break; case KH_OPPOGROUP: conn_st->policy |= POLICY_OPPO|POLICY_GROUP; break; case KH_GROUP: conn_st->policy |= POLICY_GROUP; break; case KH_IPHOSTNAME: /* XXX */ break; case KH_DEFAULTROUTE: break; case KH_NOTSET: break; } /* validate the KSCF_SUBNET */ if(end->strings_set[KSCF_SUBNET]) { char *value = end->strings[KSCF_SUBNET]; if ( ((strlen(value)>=6) && (strncmp(value,"vhost:",6)==0)) || ((strlen(value)>=5) && (strncmp(value,"vnet:",5)==0)) ) { er = NULL; end->virt = clone_str(value, "end->virt"); } else { end->has_client = TRUE; er = ttosubnet(value, 0, 0, &(end->subnet)); } if (er) ERR_FOUND("bad subnet %ssubnet=%s [%s] family=%s", leftright, value, er, family2str(family)); } /* set nexthop address to something consistent, by default */ anyaddr(family, &end->nexthop); anyaddr(addrtypeof(&end->addr), &end->nexthop); /* validate the KSCF_NEXTHOP */ if(end->strings_set[KSCF_NEXTHOP]) { char *value = end->strings[KSCF_NEXTHOP]; if(strcasecmp(value, "%defaultroute")==0) { end->nexttype=KH_DEFAULTROUTE; } else { if (tnatoaddr(value, strlen(value), AF_INET, &(end->nexthop)) != NULL && tnatoaddr(value, strlen(value), AF_INET6, &(end->nexthop)) != NULL) { er = ttoaddr(value, 0, family, &(end->nexthop)); if (er) ERR_FOUND("bad addr %snexthop=%s [%s]", leftright, value, er); } end->nexttype = KH_IPADDR; } } else { if (end->addrtype == KH_DEFAULTROUTE) { end->nexttype = KH_DEFAULTROUTE; } anyaddr(family, &end->nexthop); } /* validate the KSCF_ID */ if(end->strings_set[KSCF_ID]) { char *value = end->strings[KSCF_ID]; pfreeany(end->id); end->id = clone_str(value, "end->id"); } if(end->options_set[KSCF_RSAKEY1]) { end->rsakey1_type = end->options[KSCF_RSAKEY1]; end->rsakey2_type = end->options[KSCF_RSAKEY2]; switch(end->rsakey1_type) { case PUBKEY_DNS: case PUBKEY_DNSONDEMAND: end->key_from_DNS_on_demand = TRUE; break; default: end->key_from_DNS_on_demand = FALSE; /* validate the KSCF_RSAKEY1/RSAKEY2 */ if(end->strings[KSCF_RSAKEY1] != NULL) { char *value = end->strings[KSCF_RSAKEY1]; pfreeany(end->rsakey1); end->rsakey1 = (unsigned char *)clone_str(value,"end->rsakey1"); } if(end->strings[KSCF_RSAKEY2] != NULL) { char *value = end->strings[KSCF_RSAKEY2]; pfreeany(end->rsakey2); end->rsakey2 = (unsigned char *)clone_str(value,"end->rsakey2"); } } } /* validate the KSCF_SOURCEIP, if any, and if set, * set the subnet to same value, if not set. */ if(end->strings_set[KSCF_SOURCEIP]) { char *value = end->strings[KSCF_SOURCEIP]; if (tnatoaddr(value, strlen(value), AF_INET, &(end->sourceip)) != NULL && tnatoaddr(value, strlen(value), AF_INET6, &(end->sourceip)) != NULL) { er = ttoaddr(value, 0, 0, &(end->sourceip)); if (er) ERR_FOUND("bad addr %ssourceip=%s [%s]", leftright, value, er); } else { er = tnatoaddr(value, 0, 0, &(end->sourceip)); if (er) ERR_FOUND("bad numerical addr %ssourceip=%s [%s]", leftright, value, er); } if(!end->has_client) { starter_log(LOG_LEVEL_INFO, "defaulting %ssubnet to %s\n", leftright, value); er = addrtosubnet(&end->sourceip, &end->subnet); if (er) ERR_FOUND("attempt to default %ssubnet from %s failed: %s", leftright, value, er); end->has_client = TRUE; end->has_client_wildcard = FALSE; } } /* copy certificate path name */ if(end->strings_set[KSCF_CERT]) { end->cert = clone_str(end->strings[KSCF_CERT], "KSCF_CERT"); } if(end->strings_set[KSCF_CA]) { end->ca = clone_str(end->strings[KSCF_CA], "KSCF_CA"); } if(end->strings_set[KSCF_UPDOWN]) { end->updown = clone_str(end->strings[KSCF_UPDOWN], "KSCF_UPDOWN"); } if(end->strings_set[KSCF_PROTOPORT]) { err_t ugh; char *value = end->strings[KSCF_PROTOPORT]; ugh = ttoprotoport(value, 0, &end->protocol, &end->port, &end->has_port_wildcard); if (ugh) ERR_FOUND("bad %sprotoport=%s [%s]", leftright, value, ugh); } if (end->options_set[KNCF_XAUTHSERVER] || end->options_set[KNCF_XAUTHCLIENT]) { conn_st->policy |= POLICY_XAUTH; } /* KSCF_SUBNETWITHIN --- not sure what to do with it. KSCF_ESPENCKEY --- todo (manual keying) KSCF_ESPAUTHKEY --- todo (manual keying) KSCF_SOURCEIP = 16, KSCF_MAX = 19 */ if(err) *perr = err_str; return err; # undef ERR_FOUND }
struct starter_config *confread_load(const char *file , err_t *perr , bool resolvip , char *ctlbase , bool setuponly) { struct starter_config *cfg = NULL; struct config_parsed *cfgp; struct section_list *sconn; unsigned int err = 0, connerr; /** * Load file */ cfgp = parser_load_conf(file, perr); if (!cfgp) return NULL; cfg = (struct starter_config *)alloc_bytes(sizeof(struct starter_config),"starter_config cfg"); zero(cfg); /** * Set default values */ ipsecconf_default_values(cfg); if(ctlbase) { pfree(cfg->ctlbase); cfg->ctlbase = clone_str(ctlbase, "control socket"); } starter_whack_init_cfg(cfg); /* set default sender to send to socket */ /** * Load setup */ err += load_setup(cfg, cfgp); if(err) { parser_free_conf(cfgp); confread_free(cfg); return NULL; } if(!setuponly) { /** * Find %default and %oedefault conn * */ for(sconn = cfgp->sections.tqh_first; (!err) && sconn != NULL; sconn = sconn->link.tqe_next) { if (strcmp(sconn->name,"%default")==0) { starter_log(LOG_LEVEL_DEBUG, "Loading default conn"); err += load_conn (cfg, &cfg->conn_default, cfgp, sconn, FALSE, /*default conn*/TRUE, resolvip, perr); } if (strcmp(sconn->name,"%oedefault")==0) { starter_log(LOG_LEVEL_DEBUG, "Loading oedefault conn"); err += load_conn (cfg, &cfg->conn_oedefault, cfgp, sconn, FALSE, /*default conn*/TRUE, resolvip, perr); if(err == 0) { cfg->got_oedefault=TRUE; } } } /** * Load other conns */ for(sconn = cfgp->sections.tqh_first; sconn != NULL; sconn = sconn->link.tqe_next) { if (strcmp(sconn->name,"%default")==0) continue; if (strcmp(sconn->name,"%oedefault")==0) continue; connerr = init_load_conn(cfg, cfgp, sconn, TRUE, FALSE, resolvip, perr); if(connerr == -1) { parser_free_conf(cfgp); confread_free(cfg); return NULL; } err += connerr; } /* if we have OE on, then create any missing OE conns! */ if(cfg->setup.options[KBF_OPPOENCRYPT]) { starter_log(LOG_LEVEL_DEBUG, "Enabling OE conns\n"); add_any_oeconns(cfg, cfgp); } } parser_free_conf(cfgp); return cfg; }
struct starter_config *confread_load(const char *file, err_t *perr, bool resolvip, const char *ctlbase, bool setuponly) { bool err = FALSE; /** * Load file */ struct config_parsed *cfgp = parser_load_conf(file, perr); if (cfgp == NULL) return NULL; struct starter_config *cfg = alloc_thing(struct starter_config, "starter_config cfg"); /** * Set default values */ ipsecconf_default_values(cfg); if (ctlbase != NULL) { pfree(cfg->ctlbase); cfg->ctlbase = clone_str(ctlbase, "control socket"); } /** * Load setup */ err |= load_setup(cfg, cfgp); if (err) { parser_free_conf(cfgp); confread_free(cfg); return NULL; } #ifdef DNSSEC struct ub_ctx *dnsctx = unbound_init(); if (dnsctx == NULL) return NULL; #endif if (!setuponly) { /** * Find %default * */ struct section_list *sconn; for (sconn = cfgp->sections.tqh_first; (!err) && sconn != NULL; sconn = sconn->link.tqe_next) { if (streq(sconn->name, "%default")) { starter_log(LOG_LEVEL_DEBUG, "Loading default conn"); err |= load_conn( #ifdef DNSSEC dnsctx, #endif &cfg->conn_default, cfgp, sconn, FALSE, /*default conn*/ TRUE, resolvip, perr); } } /** * Load other conns */ for (sconn = cfgp->sections.tqh_first; sconn != NULL; sconn = sconn->link.tqe_next) { if (streq(sconn->name, "%default")) continue; err |= init_load_conn( #ifdef DNSSEC dnsctx, #endif cfg, cfgp, sconn, FALSE, resolvip, perr); } } parser_free_conf(cfgp); #ifdef DNSSEC ub_ctx_delete(dnsctx); #endif return cfg; }
static bool load_conn( #ifdef DNSSEC struct ub_ctx *dnsctx, #endif struct starter_conn *conn, const struct config_parsed *cfgp, struct section_list *sl, bool alsoprocessing, bool defaultconn, bool resolvip, err_t *perr) { bool err; /* turn all of the keyword/value pairs into options/strings in left/right */ err = translate_conn(conn, sl, defaultconn ? k_default : k_set, perr); move_comment_list(&conn->comments, &sl->comments); if (err) return err; if (conn->strings[KSCF_ALSO] != NULL && !alsoprocessing) { starter_log(LOG_LEVEL_INFO, "also= is not valid in section '%s'", sl->name); /* ??? should we not set *perr? */ return TRUE; /* error */ } /* now, process the also's */ if (conn->alsos != NULL) FREE_LIST(conn->alsos); conn->alsos = new_list(conn->strings[KSCF_ALSO]); if (alsoprocessing && conn->alsos != NULL) { struct section_list *sl1; /* note: for the duration of this loop body * conn->alsos is migrated to local variable alsos. */ char **alsos = conn->alsos; int alsosize; int alsoplace; conn->alsos = NULL; /* reset all of the "beenhere" flags */ for (sl1 = cfgp->sections.tqh_first; sl1 != NULL; sl1 = sl1->link.tqe_next) sl1->beenhere = FALSE; sl->beenhere = TRUE; /* count them */ for (alsosize = 0; alsos[alsosize] != NULL; alsosize++) ; alsoplace = 0; while (alsoplace < alsosize && alsos[alsoplace] != NULL && alsoplace < ALSO_LIMIT) { /* * for each also= listed, go find this section's keyword list, and * load it as well. This may extend the also= list (and the end), * which we handle by zeroing the also list, and adding to it after * checking for duplicates. */ for (sl1 = cfgp->sections.tqh_first; sl1 != NULL && !streq(alsos[alsoplace], sl1->name); sl1 = sl1->link.tqe_next) ; starter_log(LOG_LEVEL_DEBUG, "\twhile loading conn '%s' also including '%s'", conn->name, alsos[alsoplace]); /* * if we found something that matches by name, * and we haven't been there, then process it. */ if (sl1 != NULL && !sl1->beenhere) { conn->strings_set[KSCF_ALSO] = FALSE; pfreeany(conn->strings[KSCF_ALSO]); conn->strings[KSCF_ALSO] = NULL; sl1->beenhere = TRUE; /* translate things, but do not replace earlier settings! */ err |= translate_conn(conn, sl1, k_set, perr); if (conn->strings[KSCF_ALSO] != NULL) { /* now, check out the KSCF_ALSO, and extend list if we need to */ char **newalsos = new_list( conn->strings[KSCF_ALSO]); if (newalsos != NULL) { char **ra; int newalsoplace; /* count them */ for (newalsoplace = 0; newalsos[newalsoplace] != NULL; newalsoplace++) ; /* extend conn->alss */ ra = alloc_bytes((alsosize + newalsoplace + 1) * sizeof(char *), "conn->alsos"); memcpy(ra, alsos, alsosize * sizeof(char *)); pfree(alsos); alsos = ra; for (newalsoplace = 0; newalsos[newalsoplace] != NULL; newalsoplace++) { assert(conn != NULL); assert(conn->name != NULL); starter_log( LOG_LEVEL_DEBUG, "\twhile processing section '%s' added also=%s", sl1->name, newalsos[newalsoplace]); alsos[alsosize++] = clone_str(newalsos[newalsoplace], "alsos"); } alsos[alsosize] = NULL; } FREE_LIST(newalsos); } } alsoplace++; } /* migrate alsos back to conn->alsos */ conn->alsos = alsos; if (alsoplace >= ALSO_LIMIT) { starter_log(LOG_LEVEL_INFO, "while loading conn '%s', too many also= used at section %s. Limit is %d", conn->name, alsos[alsoplace], ALSO_LIMIT); /* ??? should we not set *perr? */ return TRUE; /* error */ } } if (conn->options_set[KBF_TYPE]) { switch ((enum keyword_satype)conn->options[KBF_TYPE]) { case KS_TUNNEL: if (conn->options_set[KBF_AUTHBY] && conn->options[KBF_AUTHBY] == POLICY_AUTH_NEVER) { *perr = "connection type=tunnel must not specify authby=never"; return TRUE; } conn->policy |= POLICY_TUNNEL; conn->policy &= ~POLICY_SHUNT_MASK; break; case KS_TRANSPORT: if (conn->options_set[KBF_AUTHBY] && conn->options[KBF_AUTHBY] == POLICY_AUTH_NEVER) { *perr = "connection type=transport must not specify authby=never"; return TRUE; } conn->policy &= ~POLICY_TUNNEL; conn->policy &= ~POLICY_SHUNT_MASK; break; case KS_PASSTHROUGH: if (!conn->options_set[KBF_AUTHBY] || conn->options[KBF_AUTHBY] != POLICY_AUTH_NEVER) { *perr = "connection type=passthrough must specify authby=never"; } conn->policy &= ~(POLICY_ENCRYPT | POLICY_AUTHENTICATE | POLICY_TUNNEL | POLICY_RSASIG); conn->policy &= ~POLICY_SHUNT_MASK; conn->policy |= POLICY_SHUNT_PASS; break; case KS_DROP: if (!conn->options_set[KBF_AUTHBY] || conn->options[KBF_AUTHBY] != POLICY_AUTH_NEVER) { *perr = "connection type=drop must specify authby=never"; } conn->policy &= ~(POLICY_ENCRYPT | POLICY_AUTHENTICATE | POLICY_TUNNEL | POLICY_RSASIG); conn->policy &= ~POLICY_SHUNT_MASK; conn->policy |= POLICY_SHUNT_DROP; break; case KS_REJECT: if (!conn->options_set[KBF_AUTHBY] || conn->options[KBF_AUTHBY] != POLICY_AUTH_NEVER) { *perr = "connection type=drop must specify authby=never"; } conn->policy &= ~(POLICY_ENCRYPT | POLICY_AUTHENTICATE | POLICY_TUNNEL | POLICY_RSASIG); conn->policy &= ~POLICY_SHUNT_MASK; conn->policy |= POLICY_SHUNT_REJECT; break; } } if (conn->options_set[KBF_FAILURESHUNT]) { conn->policy &= ~POLICY_FAIL_MASK; switch (conn->options[KBF_FAILURESHUNT]) { case KFS_FAIL_NONE: conn->policy |= POLICY_FAIL_NONE; break; case KFS_FAIL_PASS: conn->policy |= POLICY_FAIL_PASS; break; case KFS_FAIL_DROP: conn->policy |= POLICY_FAIL_DROP; break; case KFS_FAIL_REJECT: conn->policy |= POLICY_FAIL_REJECT; break; } } if (conn->options_set[KBF_NEGOTIATIONSHUNT]) { switch (conn->options[KBF_NEGOTIATIONSHUNT]) { case KNS_FAIL_PASS: conn->policy |= POLICY_NEGO_PASS; break; case KNS_FAIL_DROP: conn->policy &= ~POLICY_NEGO_PASS; break; } } KW_POLICY_FLAG(KBF_COMPRESS, POLICY_COMPRESS); KW_POLICY_FLAG(KBF_PFS, POLICY_PFS); /* reset authby= flags */ if (conn->options_set[KBF_AUTHBY]) { conn->policy &= ~POLICY_ID_AUTH_MASK; conn->policy |= conn->options[KBF_AUTHBY]; } KW_POLICY_NEGATIVE_FLAG(KBF_IKEPAD, POLICY_NO_IKEPAD); KW_POLICY_NEGATIVE_FLAG(KBF_REKEY, POLICY_DONT_REKEY); KW_POLICY_FLAG(KBF_AGGRMODE, POLICY_AGGRESSIVE); KW_POLICY_FLAG(KBF_MODECONFIGPULL, POLICY_MODECFG_PULL); KW_POLICY_FLAG(KBF_OVERLAPIP, POLICY_OVERLAPIP); KW_POLICY_FLAG(KBF_IKEv2_ALLOW_NARROWING, POLICY_IKEV2_ALLOW_NARROWING); KW_POLICY_FLAG(KBF_IKEv2_PAM_AUTHORIZE, POLICY_IKEV2_PAM_AUTHORIZE); # define str_to_conn(member, kscf) { \ if (conn->strings_set[kscf]) \ conn->member = clone_str(conn->strings[kscf], #kscf); \ } str_to_conn(esp, KSCF_ESP); #ifdef HAVE_LABELED_IPSEC str_to_conn(policy_label, KSCF_POLICY_LABEL); if (conn->policy_label != NULL) starter_log(LOG_LEVEL_DEBUG, "connection's policy label: %s", conn->policy_label); #endif str_to_conn(ike, KSCF_IKE); str_to_conn(modecfg_dns1, KSCF_MODECFGDNS1); str_to_conn(modecfg_dns2, KSCF_MODECFGDNS2); str_to_conn(modecfg_domain, KSCF_MODECFGDOMAIN); str_to_conn(modecfg_banner, KSCF_MODECFGBANNER); /* mark-in= and mark-out= override mark= */ str_to_conn(conn_mark_in, KSCF_CONN_MARK_BOTH); str_to_conn(conn_mark_out, KSCF_CONN_MARK_BOTH); str_to_conn(conn_mark_in, KSCF_CONN_MARK_IN); str_to_conn(conn_mark_out, KSCF_CONN_MARK_OUT); str_to_conn(vti_iface, KSCF_VTI_IFACE); str_to_conn(connalias, KSCF_CONNALIAS); # undef str_to_conn if (conn->options_set[KBF_PHASE2]) { conn->policy &= ~(POLICY_AUTHENTICATE | POLICY_ENCRYPT); conn->policy |= conn->options[KBF_PHASE2]; } if (conn->options_set[KBF_IKEv2]) { lset_t pv2 = LEMPTY; switch (conn->options[KBF_IKEv2]) { case fo_never: pv2 = POLICY_IKEV1_ALLOW; break; case fo_permit: /* this is the default for now */ pv2 = POLICY_IKEV1_ALLOW | POLICY_IKEV2_ALLOW; break; case fo_propose: pv2 = POLICY_IKEV1_ALLOW | POLICY_IKEV2_ALLOW | POLICY_IKEV2_PROPOSE; break; case fo_insist: pv2 = POLICY_IKEV2_ALLOW | POLICY_IKEV2_PROPOSE; break; } conn->policy = (conn->policy & ~POLICY_IKEV2_MASK) | pv2; } if (conn->options_set[KBF_ESN]) { conn->policy &= ~(POLICY_ESN_NO | POLICY_ESN_YES); switch (conn->options[KBF_ESN]) { case esn_yes: conn->policy |= POLICY_ESN_YES; break; case esn_no: /* this is the default for now */ conn->policy |= POLICY_ESN_NO; break; case esn_either: conn->policy |= POLICY_ESN_NO | POLICY_ESN_YES; break; } } if (conn->options_set[KBF_IKE_FRAG]) { conn->policy &= ~(POLICY_IKE_FRAG_ALLOW | POLICY_IKE_FRAG_FORCE); switch (conn->options[KBF_IKE_FRAG]) { case ynf_no: break; case ynf_yes: /* this is the default */ conn->policy |= POLICY_IKE_FRAG_ALLOW; break; case ynf_force: conn->policy |= POLICY_IKE_FRAG_ALLOW | POLICY_IKE_FRAG_FORCE; break; } } if (conn->options_set[KBF_SAREFTRACK]) { conn->policy &= ~(POLICY_SAREF_TRACK | POLICY_SAREF_TRACK_CONNTRACK); switch (conn->options[KBF_SAREFTRACK]) { case sat_yes: /* this is the default */ conn->policy |= POLICY_SAREF_TRACK; break; case sat_conntrack: conn->policy |= POLICY_SAREF_TRACK | POLICY_SAREF_TRACK_CONNTRACK; break; case sat_no: break; } } /* * some options are set as part of our default, but * some make no sense for shunts, so remove those again */ if (NEVER_NEGOTIATE(conn->policy)) { /* remove IPsec related options */ conn->policy &= ~(POLICY_PFS | POLICY_COMPRESS | POLICY_ESN_NO | POLICY_ESN_YES | POLICY_SAREF_TRACK | POLICY_SAREF_TRACK_CONNTRACK); /* remove IKE related options */ conn->policy &= ~(POLICY_IKEV1_ALLOW | POLICY_IKEV2_ALLOW | POLICY_IKEV2_PROPOSE | POLICY_IKE_FRAG_ALLOW | POLICY_IKE_FRAG_FORCE); } err |= validate_end( #ifdef DNSSEC dnsctx, #endif conn, &conn->left, "left", resolvip, perr); err |= validate_end( #ifdef DNSSEC dnsctx, #endif conn, &conn->right, "right", resolvip, perr); /* * TODO: * verify both ends are using the same inet family, if one end * is "%any" or "%defaultroute", then perhaps adjust it. * ensource this for left,leftnexthop,right,rightnexthop * Ideally, phase out connaddrfamily= which now wrongly assumes * left,leftnextop,leftsubnet are the same inet family * Currently, these tests are implicitely done, and wrongly * in case of 6in4 and 4in6 tunnels */ if (conn->options_set[KBF_AUTO]) conn->desired_state = conn->options[KBF_AUTO]; return err; }
static int _iface_up (int sock, struct st_ipsec_if *iface, char *phys, unsigned int mtu, int nat_t) { struct ifreq req; struct ipsectunnelconf *shc=(struct ipsectunnelconf *)&req.ifr_data; short phys_flags; int ret = 0; strncpy(req.ifr_name, phys, IFNAMSIZ); if (ioctl(sock, SIOCGIFFLAGS, &req)!=0) { return ret; } phys_flags = req.ifr_flags; strncpy(req.ifr_name, iface->name, IFNAMSIZ); if (ioctl(sock, SIOCGIFFLAGS, &req)!=0) { return ret; } if ((!(req.ifr_flags & IFF_UP)) || (!iface->up)) { starter_log(LOG_LEVEL_INFO, "attaching interface %s to %s", iface->name, phys); ret = 1; } if ((*iface->phys) && (strcmp(iface->phys, phys)!=0)) { /* tncfg --detach if phys has changed */ strncpy(req.ifr_name, iface->name, IFNAMSIZ); ioctl(sock, IPSEC_DEL_DEV, &req); ret = 1; } /* tncfg --attach */ strncpy(req.ifr_name, iface->name, IFNAMSIZ); strncpy(shc->cf_name, phys, sizeof(shc->cf_name)); ioctl(sock, IPSEC_SET_DEV, &req); /* set ipsec addr = phys addr */ strncpy(req.ifr_name, phys, IFNAMSIZ); if (ioctl(sock, SIOCGIFADDR, &req)==0) { strncpy(req.ifr_name, iface->name, IFNAMSIZ); ioctl(sock, SIOCSIFADDR, &req); } /* set ipsec mask = phys mask */ strncpy(req.ifr_name, phys, IFNAMSIZ); if (ioctl(sock, SIOCGIFNETMASK, &req)==0) { strncpy(req.ifr_name, iface->name, IFNAMSIZ); ioctl(sock, SIOCSIFNETMASK, &req); } /* set other flags & addr */ strncpy(req.ifr_name, iface->name, IFNAMSIZ); if (ioctl(sock, SIOCGIFFLAGS, &req)==0) { if (phys_flags & IFF_POINTOPOINT) { req.ifr_flags |= IFF_POINTOPOINT; req.ifr_flags &= ~IFF_BROADCAST; ioctl(sock, SIOCSIFFLAGS, &req); strncpy(req.ifr_name, phys, IFNAMSIZ); if (ioctl(sock, SIOCGIFDSTADDR, &req)==0) { strncpy(req.ifr_name, iface->name, IFNAMSIZ); ioctl(sock, SIOCSIFDSTADDR, &req); } } else if (phys_flags & IFF_BROADCAST) { req.ifr_flags &= ~IFF_POINTOPOINT; req.ifr_flags |= IFF_BROADCAST; ioctl(sock, SIOCSIFFLAGS, &req); strncpy(req.ifr_name, phys, IFNAMSIZ); if (ioctl(sock, SIOCGIFBRDADDR, &req)==0) { strncpy(req.ifr_name, iface->name, IFNAMSIZ); ioctl(sock, SIOCSIFBRDADDR, &req); } } else { req.ifr_flags &= ~IFF_POINTOPOINT; req.ifr_flags &= ~IFF_BROADCAST; ioctl(sock, SIOCSIFFLAGS, &req); } } /* * guess MTU = phys interface MTU - ESP Overhead * * ESP overhead : 10+16+7+2+12=57 -> 60 by security * NAT-T overhead : 20 */ if (mtu==0) { strncpy(req.ifr_name, phys, IFNAMSIZ); ioctl(sock, SIOCGIFMTU, &req); mtu = req.ifr_mtu - 60; if (nat_t) mtu -= 20; } /* set MTU */ if (mtu) { strncpy(req.ifr_name, iface->name, IFNAMSIZ); req.ifr_mtu = mtu; ioctl(sock, SIOCSIFMTU, &req); } /* ipsec interface UP */ strncpy(req.ifr_name, iface->name, IFNAMSIZ); if (ioctl(sock, SIOCGIFFLAGS, &req)==0) { req.ifr_flags |= IFF_UP; ioctl(sock, SIOCSIFFLAGS, &req); } iface->up = 1; strncpy(iface->phys, phys, IFNAMSIZ); return ret; }
int starter_start_pluto (struct starter_config *cfg, int debug) { int i; struct stat stb; pid_t pid; char *arg[] = { PLUTO_CMD, "--nofork", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; int argc = 2; if (debug) { arg[argc++] = "--stderrlog"; } if (cfg->setup.options[KBF_UNIQUEIDS]) { arg[argc++] = "--uniqueids"; } if(cfg->setup.options[KBF_PLUTODEBUG] & DBG_ALL) { arg[argc++] = "--debug-all"; } else { ADD_DEBUG(DBG_RAW, "raw"); ADD_DEBUG(DBG_CRYPT, "crypt"); ADD_DEBUG(DBG_PARSING,"parsing"); ADD_DEBUG(DBG_EMITTING,"emitting"); ADD_DEBUG(DBG_CONTROL, "control"); ADD_DEBUG(DBG_CONTROLMORE, "controlmore"); ADD_DEBUG(DBG_KLIPS, "klips"); ADD_DEBUG(DBG_DNS, "dns"); ADD_DEBUG(DBG_OPPO, "oppo"); ADD_DEBUG(DBG_PRIVATE, "private"); ADD_DEBUG(IMPAIR_DELAY_ADNS_KEY_ANSWER, "impair-delay-adns-key-answer"); ADD_DEBUG(IMPAIR_DELAY_ADNS_TXT_ANSWER,"impair-delay-adns-txt-answer"); ADD_DEBUG(IMPAIR_BUST_MI2, "impair-bust-mi2"); ADD_DEBUG(IMPAIR_BUST_MR2, "impair-bust-mr2"); } if (cfg->setup.strictcrlpolicy) { arg[argc++] = "--strictcrlpolicy"; } if (cfg->setup.nocrsend) { arg[argc++] = "--nocrsend"; } #ifdef NAT_TRAVERSAL { static char ka[15]; if (cfg->setup.nat_traversal) { arg[argc++] = "--nat_traversal"; } if (cfg->setup.keep_alive) { arg[argc++] = "--keep_alive"; sprintf(ka, "%u", cfg->setup.keep_alive); arg[argc++] = ka; } } #endif #ifdef VIRTUAL_IP if (cfg->setup.virtual_private) { arg[argc++] = "--virtual_private"; arg[argc++] = cfg->setup.virtual_private; } #endif if (_pluto_pid) { starter_log(LOG_LEVEL_ERR, "starter_start_pluto(): pluto already started..."); return -1; } else { unlink(CTL_FILE); _stop_requested = 0; if (cfg->setup.strings[KSF_PREPLUTO]) system(cfg->setup.strings[KSF_PREPLUTO]); pid = fork(); switch (pid) { case -1: starter_log(LOG_LEVEL_ERR, "can't fork(): %s", strerror(errno)); return -1; break; case 0: /** * Child */ setsid(); sigsetmask(0); execv(arg[0], arg); starter_log(LOG_LEVEL_ERR, "can't execv(%s,...): %s", arg[0], strerror(errno)); exit(1); break; default: /** * Father */ _pluto_pid = pid; for (i=0; (i<50) && (_pluto_pid); i++) { /** * Wait for pluto */ usleep(20000); if (stat(CTL_FILE, &stb)==0) { starter_log(LOG_LEVEL_INFO, "pluto (%d) started", _pluto_pid); if (cfg->setup.strings[KSF_POSTPLUTO]) { system(cfg->setup.strings[KSF_POSTPLUTO]); } return 0; } } if (_pluto_pid) { /** * If pluto is started but with no ctl file, stop it */ starter_log(LOG_LEVEL_ERR, "pluto too long to start... - kill kill"); for (i=0; (i<20) && ((pid=_pluto_pid)!=0); i++) { if (i<10) kill(pid, SIGTERM); else kill(pid, SIGKILL); usleep(20000); } } else { starter_log(LOG_LEVEL_ERR, "pluto refused to be started"); } return -1; break; } } return -1; }