kadmin: fix leak
[metze/heimdal/wip.git] / kadmin / server.c
index b339a9ac72343b94eb3756ae30e215a41db6bbcf..cc82644c6add9d602843ae9eb57155d25a3874dc 100644 (file)
@@ -43,29 +43,32 @@ kadmind_dispatch(void *kadm_handlep, krb5_boolean initial,
                 krb5_data *in, krb5_data *out)
 {
     kadm5_ret_t ret;
-    int32_t cmd, mask, tmp;
+    int32_t cmd, mask, kvno, tmp;
     kadm5_server_context *contextp = kadm_handlep;
     char client[128], name[128], name2[128];
     const char *op = "";
-    krb5_principal princ, princ2;
+    krb5_principal princ = NULL, princ2 = NULL;
     kadm5_principal_ent_rec ent, ent_prev;
-    char *password, *expression;
+    char *password = NULL, *expression;
     krb5_keyblock *new_keys;
     krb5_key_salt_tuple *ks_tuple = NULL;
-    krb5_boolean keepold = FALSE;
+    int keepold = FALSE;
     int n_ks_tuple = 0;
     int n_keys;
     char **princs;
     int n_princs;
     int keys_ok = 0;
     krb5_storage *sp;
+    int len;
 
     krb5_unparse_name_fixed(contextp->context, contextp->caller,
                            client, sizeof(client));
 
     sp = krb5_storage_from_data(in);
-    if (sp == NULL)
-       krb5_errx(contextp->context, 1, "out of memory");
+    if (sp == NULL) {
+       ret = krb5_enomem(contextp->context);
+       goto fail;
+    }
 
     krb5_ret_int32(sp, &cmd);
     switch(cmd){
@@ -75,20 +78,17 @@ kadmind_dispatch(void *kadm_handlep, krb5_boolean initial,
        if(ret)
            goto fail;
        ret = krb5_ret_int32(sp, &mask);
-       if(ret){
-           krb5_free_principal(contextp->context, princ);
+       if (ret)
            goto fail;
-       }
+
        mask |= KADM5_PRINCIPAL;
        krb5_unparse_name_fixed(contextp->context, princ, name, sizeof(name));
        krb5_warnx(contextp->context, "%s: %s %s", client, op, name);
 
         /* If the caller doesn't have KADM5_PRIV_GET, we're done. */
        ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_GET, princ);
-        if (ret) {
-           krb5_free_principal(contextp->context, princ);
+        if (ret)
            goto fail;
-        }
 
         /* Then check to see if it is ok to return keys */
         if ((mask & KADM5_KEY_DATA) != 0) {
@@ -116,7 +116,6 @@ kadmind_dispatch(void *kadm_handlep, krb5_boolean initial,
                  * modes request other things too, so in all likelihood this
                  * heuristic will not hurt any kadmin get uses.
                  */
-                krb5_free_principal(contextp->context, princ);
                 goto fail;
             }
         }
@@ -124,6 +123,10 @@ kadmind_dispatch(void *kadm_handlep, krb5_boolean initial,
        ret = kadm5_get_principal(kadm_handlep, princ, &ent, mask);
        krb5_storage_free(sp);
        sp = krb5_storage_emem();
+        if (sp == NULL) {
+            ret = krb5_enomem(contextp->context);
+            goto fail;
+        }
        krb5_store_int32(sp, ret);
        if (ret == 0){
            if (keys_ok)
@@ -132,7 +135,6 @@ kadmind_dispatch(void *kadm_handlep, krb5_boolean initial,
                kadm5_store_principal_ent_nokeys(sp, &ent);
            kadm5_free_principal_ent(kadm_handlep, &ent);
        }
-       krb5_free_principal(contextp->context, princ);
        break;
     }
     case kadm_delete:{
@@ -143,10 +145,8 @@ kadmind_dispatch(void *kadm_handlep, krb5_boolean initial,
        krb5_unparse_name_fixed(contextp->context, princ, name, sizeof(name));
        krb5_warnx(contextp->context, "%s: %s %s", client, op, name);
        ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_DELETE, princ);
-       if(ret){
-           krb5_free_principal(contextp->context, princ);
+       if (ret)
            goto fail;
-       }
 
         /*
          * There's no need to check that the caller has permission to
@@ -154,9 +154,12 @@ kadmind_dispatch(void *kadm_handlep, krb5_boolean initial,
          */
 
        ret = kadm5_delete_principal(kadm_handlep, princ);
-       krb5_free_principal(contextp->context, princ);
        krb5_storage_free(sp);
        sp = krb5_storage_emem();
+        if (sp == NULL) {
+            ret = krb5_enomem(contextp->context);
+            goto fail;
+        }
        krb5_store_int32(sp, ret);
        break;
     }
@@ -182,8 +185,6 @@ kadmind_dispatch(void *kadm_handlep, krb5_boolean initial,
                                          ent.principal);
        if(ret){
            kadm5_free_principal_ent(kadm_handlep, &ent);
-           memset(password, 0, strlen(password));
-           free(password);
            goto fail;
        }
         if ((mask & KADM5_TL_DATA)) {
@@ -194,18 +195,18 @@ kadmind_dispatch(void *kadm_handlep, krb5_boolean initial,
             ret = check_aliases(contextp, &ent, NULL);
             if (ret) {
                 kadm5_free_principal_ent(kadm_handlep, &ent);
-                memset(password, 0, strlen(password));
-                free(password);
                 goto fail;
             }
         }
        ret = kadm5_create_principal(kadm_handlep, &ent,
                                     mask, password);
        kadm5_free_principal_ent(kadm_handlep, &ent);
-       memset(password, 0, strlen(password));
-       free(password);
        krb5_storage_free(sp);
        sp = krb5_storage_emem();
+        if (sp == NULL) {
+            ret = krb5_enomem(contextp->context);
+            goto fail;
+        }
        krb5_store_int32(sp, ret);
        break;
     }
@@ -251,19 +252,49 @@ kadmind_dispatch(void *kadm_handlep, krb5_boolean initial,
        kadm5_free_principal_ent(kadm_handlep, &ent);
        krb5_storage_free(sp);
        sp = krb5_storage_emem();
+        if (sp == NULL) {
+            ret = krb5_enomem(contextp->context);
+            goto fail;
+        }
        krb5_store_int32(sp, ret);
        break;
     }
+    case kadm_prune:{
+        op = "PRUNE";
+        ret = krb5_ret_principal(sp, &princ);
+        if (ret)
+            goto fail;
+        ret = krb5_ret_int32(sp, &kvno);
+        if (ret == HEIM_ERR_EOF) {
+            kvno = 0;
+        } else if (ret) {
+            goto fail;
+        }
+        krb5_unparse_name_fixed(contextp->context, princ, name, sizeof(name));
+        krb5_warnx(contextp->context, "%s: %s %s", client, op, name);
+        ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_CPW, princ);
+        if (ret)
+            goto fail;
+
+        ret = kadm5_prune_principal(kadm_handlep, princ, kvno);
+        krb5_storage_free(sp);
+        sp = krb5_storage_emem();
+        if (sp == NULL) {
+            ret = krb5_enomem(contextp->context);
+            goto fail;
+        }
+        krb5_store_int32(sp, ret);
+        break;
+    }
     case kadm_rename:{
        op = "RENAME";
        ret = krb5_ret_principal(sp, &princ);
        if(ret)
            goto fail;
        ret = krb5_ret_principal(sp, &princ2);
-       if(ret){
-           krb5_free_principal(contextp->context, princ);
+       if (ret)
            goto fail;
-       }
+
        krb5_unparse_name_fixed(contextp->context, princ, name, sizeof(name));
        krb5_unparse_name_fixed(contextp->context, princ2,
                                 name2, sizeof(name2));
@@ -287,81 +318,61 @@ kadmind_dispatch(void *kadm_handlep, krb5_boolean initial,
                                                   princ);
             }
         }
-       if(ret){
-           krb5_free_principal(contextp->context, princ);
-           krb5_free_principal(contextp->context, princ2);
+       if (ret)
            goto fail;
-       }
+
        ret = kadm5_rename_principal(kadm_handlep, princ, princ2);
-       krb5_free_principal(contextp->context, princ);
-       krb5_free_principal(contextp->context, princ2);
        krb5_storage_free(sp);
        sp = krb5_storage_emem();
+        if (sp == NULL) {
+            ret = krb5_enomem(contextp->context);
+            goto fail;
+        }
        krb5_store_int32(sp, ret);
        break;
     }
     case kadm_chpass:{
+       krb5_boolean is_self_cpw, allow_self_cpw;
+
        op = "CHPASS";
        ret = krb5_ret_principal(sp, &princ);
        if (ret)
            goto fail;
        ret = krb5_ret_string(sp, &password);
-       if (ret) {
-           krb5_free_principal(contextp->context, princ);
+       if (ret)
            goto fail;
-       }
+
        ret = krb5_ret_int32(sp, &keepold);
-       if (ret && ret != HEIM_ERR_EOF) {
-           krb5_free_principal(contextp->context, princ);
+       if (ret && ret != HEIM_ERR_EOF)
            goto fail;
-       }
+
        krb5_unparse_name_fixed(contextp->context, princ, name, sizeof(name));
        krb5_warnx(contextp->context, "%s: %s %s", client, op, name);
 
        /*
-        * The change is allowed if at least one of:
-        *
-        * a) allowed by sysadmin
-        * b) it's for the principal him/herself and this was an
-        *    initial ticket, but then, check with the password quality
-        *    function.
-        * c) the user is on the CPW ACL.
+        * Change password requests are subject to ACLs unless the principal is
+        * changing their own password and the initial ticket flag is set, and
+        * the allow_self_change_password configuration option is TRUE.
         */
-
-       if (krb5_config_get_bool_default(contextp->context, NULL, TRUE,
-                                        "kadmin", "allow_self_change_password", NULL)
-           && initial
-           && krb5_principal_compare (contextp->context, contextp->caller,
-                                      princ))
-       {
-           krb5_data pwd_data;
-           const char *pwd_reason;
-
-           pwd_data.data = password;
-           pwd_data.length = strlen(password);
-
-           pwd_reason = kadm5_check_password_quality (contextp->context,
-                                                      princ, &pwd_data);
-           if (pwd_reason != NULL)
-               ret = KADM5_PASS_Q_DICT;
-           else
-               ret = 0;
-       } else
+       is_self_cpw =
+           krb5_principal_compare(contextp->context, contextp->caller, princ);
+       allow_self_cpw =
+           krb5_config_get_bool_default(contextp->context, NULL, TRUE,
+                                        "kadmin", "allow_self_change_password", NULL);
+       if (!(is_self_cpw && initial && allow_self_cpw)) {
            ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_CPW, princ);
-
-       if(ret) {
-           krb5_free_principal(contextp->context, princ);
-           memset(password, 0, strlen(password));
-           free(password);
-           goto fail;
+           if (ret)
+               goto fail;
        }
+
        ret = kadm5_chpass_principal_3(kadm_handlep, princ, keepold, 0, NULL,
                                       password);
-       krb5_free_principal(contextp->context, princ);
-       memset(password, 0, strlen(password));
-       free(password);
        krb5_storage_free(sp);
        sp = krb5_storage_emem();
+        if (sp == NULL) {
+            ret = krb5_enomem(contextp->context);
+            goto fail;
+        }
        krb5_store_int32(sp, ret);
        break;
     }
@@ -375,27 +386,23 @@ kadmind_dispatch(void *kadm_handlep, krb5_boolean initial,
        if(ret)
            goto fail;
        ret = krb5_ret_int32(sp, &n_key_data);
-       if (ret) {
-           krb5_free_principal(contextp->context, princ);
+       if (ret)
            goto fail;
-       }
+
        ret = krb5_ret_int32(sp, &keepold);
-       if (ret && ret != HEIM_ERR_EOF) {
-           krb5_free_principal(contextp->context, princ);
+       if (ret && ret != HEIM_ERR_EOF)
            goto fail;
-       }
+
        /* n_key_data will be squeezed into an int16_t below. */
        if (n_key_data < 0 || n_key_data >= 1 << 16 ||
            (size_t)n_key_data > UINT_MAX/sizeof(*key_data)) {
            ret = ERANGE;
-           krb5_free_principal(contextp->context, princ);
            goto fail;
        }
 
        key_data = malloc (n_key_data * sizeof(*key_data));
        if (key_data == NULL && n_key_data != 0) {
-           ret = ENOMEM;
-           krb5_free_principal(contextp->context, princ);
+           ret = krb5_enomem(contextp->context);
            goto fail;
        }
 
@@ -406,7 +413,6 @@ kadmind_dispatch(void *kadm_handlep, krb5_boolean initial,
 
                kadm5_free_key_data (contextp, &dummy, key_data);
                free (key_data);
-               krb5_free_principal(contextp->context, princ);
                goto fail;
            }
        }
@@ -425,7 +431,6 @@ kadmind_dispatch(void *kadm_handlep, krb5_boolean initial,
 
            kadm5_free_key_data (contextp, &dummy, key_data);
            free (key_data);
-           krb5_free_principal(contextp->context, princ);
            goto fail;
        }
        ret = kadm5_chpass_principal_with_key_3(kadm_handlep, princ, keepold,
@@ -435,16 +440,21 @@ kadmind_dispatch(void *kadm_handlep, krb5_boolean initial,
            kadm5_free_key_data (contextp, &dummy, key_data);
        }
        free (key_data);
-       krb5_free_principal(contextp->context, princ);
        krb5_storage_free(sp);
        sp = krb5_storage_emem();
+        if (sp == NULL) {
+            ret = krb5_enomem(contextp->context);
+            goto fail;
+        }
        krb5_store_int32(sp, ret);
        break;
     }
     case kadm_randkey:{
+        size_t i;
+
        op = "RANDKEY";
        ret = krb5_ret_principal(sp, &princ);
-       if(ret)
+       if (ret)
            goto fail;
        krb5_unparse_name_fixed(contextp->context, princ, name, sizeof(name));
        krb5_warnx(contextp->context, "%s: %s %s", client, op, name);
@@ -461,66 +471,75 @@ kadmind_dispatch(void *kadm_handlep, krb5_boolean initial,
        else
            ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_CPW, princ);
 
-       if(ret) {
-           krb5_free_principal(contextp->context, princ);
+       if (ret)
            goto fail;
-       }
 
        /*
         * See comments in kadm5_c_randkey_principal() regarding the
         * protocol.
         */
        ret = krb5_ret_int32(sp, &keepold);
-       if (ret != 0 && ret != HEIM_ERR_EOF) {
-           krb5_free_principal(contextp->context, princ);
+       if (ret != 0 && ret != HEIM_ERR_EOF)
            goto fail;
-       }
 
        ret = krb5_ret_int32(sp, &n_ks_tuple);
-       if (ret != 0 && ret != HEIM_ERR_EOF) {
-           krb5_free_principal(contextp->context, princ);
+       if (ret == HEIM_ERR_EOF) {
+            const char *enctypes;
+           size_t n;
+
+            enctypes = krb5_config_get_string(contextp->context, NULL,
+                                              "realms",
+                                              krb5_principal_get_realm(contextp->context,
+                                                                       princ),
+                                              "supported_enctypes", NULL);
+            if (enctypes == NULL || enctypes[0] == '\0')
+                enctypes = "aes128-cts-hmac-sha1-96";
+            ret = krb5_string_to_keysalts2(contextp->context, enctypes,
+                                           &n, &ks_tuple);
+           n_ks_tuple = n;
+        }
+        if (ret != 0)
            goto fail;
-       } else if (ret == 0) {
-           size_t i;
 
-           if (n_ks_tuple < 0) {
-               ret = EOVERFLOW;
-               krb5_free_principal(contextp->context, princ);
-               goto fail;
-           }
+        if (n_ks_tuple < 0) {
+            ret = EOVERFLOW;
+            goto fail;
+        }
 
-           if ((ks_tuple = calloc(n_ks_tuple, sizeof (*ks_tuple))) == NULL) {
-               ret = errno;
-               krb5_free_principal(contextp->context, princ);
-               goto fail;
-           }
+        if ((ks_tuple = calloc(n_ks_tuple, sizeof (*ks_tuple))) == NULL) {
+            ret = errno;
+            goto fail;
+        }
 
-           for (i = 0; i < n_ks_tuple; i++) {
-               ret = krb5_ret_int32(sp, &ks_tuple[i].ks_enctype);
-               if (ret != 0) {
-                   krb5_free_principal(contextp->context, princ);
-                   goto fail;
-               }
-               ret = krb5_ret_int32(sp, &ks_tuple[i].ks_salttype);
-               if (ret != 0) {
-                   krb5_free_principal(contextp->context, princ);
-                   goto fail;
-               }
-           }
-       }
+        for (i = 0; i < n_ks_tuple; i++) {
+            ret = krb5_ret_int32(sp, &ks_tuple[i].ks_enctype);
+            if (ret != 0) {
+                free(ks_tuple);
+                goto fail;
+            }
+            ret = krb5_ret_int32(sp, &ks_tuple[i].ks_salttype);
+            if (ret != 0) {
+                free(ks_tuple);
+                goto fail;
+            }
+        }
        ret = kadm5_randkey_principal_3(kadm_handlep, princ, keepold,
                                        n_ks_tuple, ks_tuple, &new_keys,
                                        &n_keys);
-       krb5_free_principal(contextp->context, princ);
+        free(ks_tuple);
 
        krb5_storage_free(sp);
        sp = krb5_storage_emem();
+        if (sp == NULL) {
+            ret = krb5_enomem(contextp->context);
+            goto fail;
+        }
        krb5_store_int32(sp, ret);
-       if(ret == 0){
-           int i;
+       if (ret == 0){
            krb5_store_int32(sp, n_keys);
-           for(i = 0; i < n_keys; i++){
-               krb5_store_keyblock(sp, new_keys[i]);
+           for (i = 0; i < n_keys; i++){
+                if (ret == 0)
+                    ret = krb5_store_keyblock(sp, new_keys[i]);
                krb5_free_keyblock_contents(contextp->context, &new_keys[i]);
            }
            free(new_keys);
@@ -532,6 +551,10 @@ kadmind_dispatch(void *kadm_handlep, krb5_boolean initial,
        ret = kadm5_get_privs(kadm_handlep, &privs);
        krb5_storage_free(sp);
        sp = krb5_storage_emem();
+        if (sp == NULL) {
+            ret = krb5_enomem(contextp->context);
+            goto fail;
+        }
        krb5_store_int32(sp, ret);
        if(ret == 0)
            krb5_store_uint32(sp, privs);
@@ -559,6 +582,10 @@ kadmind_dispatch(void *kadm_handlep, krb5_boolean initial,
        free(expression);
        krb5_storage_free(sp);
        sp = krb5_storage_emem();
+        if (sp == NULL) {
+            ret = krb5_enomem(contextp->context);
+            goto fail;
+        }
        krb5_store_int32(sp, ret);
        if(ret == 0){
            int i;
@@ -573,18 +600,42 @@ kadmind_dispatch(void *kadm_handlep, krb5_boolean initial,
        krb5_warnx(contextp->context, "%s: UNKNOWN OP %d", client, cmd);
        krb5_storage_free(sp);
        sp = krb5_storage_emem();
+        if (sp == NULL) {
+            ret = krb5_enomem(contextp->context);
+            goto fail;
+        }
        krb5_store_int32(sp, KADM5_FAILURE);
        break;
     }
+    if (password != NULL) {
+       len = strlen(password);
+       memset_s(password, len, 0, len);
+       free(password);
+    }
     krb5_storage_to_data(sp, out);
     krb5_storage_free(sp);
+    if (princ != NULL)
+       krb5_free_principal(contextp->context, princ);
+    if (princ2 != NULL)
+       krb5_free_principal(contextp->context, princ2);
     return 0;
 fail:
+    if (password != NULL) {
+       len = strlen(password);
+       memset_s(password, len, 0, len);
+       free(password);
+    }
     krb5_warn(contextp->context, ret, "%s", op);
-    krb5_storage_seek(sp, 0, SEEK_SET);
-    krb5_store_int32(sp, ret);
-    krb5_storage_to_data(sp, out);
-    krb5_storage_free(sp);
+    if (sp != NULL) {
+       krb5_storage_seek(sp, 0, SEEK_SET);
+       krb5_store_int32(sp, ret);
+       krb5_storage_to_data(sp, out);
+       krb5_storage_free(sp);
+    }
+    if (princ != NULL)
+       krb5_free_principal(contextp->context, princ);
+    if (princ2 != NULL)
+       krb5_free_principal(contextp->context, princ2);
     return 0;
 }
 
@@ -722,9 +773,12 @@ v5_loop (krb5_context contextp,
        if(ret)
            krb5_err(contextp, 1, ret, "krb5_read_priv_message");
        doing_useful_work = 1;
-       kadmind_dispatch(kadm_handlep, initial, &in, &out);
+       ret = kadmind_dispatch(kadm_handlep, initial, &in, &out);
+       if (ret)
+           krb5_err(contextp, 1, ret, "kadmind_dispatch");
        krb5_data_free(&in);
        ret = krb5_write_priv_message(contextp, ac, &fd, &out);
+       krb5_data_free(&out);
        if(ret)
            krb5_err(contextp, 1, ret, "krb5_write_priv_message");
     }
@@ -754,7 +808,7 @@ handle_v5(krb5_context contextp,
     krb5_boolean initial;
     krb5_auth_context ac = NULL;
 
-    unsigned kadm_version;
+    unsigned kadm_version = 1;
     kadm5_config_params realm_params;
 
     ret = krb5_recvauth_match_version(contextp, &ac, &fd,
@@ -838,3 +892,4 @@ kadmind_loop(krb5_context contextp,
 
     return 0;
 }
+