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?

> 
> 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);
> 

Reply via email to