]> granicus.if.org Git - php/commitdiff
fix memory leaks in ext/ldap/ldap.c
authorPaweł Tomulik <ptomulik@meil.pw.edu.pl>
Fri, 10 Jul 2020 19:55:42 +0000 (21:55 +0200)
committerCôme Chilliet <mcmic@php.net>
Tue, 28 Jul 2020 08:23:10 +0000 (10:23 +0200)
NEWS
ext/ldap/ldap.c

diff --git a/NEWS b/NEWS
index 0a6e60d4192ec7f18383b4e0170512c384d45fef..cc882ef1e21312309c26c0ec4e9e8f676aecb23d 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -6,6 +6,9 @@ PHP                                                                        NEWS
   . Fixed bug #79884 (PHP_CONFIG_FILE_PATH is meaningless). (cmb)
   . Fixed bug #77932 (File extensions are case-sensitive). (cmb)
 
+- LDAP:
+  . Fixed memory leaks. (ptomulik)
+
 ?? ??? ????, PHP 7.3.21
 
 - Apache:
index 2fce67a4c8bf385eff8b7135c647a8beb7e5e087..d5a90b879e847e73afa3b4b08165fd216b48ae67 100644 (file)
@@ -167,15 +167,17 @@ static void _php_ldap_control_to_array(LDAP *ld, LDAPControl* ctrl, zval* array,
                }
        } else if (strcmp(ctrl->ldctl_oid, LDAP_CONTROL_PAGEDRESULTS) == 0) {
                int lestimated, rc;
-               struct berval lcookie;
+               struct berval lcookie = { 0L, NULL };
                zval value;
 
                if (ctrl->ldctl_value.bv_len) {
+                       /* ldap_parse_pageresponse_control() allocates lcookie.bv_val */
                        rc = ldap_parse_pageresponse_control(ld, ctrl, &lestimated, &lcookie);
                } else {
                        /* ldap_parse_pageresponse_control will crash if value is empty */
                        rc = -1;
                }
+
                if ( rc == LDAP_SUCCESS ) {
                        array_init(&value);
                        add_assoc_long(&value, "size", lestimated);
@@ -184,6 +186,10 @@ static void _php_ldap_control_to_array(LDAP *ld, LDAPControl* ctrl, zval* array,
                } else {
                        add_assoc_null(array, "value");
                }
+
+               if (lcookie.bv_val) {
+                       ldap_memfree(lcookie.bv_val);
+               }
        } else if ((strcmp(ctrl->ldctl_oid, LDAP_CONTROL_PRE_READ) == 0) || (strcmp(ctrl->ldctl_oid, LDAP_CONTROL_POST_READ) == 0)) {
                BerElement *ber;
                struct berval bv;
@@ -302,27 +308,23 @@ static int _php_ldap_control_from_array(LDAP *ld, LDAPControl** ctrl, zval* arra
                control_iscritical = 0;
        }
 
-       struct berval *control_value = NULL;
+       BerElement *ber = NULL;
+       struct berval control_value = { 0L, NULL };
+       int control_value_alloc = 0;
 
        if ((val = zend_hash_str_find(Z_ARRVAL_P(array), "value", sizeof("value") - 1)) != NULL) {
                if (Z_TYPE_P(val) != IS_ARRAY) {
-                       control_value = ber_memalloc(sizeof * control_value);
-                       if (control_value == NULL) {
+                       tmpstring = zval_get_string(val);
+                       if (EG(exception)) {
                                rc = -1;
-                               php_error_docref(NULL, E_WARNING, "Failed to allocate control value");
-                       } else {
-                               tmpstring = zval_get_string(val);
-                               if (EG(exception)) {
-                                       rc = -1;
-                                       goto failure;
-                               }
-                               control_value->bv_val = ZSTR_VAL(tmpstring);
-                               control_value->bv_len = ZSTR_LEN(tmpstring);
+                               goto failure;
                        }
+                       control_value.bv_val = ZSTR_VAL(tmpstring);
+                       control_value.bv_len = ZSTR_LEN(tmpstring);
                } else if (strcmp(ZSTR_VAL(control_oid), LDAP_CONTROL_PAGEDRESULTS) == 0) {
                        zval* tmp;
                        int pagesize = 1;
-                       struct berval cookie = { 0, NULL };
+                       struct berval cookie = { 0L, NULL };
                        if ((tmp = zend_hash_str_find(Z_ARRVAL_P(val), "size", sizeof("size") - 1)) != NULL) {
                                pagesize = zval_get_long(tmp);
                        }
@@ -335,15 +337,11 @@ static int _php_ldap_control_from_array(LDAP *ld, LDAPControl** ctrl, zval* arra
                                cookie.bv_val = ZSTR_VAL(tmpstring);
                                cookie.bv_len = ZSTR_LEN(tmpstring);
                        }
-                       control_value = ber_memalloc(sizeof * control_value);
-                       if (control_value == NULL) {
-                               rc = -1;
-                               php_error_docref(NULL, E_WARNING, "Failed to allocate control value");
-                       } else {
-                               rc = ldap_create_page_control_value(ld, pagesize, &cookie, control_value);
-                               if (rc != LDAP_SUCCESS) {
-                                       php_error_docref(NULL, E_WARNING, "Failed to create paged result control value: %s (%d)", ldap_err2string(rc), rc);
-                               }
+                       /* ldap_create_page_control_value() allocates memory for control_value.bv_val */
+                       control_value_alloc = 1;
+                       rc = ldap_create_page_control_value(ld, pagesize, &cookie, &control_value);
+                       if (rc != LDAP_SUCCESS) {
+                               php_error_docref(NULL, E_WARNING, "Failed to create paged result control value: %s (%d)", ldap_err2string(rc), rc);
                        }
                } else if (strcmp(ZSTR_VAL(control_oid), LDAP_CONTROL_ASSERT) == 0) {
                        zval* tmp;
@@ -357,19 +355,15 @@ static int _php_ldap_control_from_array(LDAP *ld, LDAPControl** ctrl, zval* arra
                                        rc = -1;
                                        goto failure;
                                }
-                               control_value = ber_memalloc(sizeof * control_value);
-                               if (control_value == NULL) {
-                                       rc = -1;
-                                       php_error_docref(NULL, E_WARNING, "Failed to allocate control value");
-                               } else {
-                                       /* ldap_create_assertion_control_value does not reset ld_errno, we need to do it ourselves
-                                          See http://www.openldap.org/its/index.cgi/Incoming?id=8674 */
-                                       int success = LDAP_SUCCESS;
-                                       ldap_set_option(ld, LDAP_OPT_RESULT_CODE, &success);
-                                       rc = ldap_create_assertion_control_value(ld, ZSTR_VAL(assert), control_value);
-                                       if (rc != LDAP_SUCCESS) {
-                                               php_error_docref(NULL, E_WARNING, "Failed to create assert control value: %s (%d)", ldap_err2string(rc), rc);
-                                       }
+                               /* ldap_create_assertion_control_value does not reset ld_errno, we need to do it ourselves
+                                        See http://www.openldap.org/its/index.cgi/Incoming?id=8674 */
+                               int success = LDAP_SUCCESS;
+                               ldap_set_option(ld, LDAP_OPT_RESULT_CODE, &success);
+                               /* ldap_create_assertion_control_value() allocates memory for control_value.bv_val */
+                               control_value_alloc = 1;
+                               rc = ldap_create_assertion_control_value(ld, ZSTR_VAL(assert), &control_value);
+                               if (rc != LDAP_SUCCESS) {
+                                       php_error_docref(NULL, E_WARNING, "Failed to create assert control value: %s (%d)", ldap_err2string(rc), rc);
                                }
                                zend_string_release(assert);
                        }
@@ -379,9 +373,8 @@ static int _php_ldap_control_from_array(LDAP *ld, LDAPControl** ctrl, zval* arra
                                rc = -1;
                                php_error_docref(NULL, E_WARNING, "Filter missing from control value array");
                        } else {
-                               BerElement *vrber = ber_alloc_t(LBER_USE_DER);
-                               control_value = ber_memalloc(sizeof * control_value);
-                               if ((control_value == NULL) || (vrber == NULL)) {
+                               ber = ber_alloc_t(LBER_USE_DER);
+                               if (ber == NULL) {
                                        rc = -1;
                                        php_error_docref(NULL, E_WARNING, "Failed to allocate control value");
                                } else {
@@ -390,14 +383,11 @@ static int _php_ldap_control_from_array(LDAP *ld, LDAPControl** ctrl, zval* arra
                                                rc = -1;
                                                goto failure;
                                        }
-                                       if (ldap_put_vrFilter(vrber, ZSTR_VAL(tmpstring)) == -1) {
-                                               ber_free(vrber, 1);
+                                       if (ldap_put_vrFilter(ber, ZSTR_VAL(tmpstring)) == -1) {
                                                rc = -1;
                                                php_error_docref(NULL, E_WARNING, "Failed to create control value: Bad ValuesReturnFilter: %s", ZSTR_VAL(tmpstring));
-                                       } else {
-                                               if (ber_flatten2(vrber, control_value, 0) == -1) {
-                                                       rc = -1;
-                                               }
+                                       } else if (ber_flatten2(ber, &control_value, control_value_alloc) == -1) {
+                                               rc = -1;
                                        }
                                }
                        }
@@ -407,10 +397,9 @@ static int _php_ldap_control_from_array(LDAP *ld, LDAPControl** ctrl, zval* arra
                                rc = -1;
                                php_error_docref(NULL, E_WARNING, "Attributes list missing from control value array");
                        } else {
-                               BerElement *ber = ber_alloc_t(LBER_USE_DER);
+                               ber = ber_alloc_t(LBER_USE_DER);
 
-                               control_value = ber_memalloc(sizeof * control_value);
-                               if ((control_value == NULL) || (ber == NULL)) {
+                               if (ber == NULL) {
                                        rc = -1;
                                        php_error_docref(NULL, E_WARNING, "Failed to allocate control value");
                                } else {
@@ -446,7 +435,7 @@ static int _php_ldap_control_from_array(LDAP *ld, LDAPControl** ctrl, zval* arra
                                                php_error_docref(NULL, E_WARNING, "Failed to encode attribute list");
                                        } else {
                                                int err;
-                                               err = ber_flatten2(ber, control_value, 0);
+                                               err = ber_flatten2(ber, &control_value, control_value_alloc);
                                                if (err < 0) {
                                                        rc = -1;
                                                        php_error_docref(NULL, E_WARNING, "Failed to encode control value (%d)", err);
@@ -505,15 +494,11 @@ static int _php_ldap_control_from_array(LDAP *ld, LDAPControl** ctrl, zval* arra
                                }
                        }
                        sort_keys[num_keys] = NULL;
-                       control_value = ber_memalloc(sizeof * control_value);
-                       if (control_value == NULL) {
-                               rc = -1;
-                               php_error_docref(NULL, E_WARNING, "Failed to allocate control value");
-                       } else {
-                               rc = ldap_create_sort_control_value(ld, sort_keys, control_value);
-                               if (rc != LDAP_SUCCESS) {
-                                       php_error_docref(NULL, E_WARNING, "Failed to create sort control value: %s (%d)", ldap_err2string(rc), rc);
-                               }
+                       /* ldap_create_sort_control_value() allocates memory for control_value.bv_val */
+                       control_value_alloc = 1;
+                       rc = ldap_create_sort_control_value(ld, sort_keys, &control_value);
+                       if (rc != LDAP_SUCCESS) {
+                               php_error_docref(NULL, E_WARNING, "Failed to create sort control value: %s (%d)", ldap_err2string(rc), rc);
                        }
                } else if (strcmp(ZSTR_VAL(control_oid), LDAP_CONTROL_VLVREQUEST) == 0) {
                        zval* tmp;
@@ -575,15 +560,11 @@ static int _php_ldap_control_from_array(LDAP *ld, LDAPControl** ctrl, zval* arra
                                vlvInfo.ldvlv_context = NULL;
                        }
 
-                       control_value = ber_memalloc(sizeof * control_value);
-                       if (control_value == NULL) {
-                               rc = -1;
-                               php_error_docref(NULL, E_WARNING, "Failed to allocate control value");
-                       } else {
-                               rc = ldap_create_vlv_control_value(ld, &vlvInfo, control_value);
-                               if (rc != LDAP_SUCCESS) {
-                                       php_error_docref(NULL, E_WARNING, "Failed to create VLV control value: %s (%d)", ldap_err2string(rc), rc);
-                               }
+                       /* ldap_create_vlv_control_value() allocates memory for control_value.bv_val */
+                       control_value_alloc = 1;
+                       rc = ldap_create_vlv_control_value(ld, &vlvInfo, &control_value);
+                       if (rc != LDAP_SUCCESS) {
+                               php_error_docref(NULL, E_WARNING, "Failed to create VLV control value: %s (%d)", ldap_err2string(rc), rc);
                        }
                } else {
                        php_error_docref(NULL, E_WARNING, "Control OID %s does not expect an array as value", ZSTR_VAL(control_oid));
@@ -592,7 +573,7 @@ static int _php_ldap_control_from_array(LDAP *ld, LDAPControl** ctrl, zval* arra
        }
 
        if (rc == LDAP_SUCCESS) {
-               rc = ldap_control_create(ZSTR_VAL(control_oid), control_iscritical, control_value, 1, ctrl);
+               rc = ldap_control_create(ZSTR_VAL(control_oid), control_iscritical, &control_value, 1, ctrl);
        }
 
 failure:
@@ -614,9 +595,11 @@ failure:
                }
                efree(tmpstrings2);
        }
-       if (control_value != NULL) {
-               ber_memfree(control_value);
-               control_value = NULL;
+       if (control_value.bv_val != NULL && control_value_alloc != 0) {
+               ber_memfree(control_value.bv_val);
+       }
+       if (ber != NULL) {
+               ber_free(ber, 1);
        }
        if (ldap_attrs != NULL) {
                efree(ldap_attrs);
@@ -1488,7 +1471,7 @@ static void php_ldap_do_search(INTERNAL_FUNCTION_PARAMETERS, int scope)
        zend_string *ldap_filter = NULL, *ldap_base_dn = NULL;
        char **ldap_attrs = NULL;
        ldap_linkdata *ld = NULL;
-       LDAPMessage *ldap_res;
+       LDAPMessage *ldap_res = NULL;
        LDAPControl **lserverctrls = NULL;
        int ldap_attrsonly = 0, ldap_sizelimit = -1, ldap_timelimit = -1, ldap_deref = -1;
        int old_ldap_sizelimit = -1, old_ldap_timelimit = -1, old_ldap_deref = -1;
@@ -1688,6 +1671,11 @@ cleanup_parallel:
                        && errno != LDAP_REFERRAL
 #endif
                ) {
+                       /* ldap_res should be freed regardless of return value of ldap_search_ext_s()
+                        * see: https://linux.die.net/man/3/ldap_search_ext_s */
+                       if (ldap_res != NULL) {
+                               ldap_msgfree(ldap_res);
+                       }
                        php_error_docref(NULL, E_WARNING, "Search: %s", ldap_err2string(errno));
                        ret = 0;
                } else {
@@ -4016,7 +4004,7 @@ PHP_FUNCTION(ldap_control_paged_result)
        zval *link;
        char *cookie = NULL;
        size_t cookie_len = 0;
-       struct berval lcookie = { 0, NULL };
+       struct berval lcookie = { 0L, NULL };
        ldap_linkdata *ld;
        LDAP *ldap;
        BerElement *ber = NULL;
@@ -4311,38 +4299,31 @@ PHP_FUNCTION(ldap_exop)
 PHP_FUNCTION(ldap_exop_passwd)
 {
        zval *link, *serverctrls;
-       struct berval luser, loldpw, lnewpw, lgenpasswd;
-       LDAPControl **lserverctrls = NULL, **requestctrls = NULL;
-       LDAPControl *ctrl, **ctrlp;
-       LDAPMessage* ldap_res;
+       struct berval luser = { 0L, NULL };
+       struct berval loldpw = { 0L, NULL };
+       struct berval lnewpw = { 0L, NULL };
+       struct berval lgenpasswd = { 0L, NULL };
+       LDAPControl *ctrl, **lserverctrls = NULL, *requestctrls[2] = { NULL, NULL };
+       LDAPMessage* ldap_res = NULL;
        ldap_linkdata *ld;
        int rc, myargcount = ZEND_NUM_ARGS(), msgid, err;
-       char* errmsg;
-
-       luser.bv_len = 0;
-       loldpw.bv_len = 0;
-       lnewpw.bv_len = 0;
+       char* errmsg = NULL;
 
        if (zend_parse_parameters(myargcount, "r|sssz/", &link, &luser.bv_val, &luser.bv_len, &loldpw.bv_val, &loldpw.bv_len, &lnewpw.bv_val, &lnewpw.bv_len, &serverctrls) == FAILURE) {
                return;
        }
 
        if ((ld = (ldap_linkdata *)zend_fetch_resource(Z_RES_P(link), "ldap link", le_link)) == NULL) {
-               RETURN_FALSE;
+               RETVAL_FALSE;
+               goto cleanup;
        }
 
        switch (myargcount) {
                case 5:
-                       requestctrls = safe_emalloc(2, sizeof(*requestctrls), 0);
-                       *requestctrls = NULL;
-                       ctrlp = requestctrls;
-
+                       /* ldap_create_passwordpolicy_control() allocates ctrl */
                        if (ldap_create_passwordpolicy_control(ld->link, &ctrl) == LDAP_SUCCESS) {
-                               *ctrlp = ctrl;
-                               ++ctrlp;
+                               requestctrls[0] = ctrl;
                        }
-
-                       *ctrlp = NULL;
        }
 
        /* asynchronous call to get result and controls */
@@ -4352,35 +4333,44 @@ PHP_FUNCTION(ldap_exop_passwd)
                requestctrls,
                NULL, &msgid);
 
-       if (requestctrls != NULL) {
-               efree(requestctrls);
+       if (requestctrls[0] != NULL) {
+               ldap_control_free(requestctrls[0]);
        }
 
        if (rc != LDAP_SUCCESS ) {
                php_error_docref(NULL, E_WARNING, "Passwd modify extended operation failed: %s (%d)", ldap_err2string(rc), rc);
-               RETURN_FALSE;
+               RETVAL_FALSE;
+               goto cleanup;
        }
 
        rc = ldap_result(ld->link, msgid, 1 /* LDAP_MSG_ALL */, NULL, &ldap_res);
        if ((rc < 0) || !ldap_res) {
                rc = _get_lderrno(ld->link);
                php_error_docref(NULL, E_WARNING, "Passwd modify extended operation failed: %s (%d)", ldap_err2string(rc), rc);
-               RETURN_FALSE;
+               RETVAL_FALSE;
+               goto cleanup;
        }
 
        rc = ldap_parse_passwd(ld->link, ldap_res, &lgenpasswd);
        if( rc != LDAP_SUCCESS ) {
                php_error_docref(NULL, E_WARNING, "Passwd modify extended operation failed: %s (%d)", ldap_err2string(rc), rc);
-               ldap_msgfree(ldap_res);
-               RETURN_FALSE;
+               RETVAL_FALSE;
+               goto cleanup;
        }
 
-       rc = ldap_parse_result(ld->link, ldap_res, &err, NULL, &errmsg, NULL, (myargcount > 4 ? &lserverctrls : NULL), 1);
+       rc = ldap_parse_result(ld->link, ldap_res, &err, NULL, &errmsg, NULL, (myargcount > 4 ? &lserverctrls : NULL), 0);
        if( rc != LDAP_SUCCESS ) {
                php_error_docref(NULL, E_WARNING, "Passwd modify extended operation failed: %s (%d)", ldap_err2string(rc), rc);
-               RETURN_FALSE;
+               RETVAL_FALSE;
+               goto cleanup;
+       }
+
+       if (myargcount > 4) {
+               zval_ptr_dtor(serverctrls);
+               _php_ldap_controls_to_array(ld->link, lserverctrls, serverctrls, 0);
        }
 
+       /* return */
        if (lnewpw.bv_len == 0) {
                if (lgenpasswd.bv_len == 0) {
                        RETVAL_EMPTY_STRING();
@@ -4394,12 +4384,16 @@ PHP_FUNCTION(ldap_exop_passwd)
                RETVAL_FALSE;
        }
 
-       if (myargcount > 4) {
-               zval_ptr_dtor(serverctrls);
-               _php_ldap_controls_to_array(ld->link, lserverctrls, serverctrls, 0);
+cleanup:
+       if (lgenpasswd.bv_val != NULL) {
+               ldap_memfree(lgenpasswd.bv_val);
+       }
+       if (ldap_res != NULL) {
+               ldap_msgfree(ldap_res);
+       }
+       if (errmsg != NULL) {
+               ldap_memfree(errmsg);
        }
-
-       ldap_memfree(lgenpasswd.bv_val);
 }
 /* }}} */
 #endif