LCOV - code coverage report
Current view: top level - third_party/heimdal/lib/hdb - ext.c (source / functions) Hit Total Coverage
Test: coverage report for master 2f515e9b Lines: 27 374 7.2 %
Date: 2024-04-21 15:09:00 Functions: 4 28 14.3 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2004 - 2005 Kungliga Tekniska Högskolan
       3             :  * (Royal Institute of Technology, Stockholm, Sweden).
       4             :  * All rights reserved.
       5             :  *
       6             :  * Redistribution and use in source and binary forms, with or without
       7             :  * modification, are permitted provided that the following conditions
       8             :  * are met:
       9             :  *
      10             :  * 1. Redistributions of source code must retain the above copyright
      11             :  *    notice, this list of conditions and the following disclaimer.
      12             :  *
      13             :  * 2. Redistributions in binary form must reproduce the above copyright
      14             :  *    notice, this list of conditions and the following disclaimer in the
      15             :  *    documentation and/or other materials provided with the distribution.
      16             :  *
      17             :  * 3. Neither the name of the Institute nor the names of its contributors
      18             :  *    may be used to endorse or promote products derived from this software
      19             :  *    without specific prior written permission.
      20             :  *
      21             :  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
      22             :  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
      23             :  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
      24             :  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
      25             :  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
      26             :  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
      27             :  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
      28             :  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
      29             :  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
      30             :  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
      31             :  * SUCH DAMAGE.
      32             :  */
      33             : 
      34             : #include "hdb_locl.h"
      35             : #include <der.h>
      36             : 
      37             : krb5_error_code
      38           0 : hdb_entry_check_mandatory(krb5_context context, const hdb_entry *ent)
      39             : {
      40           0 :     size_t i;
      41             : 
      42           0 :     if (ent->extensions == NULL)
      43           0 :         return 0;
      44             : 
      45             :     /*
      46             :      * check for unknown extensions and if they were tagged mandatory
      47             :      */
      48             : 
      49           0 :     for (i = 0; i < ent->extensions->len; i++) {
      50           0 :         if (ent->extensions->val[i].data.element !=
      51             :             choice_HDB_extension_data_asn1_ellipsis)
      52           0 :             continue;
      53           0 :         if (ent->extensions->val[i].mandatory) {
      54           0 :             krb5_set_error_message(context, HDB_ERR_MANDATORY_OPTION,
      55             :                                    "Principal has unknown "
      56             :                                    "mandatory extension");
      57           0 :             return HDB_ERR_MANDATORY_OPTION;
      58             :         }
      59             :     }
      60           0 :     return 0;
      61             : }
      62             : 
      63             : HDB_extension *
      64        7370 : hdb_find_extension(const hdb_entry *entry, int type)
      65             : {
      66          72 :     size_t i;
      67             : 
      68        7370 :     if (entry->extensions == NULL)
      69        1890 :         return NULL;
      70             : 
      71        5468 :     for (i = 0; i < entry->extensions->len; i++)
      72        5462 :         if (entry->extensions->val[i].data.element == (unsigned)type)
      73        5456 :             return &entry->extensions->val[i];
      74           6 :     return NULL;
      75             : }
      76             : 
      77             : /*
      78             :  * Replace the extension `ext' in `entry'. Make a copy of the
      79             :  * extension, so the caller must still free `ext' on both success and
      80             :  * failure. Returns 0 or error code.
      81             :  */
      82             : 
      83             : krb5_error_code
      84        1645 : hdb_replace_extension(krb5_context context,
      85             :                       hdb_entry *entry,
      86             :                       const HDB_extension *ext)
      87             : {
      88          18 :     HDB_extension *ext2;
      89          18 :     int ret;
      90             : 
      91        1645 :     ext2 = NULL;
      92             : 
      93        1645 :     if (entry->extensions == NULL) {
      94        1645 :         entry->extensions = calloc(1, sizeof(*entry->extensions));
      95        1645 :         if (entry->extensions == NULL) {
      96           0 :             krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
      97           0 :             return ENOMEM;
      98             :         }
      99           0 :     } else if (ext->data.element != choice_HDB_extension_data_asn1_ellipsis) {
     100           0 :         ext2 = hdb_find_extension(entry, ext->data.element);
     101             :     } else {
     102             :         /*
     103             :          * This is an unknown extension, and we are asked to replace a
     104             :          * possible entry in `entry' that is of the same type. This
     105             :          * might seem impossible, but ASN.1 CHOICE comes to our
     106             :          * rescue. The first tag in each branch in the CHOICE is
     107             :          * unique, so just find the element in the list that have the
     108             :          * same tag was we are putting into the list.
     109             :          */
     110           0 :         Der_class replace_class, list_class;
     111           0 :         Der_type replace_type, list_type;
     112           0 :         unsigned int replace_tag, list_tag;
     113           0 :         size_t size;
     114           0 :         size_t i;
     115             : 
     116           0 :         ret = der_get_tag(ext->data.u.asn1_ellipsis.data,
     117           0 :                           ext->data.u.asn1_ellipsis.length,
     118             :                           &replace_class, &replace_type, &replace_tag,
     119             :                           &size);
     120           0 :         if (ret) {
     121           0 :             krb5_set_error_message(context, ret, "hdb: failed to decode "
     122             :                                    "replacement hdb extension");
     123           0 :             return ret;
     124             :         }
     125             : 
     126           0 :         for (i = 0; i < entry->extensions->len; i++) {
     127           0 :             HDB_extension *ext3 = &entry->extensions->val[i];
     128             : 
     129           0 :             if (ext3->data.element != choice_HDB_extension_data_asn1_ellipsis)
     130           0 :                 continue;
     131             : 
     132           0 :             ret = der_get_tag(ext3->data.u.asn1_ellipsis.data,
     133             :                               ext3->data.u.asn1_ellipsis.length,
     134             :                               &list_class, &list_type, &list_tag,
     135             :                               &size);
     136           0 :             if (ret) {
     137           0 :                 krb5_set_error_message(context, ret, "hdb: failed to decode "
     138             :                                        "present hdb extension");
     139           0 :                 return ret;
     140             :             }
     141             : 
     142           0 :             if (MAKE_TAG(replace_class,replace_type,replace_type) ==
     143           0 :                 MAKE_TAG(list_class,list_type,list_type)) {
     144           0 :                 ext2 = ext3;
     145           0 :                 break;
     146             :             }
     147             :         }
     148             :     }
     149             : 
     150        1627 :     if (ext2) {
     151           0 :         free_HDB_extension(ext2);
     152           0 :         ret = copy_HDB_extension(ext, ext2);
     153           0 :         if (ret)
     154           0 :             krb5_set_error_message(context, ret, "hdb: failed to copy replacement "
     155             :                                    "hdb extension");
     156           0 :         return ret;
     157             :     }
     158             : 
     159        1645 :     return add_HDB_extensions(entry->extensions, ext);
     160             : }
     161             : 
     162             : krb5_error_code
     163           0 : hdb_clear_extension(krb5_context context,
     164             :                     hdb_entry *entry,
     165             :                     int type)
     166             : {
     167           0 :     size_t i;
     168             : 
     169           0 :     if (entry->extensions == NULL)
     170           0 :         return 0;
     171             : 
     172           0 :     for (i = 0; i < entry->extensions->len; ) {
     173           0 :         if (entry->extensions->val[i].data.element == (unsigned)type)
     174           0 :             (void) remove_HDB_extensions(entry->extensions, i);
     175             :         else
     176           0 :             i++;
     177             :     }
     178           0 :     if (entry->extensions->len == 0) {
     179           0 :         free(entry->extensions->val);
     180           0 :         free(entry->extensions);
     181           0 :         entry->extensions = NULL;
     182             :     }
     183             : 
     184           0 :     return 0;
     185             : }
     186             : 
     187             : 
     188             : krb5_error_code
     189           2 : hdb_entry_get_pkinit_acl(const hdb_entry *entry, const HDB_Ext_PKINIT_acl **a)
     190             : {
     191           0 :     const HDB_extension *ext;
     192             : 
     193           2 :     ext = hdb_find_extension(entry, choice_HDB_extension_data_pkinit_acl);
     194           2 :     if (ext)
     195           0 :         *a = &ext->data.u.pkinit_acl;
     196             :     else
     197           2 :         *a = NULL;
     198             : 
     199           2 :     return 0;
     200             : }
     201             : 
     202             : krb5_error_code
     203           0 : hdb_entry_get_pkinit_hash(const hdb_entry *entry, const HDB_Ext_PKINIT_hash **a)
     204             : {
     205           0 :     const HDB_extension *ext;
     206             : 
     207           0 :     ext = hdb_find_extension(entry, choice_HDB_extension_data_pkinit_cert_hash);
     208           0 :     if (ext)
     209           0 :         *a = &ext->data.u.pkinit_cert_hash;
     210             :     else
     211           0 :         *a = NULL;
     212             : 
     213           0 :     return 0;
     214             : }
     215             : 
     216             : krb5_error_code
     217         108 : hdb_entry_get_pkinit_cert(const hdb_entry *entry, const HDB_Ext_PKINIT_cert **a)
     218             : {
     219           0 :     const HDB_extension *ext;
     220             : 
     221         108 :     ext = hdb_find_extension(entry, choice_HDB_extension_data_pkinit_cert);
     222         108 :     if (ext)
     223           0 :         *a = &ext->data.u.pkinit_cert;
     224             :     else
     225         108 :         *a = NULL;
     226             : 
     227         108 :     return 0;
     228             : }
     229             : 
     230             : krb5_error_code
     231           0 : hdb_entry_get_krb5_config(const hdb_entry *entry, heim_octet_string *c)
     232             : {
     233           0 :     const HDB_extension *ext;
     234             : 
     235           0 :     c->data = NULL;
     236           0 :     c->length = 0;
     237           0 :     ext = hdb_find_extension(entry, choice_HDB_extension_data_krb5_config);
     238           0 :     if (ext)
     239           0 :         *c = ext->data.u.krb5_config;
     240           0 :     return 0;
     241             : }
     242             : 
     243             : krb5_error_code
     244           0 : hdb_entry_set_krb5_config(krb5_context context,
     245             :                           hdb_entry *entry,
     246             :                           heim_octet_string *s)
     247             : {
     248           0 :     HDB_extension ext;
     249             : 
     250           0 :     ext.mandatory = FALSE;
     251           0 :     ext.data.element = choice_HDB_extension_data_last_pw_change;
     252             :     /* hdb_replace_extension() copies this, so no need to copy it here */
     253           0 :     ext.data.u.krb5_config = *s;
     254           0 :     return hdb_replace_extension(context, entry, &ext);
     255             : }
     256             : 
     257             : krb5_error_code
     258           0 : hdb_entry_get_pw_change_time(const hdb_entry *entry, time_t *t)
     259             : {
     260           0 :     const HDB_extension *ext;
     261             : 
     262           0 :     ext = hdb_find_extension(entry, choice_HDB_extension_data_last_pw_change);
     263           0 :     if (ext)
     264           0 :         *t = ext->data.u.last_pw_change;
     265             :     else
     266           0 :         *t = 0;
     267             : 
     268           0 :     return 0;
     269             : }
     270             : 
     271             : krb5_error_code
     272           0 : hdb_entry_set_pw_change_time(krb5_context context,
     273             :                              hdb_entry *entry,
     274             :                              time_t t)
     275             : {
     276           0 :     HDB_extension ext;
     277             : 
     278           0 :     ext.mandatory = FALSE;
     279           0 :     ext.data.element = choice_HDB_extension_data_last_pw_change;
     280           0 :     if (t == 0)
     281           0 :         t = time(NULL);
     282           0 :     ext.data.u.last_pw_change = t;
     283             : 
     284           0 :     return hdb_replace_extension(context, entry, &ext);
     285             : }
     286             : 
     287             : int
     288           0 : hdb_entry_get_password(krb5_context context, HDB *db,
     289             :                        const hdb_entry *entry, char **p)
     290             : {
     291           0 :     HDB_extension *ext;
     292           0 :     char *str;
     293           0 :     int ret;
     294             : 
     295           0 :     ext = hdb_find_extension(entry, choice_HDB_extension_data_password);
     296           0 :     if (ext) {
     297           0 :         heim_utf8_string xstr;
     298           0 :         heim_octet_string pw;
     299             : 
     300           0 :         if (db->hdb_master_key_set && ext->data.u.password.mkvno) {
     301           0 :             hdb_master_key key;
     302             : 
     303           0 :             key = _hdb_find_master_key(ext->data.u.password.mkvno,
     304             :                                        db->hdb_master_key);
     305             : 
     306           0 :             if (key == NULL) {
     307           0 :                 krb5_set_error_message(context, HDB_ERR_NO_MKEY,
     308             :                                        "master key %d missing",
     309           0 :                                        *ext->data.u.password.mkvno);
     310           0 :                 return HDB_ERR_NO_MKEY;
     311             :             }
     312             : 
     313           0 :             ret = _hdb_mkey_decrypt(context, key, HDB_KU_MKEY,
     314             :                                     ext->data.u.password.password.data,
     315             :                                     ext->data.u.password.password.length,
     316             :                                     &pw);
     317             :         } else {
     318           0 :             ret = der_copy_octet_string(&ext->data.u.password.password, &pw);
     319             :         }
     320           0 :         if (ret) {
     321           0 :             krb5_clear_error_message(context);
     322           0 :             return ret;
     323             :         }
     324             : 
     325           0 :         xstr = pw.data;
     326           0 :         if (xstr[pw.length - 1] != '\0') {
     327           0 :             krb5_set_error_message(context, EINVAL, "malformed password");
     328           0 :             return EINVAL;
     329             :         }
     330             : 
     331           0 :         *p = strdup(xstr);
     332             : 
     333           0 :         der_free_octet_string(&pw);
     334           0 :         if (*p == NULL) {
     335           0 :             krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
     336           0 :             return ENOMEM;
     337             :         }
     338           0 :         return 0;
     339             :     }
     340             : 
     341           0 :     ret = krb5_unparse_name(context, entry->principal, &str);
     342           0 :     if (ret == 0) {
     343           0 :         krb5_set_error_message(context, ENOENT,
     344             :                                "no password attribute for %s", str);
     345           0 :         free(str);
     346             :     } else
     347           0 :         krb5_clear_error_message(context);
     348             : 
     349           0 :     return ENOENT;
     350             : }
     351             : 
     352             : int
     353           0 : hdb_entry_set_password(krb5_context context, HDB *db,
     354             :                        hdb_entry *entry, const char *p)
     355             : {
     356           0 :     HDB_extension ext;
     357           0 :     hdb_master_key key;
     358           0 :     int ret;
     359             : 
     360           0 :     ext.mandatory = FALSE;
     361           0 :     ext.data.element = choice_HDB_extension_data_password;
     362             : 
     363           0 :     if (db->hdb_master_key_set) {
     364             : 
     365           0 :         key = _hdb_find_master_key(NULL, db->hdb_master_key);
     366           0 :         if (key == NULL) {
     367           0 :             krb5_set_error_message(context, HDB_ERR_NO_MKEY,
     368             :                                    "hdb_entry_set_password: "
     369             :                                    "failed to find masterkey");
     370           0 :             return HDB_ERR_NO_MKEY;
     371             :         }
     372             : 
     373           0 :         ret = _hdb_mkey_encrypt(context, key, HDB_KU_MKEY,
     374           0 :                                 p, strlen(p) + 1,
     375             :                                 &ext.data.u.password.password);
     376           0 :         if (ret)
     377           0 :             return ret;
     378             : 
     379           0 :         ext.data.u.password.mkvno =
     380           0 :             malloc(sizeof(*ext.data.u.password.mkvno));
     381           0 :         if (ext.data.u.password.mkvno == NULL) {
     382           0 :             free_HDB_extension(&ext);
     383           0 :             krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
     384           0 :             return ENOMEM;
     385             :         }
     386           0 :         *ext.data.u.password.mkvno = _hdb_mkey_version(key);
     387             : 
     388             :     } else {
     389           0 :         ext.data.u.password.mkvno = NULL;
     390             : 
     391           0 :         ret = krb5_data_copy(&ext.data.u.password.password,
     392           0 :                              p, strlen(p) + 1);
     393           0 :         if (ret) {
     394           0 :             krb5_set_error_message(context, ret, "malloc: out of memory");
     395           0 :             free_HDB_extension(&ext);
     396           0 :             return ret;
     397             :         }
     398             :     }
     399             : 
     400           0 :     ret = hdb_replace_extension(context, entry, &ext);
     401             : 
     402           0 :     free_HDB_extension(&ext);
     403             : 
     404           0 :     return ret;
     405             : }
     406             : 
     407             : int
     408           0 : hdb_entry_clear_password(krb5_context context, hdb_entry *entry)
     409             : {
     410           0 :     return hdb_clear_extension(context, entry,
     411             :                                choice_HDB_extension_data_password);
     412             : }
     413             : 
     414             : krb5_error_code
     415           0 : hdb_entry_get_ConstrainedDelegACL(const hdb_entry *entry,
     416             :                                   const HDB_Ext_Constrained_delegation_acl **a)
     417             : {
     418           0 :     const HDB_extension *ext;
     419             : 
     420           0 :     ext = hdb_find_extension(entry,
     421             :                              choice_HDB_extension_data_allowed_to_delegate_to);
     422           0 :     if (ext)
     423           0 :         *a = &ext->data.u.allowed_to_delegate_to;
     424             :     else
     425           0 :         *a = NULL;
     426             : 
     427           0 :     return 0;
     428             : }
     429             : 
     430             : krb5_error_code
     431           0 : hdb_entry_get_aliases(const hdb_entry *entry, const HDB_Ext_Aliases **a)
     432             : {
     433           0 :     const HDB_extension *ext;
     434             : 
     435           0 :     ext = hdb_find_extension(entry, choice_HDB_extension_data_aliases);
     436           0 :     if (ext)
     437           0 :         *a = &ext->data.u.aliases;
     438             :     else
     439           0 :         *a = NULL;
     440             : 
     441           0 :     return 0;
     442             : }
     443             : 
     444             : unsigned int
     445           0 : hdb_entry_get_kvno_diff_clnt(const hdb_entry *entry)
     446             : {
     447           0 :     const HDB_extension *ext;
     448             : 
     449           0 :     ext = hdb_find_extension(entry,
     450             :                              choice_HDB_extension_data_hist_kvno_diff_clnt);
     451           0 :     if (ext)
     452           0 :         return ext->data.u.hist_kvno_diff_clnt;
     453           0 :     return 1;
     454             : }
     455             : 
     456             : krb5_error_code
     457           0 : hdb_entry_set_kvno_diff_clnt(krb5_context context, hdb_entry *entry,
     458             :                              unsigned int diff)
     459             : {
     460           0 :     HDB_extension ext;
     461             : 
     462           0 :     if (diff > 16384)
     463           0 :         return EINVAL;
     464           0 :     ext.mandatory = FALSE;
     465           0 :     ext.data.element = choice_HDB_extension_data_hist_kvno_diff_clnt;
     466           0 :     ext.data.u.hist_kvno_diff_clnt = diff;
     467           0 :     return hdb_replace_extension(context, entry, &ext);
     468             : }
     469             : 
     470             : krb5_error_code
     471           0 : hdb_entry_clear_kvno_diff_clnt(krb5_context context, hdb_entry *entry)
     472             : {
     473           0 :     return hdb_clear_extension(context, entry,
     474             :                                choice_HDB_extension_data_hist_kvno_diff_clnt);
     475             : }
     476             : 
     477             : unsigned int
     478           0 : hdb_entry_get_kvno_diff_svc(const hdb_entry *entry)
     479             : {
     480           0 :     const HDB_extension *ext;
     481             : 
     482           0 :     ext = hdb_find_extension(entry,
     483             :                              choice_HDB_extension_data_hist_kvno_diff_svc);
     484           0 :     if (ext)
     485           0 :         return ext->data.u.hist_kvno_diff_svc;
     486           0 :     return 1024; /* max_life effectively provides a better default */
     487             : }
     488             : 
     489             : krb5_error_code
     490           0 : hdb_entry_set_kvno_diff_svc(krb5_context context, hdb_entry *entry,
     491             :                             unsigned int diff)
     492             : {
     493           0 :     HDB_extension ext;
     494             : 
     495           0 :     if (diff > 16384)
     496           0 :         return EINVAL;
     497           0 :     ext.mandatory = FALSE;
     498           0 :     ext.data.element = choice_HDB_extension_data_hist_kvno_diff_svc;
     499           0 :     ext.data.u.hist_kvno_diff_svc = diff;
     500           0 :     return hdb_replace_extension(context, entry, &ext);
     501             : }
     502             : 
     503             : krb5_error_code
     504           0 : hdb_entry_clear_kvno_diff_svc(krb5_context context, hdb_entry *entry)
     505             : {
     506           0 :     return hdb_clear_extension(context, entry,
     507             :                                choice_HDB_extension_data_hist_kvno_diff_svc);
     508             : }
     509             : 
     510             : krb5_error_code
     511           0 : hdb_set_last_modified_by(krb5_context context, hdb_entry *entry,
     512             :                          krb5_principal modby, time_t modtime)
     513             : {
     514           0 :     krb5_error_code ret;
     515           0 :     Event *old_ev;
     516           0 :     Event *ev;
     517             : 
     518           0 :     old_ev = entry->modified_by;
     519             : 
     520           0 :     ev = calloc(1, sizeof (*ev));
     521           0 :     if (!ev)
     522           0 :         return ENOMEM;
     523           0 :     if (modby)
     524           0 :         ret = krb5_copy_principal(context, modby, &ev->principal);
     525             :     else
     526           0 :         ret = krb5_parse_name(context, "root/admin", &ev->principal);
     527           0 :     if (ret) {
     528           0 :         free(ev);
     529           0 :         return ret;
     530             :     }
     531           0 :     ev->time = modtime;
     532           0 :     if (!ev->time)
     533           0 :         time(&ev->time);
     534             : 
     535           0 :     entry->modified_by = ev;
     536           0 :     if (old_ev)
     537           0 :         free_Event(old_ev);
     538           0 :     return 0;
     539             : }
     540             : 
     541             : krb5_error_code
     542           0 : hdb_entry_get_key_rotation(krb5_context context,
     543             :                            const hdb_entry *entry,
     544             :                            const HDB_Ext_KeyRotation **kr)
     545             : {
     546           0 :     HDB_extension *ext =
     547           0 :         hdb_find_extension(entry, choice_HDB_extension_data_key_rotation);
     548             : 
     549           0 :     *kr = ext ? &ext->data.u.key_rotation : NULL;
     550           0 :     return 0;
     551             : }
     552             : 
     553             : krb5_error_code
     554           0 : hdb_validate_key_rotation(krb5_context context,
     555             :                           const KeyRotation *past_kr,
     556             :                           const KeyRotation *new_kr)
     557             : {
     558           0 :     unsigned int last_kvno;
     559             : 
     560           0 :     if (new_kr->period < 1) {
     561           0 :         krb5_set_error_message(context, EINVAL,
     562             :                                "Key rotation periods must be non-zero "
     563             :                                "and positive");
     564           0 :         return EINVAL;
     565             :     }
     566           0 :     if (new_kr->base_key_kvno < 1 || new_kr->base_kvno < 1) {
     567           0 :         krb5_set_error_message(context, EINVAL,
     568             :                                "Key version number zero not allowed "
     569             :                                "for key rotation");
     570           0 :         return EINVAL;
     571             :     }
     572           0 :     if (!past_kr)
     573           0 :         return 0;
     574             : 
     575           0 :     if (past_kr->base_key_kvno == new_kr->base_key_kvno) {
     576             :         /*
     577             :          * The new base keys can be the same as the old, but must have
     578             :          * different kvnos.  (Well, not must must.  It's a convention for now.)
     579             :          */
     580           0 :         krb5_set_error_message(context, EINVAL,
     581             :                                "Base key version numbers for KRs must differ");
     582           0 :         return EINVAL;
     583             :     }
     584           0 :     if (new_kr->epoch - past_kr->epoch <= 0) {
     585           0 :         krb5_set_error_message(context, EINVAL,
     586             :                                "New key rotation periods must start later "
     587             :                                "than existing ones");
     588           0 :         return EINVAL;
     589             :     }
     590             : 
     591           0 :     last_kvno = 1 + ((new_kr->epoch - past_kr->epoch) / past_kr->period);
     592           0 :     if (new_kr->base_kvno <= last_kvno) {
     593           0 :         krb5_set_error_message(context, EINVAL,
     594             :                                "New key rotation base kvno must be larger "
     595             :                                "than the last kvno for the current key "
     596             :                                "rotation (%u)", last_kvno);
     597           0 :         return EINVAL;
     598             :     }
     599           0 :     return 0;
     600             : }
     601             : 
     602             : static int
     603           0 : kr_eq(const KeyRotation *a, const KeyRotation *b)
     604             : {
     605           0 :     return !!(
     606           0 :         a->epoch == b->epoch &&
     607           0 :         a->period == b->period &&
     608           0 :         a->base_kvno == b->base_kvno &&
     609           0 :         a->base_key_kvno == b->base_key_kvno &&
     610           0 :         KeyRotationFlags2int(a->flags) == KeyRotationFlags2int(b->flags)
     611             :     );
     612             : }
     613             : 
     614             : krb5_error_code
     615           0 : hdb_validate_key_rotations(krb5_context context,
     616             :                            const HDB_Ext_KeyRotation *existing,
     617             :                            const HDB_Ext_KeyRotation *krs)
     618             : {
     619           0 :     krb5_error_code ret = 0;
     620           0 :     size_t added = 0;
     621           0 :     size_t i;
     622             : 
     623           0 :     if ((!existing || !existing->len) && (!krs || !krs->len))
     624           0 :         return 0; /* Nothing to do; weird */
     625             : 
     626             :     /*
     627             :      * HDB_Ext_KeyRotation has to have 1..3 elements, and this is enforced by
     628             :      * the ASN.1 compiler and the code it generates.  Nonetheless we'll check
     629             :      * that there's not zero elements.
     630             :      */
     631           0 :     if ((!krs || !krs->len)) {
     632             :         /*
     633             :          * NOTE: We can clear this on concrete principals with virtual keys
     634             :          *       though.  The caller can check for that case.
     635             :          */
     636           0 :         krb5_set_error_message(context, EINVAL,
     637             :                                "Cannot clear key rotation metadata on "
     638             :                                "virtual principal namespaces");
     639           0 :         ret = EINVAL;
     640             :     }
     641             : 
     642             :     /* Validate the new KRs by themselves */
     643           0 :     for (i = 0; ret == 0 && i < krs->len; i++) {
     644           0 :         ret = hdb_validate_key_rotation(context,
     645           0 :                                         i+1 < krs->len ? &krs->val[i+1] : 0,
     646           0 :                                         &krs->val[i]);
     647             :     }
     648           0 :     if (ret || !existing || !existing->len)
     649           0 :         return ret;
     650             : 
     651           0 :     if (existing->len == krs->len) {
     652             :         /* Check for no change */
     653           0 :         for (i = 0; i < krs->len; i++)
     654           0 :             if (!kr_eq(&existing->val[i], &krs->val[i]))
     655           0 :                 break;
     656           0 :         if (i == krs->len)
     657           0 :             return 0; /* No change */
     658             :     }
     659             : 
     660             :     /*
     661             :      * Check that new KRs make sense in the context of the previous KRs.
     662             :      *
     663             :      * Permitted changes:
     664             :      *
     665             :      *  - add one new KR in front
     666             :      *  - drop old KRs
     667             :      *
     668             :      * Start by checking if we're adding a KR, then go on to check for dropped
     669             :      * KRs and/or last KR alteration.
     670             :      */
     671           0 :     if (existing->val[0].epoch == krs->val[0].epoch ||
     672           0 :         existing->val[0].base_kvno == krs->val[0].base_kvno) {
     673           0 :         if (!kr_eq(&existing->val[0], &krs->val[0])) {
     674           0 :             krb5_set_error_message(context, EINVAL,
     675             :                                    "Key rotation change not sensible");
     676           0 :             ret = EINVAL;
     677             :         }
     678             :         /* Key rotation *not* added */
     679             :     } else {
     680             :         /* Key rotation added; check it first */
     681           0 :         ret = hdb_validate_key_rotation(context,
     682           0 :                                         &existing->val[0],
     683           0 :                                         &krs->val[0]);
     684           0 :         added = 1;
     685             :     }
     686           0 :     for (i = 0; ret == 0 && i < existing->len && i + added < krs->len; i++)
     687           0 :         if (!kr_eq(&existing->val[i], &krs->val[i + added]))
     688           0 :             krb5_set_error_message(context, ret = EINVAL,
     689             :                                    "Only last key rotation may be truncated");
     690           0 :     return ret;
     691             : }
     692             : 
     693             : /* XXX We need a function to "revoke" the past */
     694             : 
     695             : /**
     696             :  * This function adds a KeyRotation value to an entry, validating the
     697             :  * change.  One of `entry' and `krs' must be NULL, and the other non-NULL, and
     698             :  * whichever is given will be altered.
     699             :  *
     700             :  * @param context Context
     701             :  * @param entry An HDB entry
     702             :  * @param krs A key rotation extension for hdb_entry
     703             :  * @param kr A new KeyRotation value
     704             :  *
     705             :  * @return Zero on success, an error otherwise.
     706             :  */
     707             : krb5_error_code
     708           0 : hdb_entry_add_key_rotation(krb5_context context,
     709             :                            hdb_entry *entry,
     710             :                            HDB_Ext_KeyRotation *krs,
     711             :                            const KeyRotation *kr)
     712             : {
     713           0 :     krb5_error_code ret;
     714           0 :     HDB_extension new_ext;
     715           0 :     HDB_extension *ext = &new_ext;
     716           0 :     KeyRotation tmp;
     717           0 :     size_t i, sz;
     718             : 
     719           0 :     if (kr->period < 1) {
     720           0 :         krb5_set_error_message(context, EINVAL,
     721             :                                "Key rotation period cannot be zero");
     722           0 :         return EINVAL;
     723             :     }
     724             : 
     725           0 :     new_ext.mandatory = TRUE;
     726           0 :     new_ext.data.element = choice_HDB_extension_data_key_rotation;
     727           0 :     new_ext.data.u.key_rotation.len = 0;
     728           0 :     new_ext.data.u.key_rotation.val = 0;
     729             : 
     730           0 :     if (entry && krs)
     731           0 :         return EINVAL;
     732             : 
     733           0 :     if (entry) {
     734           0 :         ext = hdb_find_extension(entry, choice_HDB_extension_data_key_rotation);
     735           0 :         if (!ext)
     736           0 :             ext = &new_ext;
     737             :     } else {
     738           0 :         const KeyRotation *prev_kr = &krs->val[0];
     739           0 :         unsigned int last_kvno = 0;
     740             : 
     741           0 :         if (kr->epoch - prev_kr->epoch <= 0) {
     742           0 :             krb5_set_error_message(context, EINVAL,
     743             :                                    "New key rotation periods must start later "
     744             :                                    "than existing ones");
     745           0 :             return EINVAL;
     746             :         }
     747             : 
     748           0 :         if (kr->base_kvno <= prev_kr->base_kvno ||
     749           0 :             kr->base_kvno - prev_kr->base_kvno <=
     750           0 :                 (last_kvno = 1 +
     751           0 :                  ((kr->epoch - prev_kr->epoch) / prev_kr->period))) {
     752           0 :             krb5_set_error_message(context, EINVAL,
     753             :                                    "New key rotation base kvno must be larger "
     754             :                                    "than the last kvno for the current key "
     755             :                                    "rotation (%u)", last_kvno);
     756           0 :             return EINVAL;
     757             :         }
     758             :     }
     759             : 
     760             :     /* First, append */
     761           0 :     ret = add_HDB_Ext_KeyRotation(&ext->data.u.key_rotation, kr);
     762           0 :     if (ret)
     763           0 :         return ret;
     764             : 
     765             :     /* Rotate new to front */
     766           0 :     tmp = ext->data.u.key_rotation.val[ext->data.u.key_rotation.len - 1];
     767           0 :     sz = sizeof(ext->data.u.key_rotation.val[0]);
     768           0 :     memmove(&ext->data.u.key_rotation.val[1], &ext->data.u.key_rotation.val[0],
     769           0 :             (ext->data.u.key_rotation.len - 1) * sz);
     770           0 :     ext->data.u.key_rotation.val[0] = tmp;
     771             : 
     772             :     /* Drop too old entries */
     773           0 :     for (i = 3; i < ext->data.u.key_rotation.len; i++)
     774           0 :         free_KeyRotation(&ext->data.u.key_rotation.val[i]);
     775           0 :     ext->data.u.key_rotation.len =
     776           0 :         ext->data.u.key_rotation.len > 3 ? 3 : ext->data.u.key_rotation.len;
     777             : 
     778           0 :     if (ext != &new_ext)
     779           0 :         return 0;
     780             : 
     781             :     /* Install new extension */
     782           0 :     if (ret == 0 && entry)
     783           0 :         ret = hdb_replace_extension(context, entry, ext);
     784           0 :     free_HDB_extension(&new_ext);
     785           0 :     return ret;
     786             : }

Generated by: LCOV version 1.14