On Fri, Sep 01, 2017 at 11:58:46AM +0200, Martin Pieuchot wrote:
> On 22/08/17(Tue) 10:50, Martin Pieuchot wrote:
> > By reviewing my last isakmpd(8) diff to fix a use-after-free, hshoexer@
> > pointed out that if exchange_establish() fails, `arg' is leaked. This is
> > not a new issue. However it generally happens under memory pressure,
> > and when you're under memory pressure leaking memory is not a clever
> > thing to do.
> >
> > In order to prevent this memory leak, we have to:
> >
> > - check for failures of exchange_establish_p{1,2}() and call the given
> > `finalize' function with the `fail' argument when this happen.
> > Because now the various finalize functions correctly free their
> > corresponding argument.
> >
> > - introduce some sanity checks in exchange_free() to be able to call
> > if even if the data structure isn't completely initialized.
> >
> > Diff below does that. It also includes a fix for a double free in one
> > of the error paths of exchange_establish().
> >
> > ok?
>
> Anyone?
The original code is clearly lacking error handling, and I cannot
spot a mistake in the error handling you've added.
ok
> >
> > Index: exchange.c
> > ===================================================================
> > RCS file: /cvs/src/sbin/isakmpd/exchange.c,v
> > retrieving revision 1.138
> > diff -u -p -r1.138 exchange.c
> > --- exchange.c 10 Mar 2016 07:32:16 -0000 1.138
> > +++ exchange.c 22 Aug 2017 08:45:21 -0000
> > @@ -529,6 +529,7 @@ exchange_enter(struct exchange *exchange
> > }
> > bucket &= bucket_mask;
> > LIST_INSERT_HEAD(&exchange_tab[bucket], exchange, link);
> > + exchange->linked = 1;
> > }
> >
> > /*
> > @@ -703,7 +704,7 @@ exchange_establish_transaction(struct ex
> > }
> >
> > /* Establish a phase 1 exchange. */
> > -void
> > +int
> > exchange_establish_p1(struct transport *t, u_int8_t type, u_int32_t doi,
> > char *name, void *args, void (*finalize)(struct exchange *, void *,
> > int),
> > void *arg, int stayalive)
> > @@ -738,7 +739,7 @@ exchange_establish_p1(struct transport *
> > else {
> > log_print("exchange_establish_p1: "
> > "DOI \"%s\" unsupported", str);
> > - return;
> > + return -1;
> > }
> >
> > /* What exchange type do we want? */
> > @@ -747,20 +748,19 @@ exchange_establish_p1(struct transport *
> > log_print("exchange_establish_p1: "
> > "no \"EXCHANGE_TYPE\" tag in [%s] section",
> > tag);
> > - return;
> > + return -1;
> > }
> > type = constant_value(isakmp_exch_cst, str);
> > if (!type) {
> > log_print("exchange_establish_p1: "
> > "unknown exchange type %s", str);
> > - return;
> > + return -1;
> > }
> > }
> > }
> > exchange = exchange_create(1, 1, doi, type);
> > if (!exchange) {
> > - /* XXX Do something here? */
> > - return;
> > + return -1;
> > }
> > if (name) {
> > exchange->name = strdup(name);
> > @@ -768,7 +768,7 @@ exchange_establish_p1(struct transport *
> > log_error("exchange_establish_p1: "
> > "strdup (\"%s\") failed", name);
> > exchange_free(exchange);
> > - return;
> > + return -1;
> > }
> > }
> > exchange->policy = name ? conf_get_str(name, "Configuration") : 0;
> > @@ -787,7 +787,7 @@ exchange_establish_p1(struct transport *
> > "calloc (1, %lu) failed",
> > (unsigned long)sizeof(*node));
> > exchange_free(exchange);
> > - return;
> > + return -1;
> > }
> > /*
> > * Insert this finalization inbetween
> > @@ -813,7 +813,7 @@ exchange_establish_p1(struct transport *
> > if (!msg) {
> > log_print("exchange_establish_p1: message_alloc () failed");
> > exchange_free(exchange);
> > - return;
> > + return 0; /* exchange_free() runs finalize */
> > }
> > msg->exchange = exchange;
> >
> > @@ -828,10 +828,9 @@ exchange_establish_p1(struct transport *
> > sa_create(exchange, 0);
> > msg->isakmp_sa = TAILQ_FIRST(&exchange->sa_list);
> > if (!msg->isakmp_sa) {
> > - /* XXX Do something more here? */
> > message_free(msg);
> > exchange_free(exchange);
> > - return;
> > + return 0; /* exchange_free() runs finalize */
> > }
> > sa_reference(msg->isakmp_sa);
> >
> > @@ -841,10 +840,11 @@ exchange_establish_p1(struct transport *
> > msg->extra = args;
> >
> > exchange_run(msg);
> > + return 0;
> > }
> >
> > /* Establish a phase 2 exchange. XXX With just one SA for now. */
> > -void
> > +int
> > exchange_establish_p2(struct sa *isakmp_sa, u_int8_t type, char *name,
> > void *args, void (*finalize)(struct exchange *, void *, int), void
> > *arg)
> > {
> > @@ -864,7 +864,7 @@ exchange_establish_p2(struct sa *isakmp_
> > if (!tag) {
> > log_print("exchange_establish_p2: "
> > "no configuration for peer \"%s\"", name);
> > - return;
> > + return -1;
> > }
> > seq = (u_int32_t)conf_get_num(name, "Acquire-ID", 0);
> >
> > @@ -877,7 +877,7 @@ exchange_establish_p2(struct sa *isakmp_
> > else {
> > log_print("exchange_establish_p2: "
> > "DOI \"%s\" unsupported", str);
> > - return;
> > + return -1;
> > }
> >
> > /* What exchange type do we want? */
> > @@ -887,21 +887,20 @@ exchange_establish_p2(struct sa *isakmp_
> > log_print("exchange_establish_p2: "
> > "no \"EXCHANGE_TYPE\" tag in [%s] section",
> > tag);
> > - return;
> > + return -1;
> > }
> > /* XXX IKE dependent. */
> > type = constant_value(ike_exch_cst, str);
> > if (!type) {
> > log_print("exchange_establish_p2: unknown "
> > "exchange type %s", str);
> > - return;
> > + return -1;
> > }
> > }
> > }
> > exchange = exchange_create(2, 1, doi, type);
> > if (!exchange) {
> > - /* XXX Do something here? */
> > - return;
> > + return -1;
> > }
> > if (name) {
> > exchange->name = strdup(name);
> > @@ -909,7 +908,7 @@ exchange_establish_p2(struct sa *isakmp_
> > log_error("exchange_establish_p2: "
> > "strdup (\"%s\") failed", name);
> > exchange_free(exchange);
> > - return;
> > + return -1;
> > }
> > }
> > exchange->policy = name ? conf_get_str(name, "Configuration") : 0;
> > @@ -936,7 +935,7 @@ exchange_establish_p2(struct sa *isakmp_
> > for (i = 0; i < 1; i++)
> > if (sa_create(exchange, isakmp_sa->transport)) {
> > exchange_free(exchange);
> > - return;
> > + return 0; /* exchange_free() runs finalize */
> > }
> > }
> > msg = message_alloc(isakmp_sa->transport, 0, ISAKMP_HDR_SZ);
> > @@ -949,6 +948,8 @@ exchange_establish_p2(struct sa *isakmp_
> > msg->exchange = exchange;
> >
> > exchange_run(msg);
> > +
> > + return 0;
> > }
> >
> > /* Out of an incoming phase 1 message, setup an exchange. */
> > @@ -1200,9 +1201,11 @@ exchange_free_aux(void *v_exch)
> > free(exchange->id_i);
> > free(exchange->id_r);
> > free(exchange->keystate);
> > - if (exchange->doi && exchange->doi->free_exchange_data)
> > - exchange->doi->free_exchange_data(exchange->data);
> > - free(exchange->data);
> > + if (exchange->data) {
> > + if (exchange->doi && exchange->doi->free_exchange_data)
> > + exchange->doi->free_exchange_data(exchange->data);
> > + free(exchange->data);
> > + }
> > free(exchange->name);
> > if (exchange->recv_cert) {
> > handler = cert_get(exchange->recv_certtype);
> > @@ -1223,7 +1226,10 @@ exchange_free_aux(void *v_exch)
> > kn_close(exchange->policy_id);
> >
> > exchange_free_aca_list(exchange);
> > - LIST_REMOVE(exchange, link);
> > + if (exchange->linked) {
> > + LIST_REMOVE(exchange, link);
> > + exchange->linked = 0;
> > + }
> >
> > /* Tell potential finalize routine we never got there. */
> > if (exchange->finalize)
> > @@ -1260,6 +1266,7 @@ exchange_upgrade_p1(struct message *msg)
> > struct exchange *exchange = msg->exchange;
> >
> > LIST_REMOVE(exchange, link);
> > + exchange->linked = 0;
> > GET_ISAKMP_HDR_RCOOKIE(msg->iov[0].iov_base, exchange->cookies +
> > ISAKMP_HDR_ICOOKIE_LEN);
> > exchange_enter(exchange);
> > @@ -1746,6 +1753,8 @@ exchange_establish(char *name, void (*fi
> > LOG_DBG((LOG_EXCHANGE, 40, "exchange_establish:"
> > " returning in passive mode for exchange %s phase %d",
> > name, phase));
> > + if (finalize)
> > + finalize(0, arg, 1);
> > return;
> > }
> >
> > @@ -1772,10 +1781,13 @@ exchange_establish(char *name, void (*fi
> > if (!transport) {
> > log_print("exchange_establish: transport \"%s\" for "
> > "peer \"%s\" could not be created", trpt, name);
> > + if (finalize)
> > + finalize(0, arg, 1);
> > return;
> > }
> > - exchange_establish_p1(transport, 0, 0, name, 0, finalize, arg,
> > - stayalive);
> > + if (exchange_establish_p1(transport, 0, 0, name, 0, finalize,
> > + arg, stayalive) < 0 && finalize)
> > + finalize(0, arg, 1);
> > break;
> >
> > case 2:
> > @@ -1783,20 +1795,27 @@ exchange_establish(char *name, void (*fi
> > if (!peer) {
> > log_print("exchange_establish: No ISAKMP-peer given "
> > "for \"%s\"", name);
> > + if (finalize)
> > + finalize(0, arg, 1);
> > return;
> > }
> > isakmp_sa = sa_lookup_by_name(peer, 1);
> > if (!isakmp_sa) {
> > + /* freed by exchange_establish_finalize() */
> > name = strdup(name);
> > if (!name) {
> > log_error("exchange_establish: "
> > "strdup (\"%s\") failed", name);
> > + if (finalize)
> > + finalize(0, arg, 1);
> > return;
> > }
> > if (conf_get_num(peer, "Phase", 0) != 1) {
> > log_print("exchange_establish: "
> > "[%s]:ISAKMP-peer's (%s) phase is not 1",
> > name, peer);
> > + if (finalize)
> > + finalize(0, arg, 1);
> > free(name);
> > return;
> > }
> > @@ -1823,12 +1842,13 @@ exchange_establish(char *name, void (*fi
> > /* Indicate failure */
> > if (finalize)
> > finalize(0, arg, 1);
> > - free(name);
> > }
> > return;
> > - } else
> > - exchange_establish_p2(isakmp_sa, 0, name, 0, finalize,
> > - arg);
> > + } else {
> > + if (exchange_establish_p2(isakmp_sa, 0, name, 0,
> > + finalize, arg) < 0 && finalize)
> > + finalize(0, arg, 1);
> > + }
> > break;
> >
> > default:
> > Index: exchange.h
> > ===================================================================
> > RCS file: /cvs/src/sbin/isakmpd/exchange.h,v
> > retrieving revision 1.34
> > diff -u -p -r1.34 exchange.h
> > --- exchange.h 16 Jan 2015 06:39:58 -0000 1.34
> > +++ exchange.h 6 Aug 2017 13:54:41 -0000
> > @@ -55,6 +55,9 @@ struct exchange {
> > /* Link to exchanges with the same hash value. */
> > LIST_ENTRY(exchange) link;
> >
> > + /* This exchange is linked to the global exchange list. */
> > + int linked;
> > +
> > /* A name of the SAs this exchange will result in. XXX non unique? */
> > char *name;
> >
> > @@ -229,10 +232,10 @@ extern void exchange_free(struct exc
> > extern void exchange_free_aca_list(struct exchange *);
> > extern void exchange_establish(char *name, void (*)(struct exchange *,
> > void *, int), void *, int);
> > -extern void exchange_establish_p1(struct transport *, u_int8_t,
> > u_int32_t,
> > +extern int exchange_establish_p1(struct transport *, u_int8_t, u_int32_t,
> > char *, void *, void (*)(struct exchange *, void *, int),
> > void *, int);
> > -extern void exchange_establish_p2(struct sa *, u_int8_t, char *, void
> > *,
> > +extern int exchange_establish_p2(struct sa *, u_int8_t, char *, void
> > *,
> > void (*)(struct exchange *, void *, int), void *);
> > extern int exchange_gen_nonce(struct message *, size_t);
> > extern void exchange_init(void);
> >
>