LCOV - code coverage report
Current view: top level - monetdb5/mal - mal_authorize.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 82 97 84.5 %
Date: 2024-04-25 20:03:45 Functions: 9 9 100.0 %

          Line data    Source code
       1             : /*
       2             :  * SPDX-License-Identifier: MPL-2.0
       3             :  *
       4             :  * This Source Code Form is subject to the terms of the Mozilla Public
       5             :  * License, v. 2.0.  If a copy of the MPL was not distributed with this
       6             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
       7             :  *
       8             :  * Copyright 2024 MonetDB Foundation;
       9             :  * Copyright August 2008 - 2023 MonetDB B.V.;
      10             :  * Copyright 1997 - July 2008 CWI.
      11             :  */
      12             : 
      13             : /*
      14             :  * (authors) M. Kersten, F. Groffen
      15             :  * Authorisation adminstration management
      16             :  * Authorisation of users is a key concept in protecting the server from
      17             :  * malicious and unauthorised users.  This file contains a number of
      18             :  * functions that administrate a set of BATs backing the authorisation
      19             :  * tables.
      20             :  *
      21             :  * The implementation is based on three persistent BATs, which keep the
      22             :  * usernames, passwords and allowed scenarios for users of the server.
      23             :  *
      24             :  */
      25             : #include "monetdb_config.h"
      26             : #include "mal_authorize.h"
      27             : #include "mal_exception.h"
      28             : #include "mal_private.h"
      29             : #include "mcrypt.h"
      30             : #include "msabaoth.h"
      31             : #include "mal_scenario.h"
      32             : #include "mal_interpreter.h"
      33             : 
      34             : #ifdef HAVE_UNISTD_H
      35             : #include <unistd.h>
      36             : #endif
      37             : 
      38             : /* yep, the vault key is just stored in memory */
      39             : static str vaultKey = NULL;
      40             : /* lock to protect the above */
      41             : static MT_RWLock rt_lock = MT_RWLOCK_INITIALIZER(rt_lock);
      42             : 
      43             : static str AUTHdecypherValueLocked(str *ret, const char *value);
      44             : 
      45             : void
      46         334 : AUTHreset(void)
      47             : {
      48         334 :         MT_rwlock_wrlock(&rt_lock);
      49         334 :         GDKfree(vaultKey);
      50         334 :         vaultKey = NULL;
      51         334 :         MT_rwlock_wrunlock(&rt_lock);
      52         334 : }
      53             : 
      54             : /**
      55             :  * Requires the current client to be the admin user thread. If not the case,
      56             :  * this function returns an InvalidCredentialsException.
      57             :  */
      58             : str
      59          44 : AUTHrequireAdmin(Client cntxt)
      60             : {
      61          44 :         assert(cntxt);
      62             : 
      63          44 :         if (cntxt->user != MAL_ADMIN)
      64           0 :                 throw(MAL, "AUTHrequireAdmin", INVCRED_ACCESS_DENIED);
      65             :         return (MAL_SUCCEED);
      66             : }
      67             : 
      68             : /*=== the vault ===*/
      69             : 
      70             : /**
      71             :  * Unlocks the vault with the given password.  Since the password is
      72             :  * just the decypher key, it is not possible to directly check whether
      73             :  * the given password is correct.  If incorrect, however, all decypher
      74             :  * operations will probably fail or return an incorrect decyphered
      75             :  * value.
      76             :  */
      77             : str
      78         336 : AUTHunlockVault(const char *password)
      79             : {
      80         336 :         if (strNil(password))
      81           0 :                 throw(ILLARG, "unlockVault", "password should not be nil");
      82             : 
      83             :         /* even though I think this function should be called only once, it
      84             :          * is not of real extra efforts to avoid a mem-leak if it is used
      85             :          * multiple times */
      86         336 :         MT_rwlock_wrlock(&rt_lock);
      87         336 :         GDKfree(vaultKey);
      88             : 
      89         336 :         vaultKey = GDKstrdup(password);
      90         336 :         if (vaultKey == NULL) {
      91           0 :                 MT_rwlock_wrunlock(&rt_lock);
      92           0 :                 throw(MAL, "unlockVault", SQLSTATE(HY013) MAL_MALLOC_FAIL " vault key");
      93             :         }
      94         336 :         MT_rwlock_wrunlock(&rt_lock);
      95         336 :         return (MAL_SUCCEED);
      96             : }
      97             : 
      98             : /**
      99             :  * Decyphers a given value, using the vaultKey.  The returned value
     100             :  * might be incorrect if the vaultKey is incorrect or unset.  If the
     101             :  * cypher algorithm fails or detects an invalid password, it might throw
     102             :  * an exception.  The ret string is GDKmalloced, and should be GDKfreed
     103             :  * by the caller.
     104             :  */
     105             : static str
     106       38199 : AUTHdecypherValueLocked(str *ret, const char *value)
     107             : {
     108             :         /* Cyphering and decyphering can be done using many algorithms.
     109             :          * Future requirements might want a stronger cypher than the XOR
     110             :          * cypher chosen here.  It is left up to the implementor how to do
     111             :          * that once those algoritms become available.  It could be
     112             :          * #ifdef-ed or on if-basis depending on whether the cypher
     113             :          * algorithm is a compile, or runtime option.  When necessary, this
     114             :          * function could be extended with an extra argument that indicates
     115             :          * the cypher algorithm.
     116             :          */
     117             : 
     118             :         /* this is the XOR decypher implementation */
     119       38199 :         str r, w;
     120       38199 :         const char *s = value;
     121       38199 :         char t = '\0';
     122       38199 :         bool escaped = false;
     123             :         /* we default to some garbage key, just to make password unreadable
     124             :          * (a space would only uppercase the password) */
     125       38199 :         size_t keylen = 0;
     126             : 
     127       38199 :         if (vaultKey == NULL)
     128           0 :                 throw(MAL, "decypherValue", "The vault is still locked!");
     129       38199 :         w = r = GDKmalloc(sizeof(char) * (strlen(value) + 1));
     130       38199 :         if (r == NULL)
     131           0 :                 throw(MAL, "decypherValue", SQLSTATE(HY013) MAL_MALLOC_FAIL);
     132             : 
     133       38199 :         keylen = strlen(vaultKey);
     134             : 
     135             :         /* XOR all characters.  If we encounter a 'one' char after the XOR
     136             :          * operation, it is an escape, so replace it with the next char. */
     137     5006648 :         for (; (t = *s) != '\0'; s++) {
     138     4968449 :                 if ((t & 0xE0) == 0xC0) {
     139           0 :                         assert((t & 0x1E) == 0x02);
     140           0 :                         assert((s[1] & 0xC0) == 0x80);
     141           0 :                         t = ((t & 0x1F) << 6) | (*++s & 0x3F);
     142             :                 }
     143     4968449 :                 if (t == '\1' && !escaped) {
     144       87553 :                         escaped = true;
     145       87553 :                         continue;
     146     4880896 :                 } else if (escaped) {
     147       87553 :                         t -= 1;
     148       87553 :                         escaped = false;
     149             :                 }
     150     4880896 :                 *w = t ^ vaultKey[(w - r) % keylen];
     151     4880896 :                 w++;
     152             :         }
     153       38199 :         *w = '\0';
     154             : 
     155       38199 :         *ret = r;
     156       38199 :         return (MAL_SUCCEED);
     157             : }
     158             : 
     159             : str
     160       38199 : AUTHdecypherValue(str *ret, const char *value)
     161             : {
     162       38199 :         MT_rwlock_rdlock(&rt_lock);
     163       38199 :         str err = AUTHdecypherValueLocked(ret, value);
     164       38199 :         MT_rwlock_rdunlock(&rt_lock);
     165       38199 :         return err;
     166             : }
     167             : 
     168             : /**
     169             :  * Cyphers the given string using the vaultKey.  If the cypher algorithm
     170             :  * fails or detects an invalid password, it might throw an exception.
     171             :  * The ret string is GDKmalloced, and should be GDKfreed by the caller.
     172             :  */
     173             : static str
     174         647 : AUTHcypherValueLocked(str *ret, const char *value)
     175             : {
     176             :         /* this is the XOR cypher implementation */
     177         647 :         str r, w;
     178         647 :         const char *s = value;
     179             :         /* we default to some garbage key, just to make password unreadable
     180             :          * (a space would only uppercase the password) */
     181         647 :         size_t keylen = 0;
     182             : 
     183         647 :         if (vaultKey == NULL)
     184           0 :                 throw(MAL, "cypherValue", "The vault is still locked!");
     185         647 :         w = r = GDKmalloc(sizeof(char) * (strlen(value) * 2 + 1));
     186         647 :         if (r == NULL)
     187           0 :                 throw(MAL, "cypherValue", SQLSTATE(HY013) MAL_MALLOC_FAIL);
     188             : 
     189         647 :         keylen = strlen(vaultKey);
     190             : 
     191             :         /* XOR all characters.  If we encounter a 'zero' char after the XOR
     192             :          * operation, escape it with a 'one' char. */
     193       83463 :         for (; *s != '\0'; s++) {
     194       82816 :                 char c = *s ^ vaultKey[(s - value) % keylen];
     195       82816 :                 if (c == '\0') {
     196        1833 :                         *w++ = '\1';
     197        1833 :                         *w = '\1';
     198       80983 :                 } else if (c == '\1') {
     199         785 :                         *w++ = '\1';
     200         785 :                         *w = '\2';
     201       80198 :                 } else if (c & 0x80) {
     202           0 :                         *w++ = 0xC0 | ((c >> 6) & 0x03);
     203           0 :                         *w = 0x80 | (c & 0x3F);
     204             :                 } else {
     205       80198 :                         *w = c;
     206             :                 }
     207       82816 :                 w++;
     208             :         }
     209         647 :         *w = '\0';
     210             : 
     211         647 :         *ret = r;
     212         647 :         return (MAL_SUCCEED);
     213             : }
     214             : 
     215             : str
     216         647 : AUTHcypherValue(str *ret, const char *value)
     217             : {
     218         647 :         MT_rwlock_rdlock(&rt_lock);
     219         647 :         str err = AUTHcypherValueLocked(ret, value);
     220         647 :         MT_rwlock_rdunlock(&rt_lock);
     221         647 :         return err;
     222             : }
     223             : 
     224             : /**
     225             :  * Checks if the given string is a (hex represented) hash for the
     226             :  * current backend.  This check allows to at least forbid storing
     227             :  * trivial plain text passwords by a simple check.
     228             :  */
     229             : #define concat(x,y)     x##y
     230             : #define digestlength(h) concat(h, _DIGEST_LENGTH)
     231             : str
     232           3 : AUTHverifyPassword(const char *passwd)
     233             : {
     234           3 :         const char *p = passwd;
     235           3 :         size_t len = strlen(p);
     236             : 
     237           3 :         if (len != digestlength(MONETDB5_PASSWDHASH_TOKEN) * 2) {
     238           0 :                 throw(MAL, "verifyPassword",
     239             :                           "password is not %d chars long, is it a hex "
     240             :                           "representation of a %s password hash?",
     241             :                           digestlength(MONETDB5_PASSWDHASH_TOKEN), MONETDB5_PASSWDHASH);
     242             :         }
     243         387 :         len++;                                          // required in case all the checks above are false
     244         387 :         while (*p != '\0') {
     245         384 :                 if (!((*p >= 'a' && *p <= 'z') || isdigit((unsigned char) *p)))
     246           0 :                         throw(MAL, "verifyPassword",
     247             :                                   "password does contain invalid characters, is it a"
     248             :                                   "lowercase hex representation of a hash?");
     249         384 :                 p++;
     250             :         }
     251             : 
     252             :         return (MAL_SUCCEED);
     253             : }
     254             : 
     255             : str
     256         550 : AUTHGeneratePasswordHash(str *res, const char *value)
     257             : {
     258         550 :         return AUTHcypherValue(res, value);
     259             : }

Generated by: LCOV version 1.14