(root)/
Linux-PAM-1.5.3/
modules/
pam_timestamp/
hmac_openssl_wrapper.c
       1  /* Wrapper for hmac openssl implementation.
       2   *
       3   * Copyright (c) 2021 Red Hat, Inc.
       4   * Written by Iker Pedrosa <ipedrosa@redhat.com>
       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   * 1. Redistributions of source code must retain the above copyright
      10   *    notice, and the entire permission notice in its entirety,
      11   *    including the disclaimer of warranties.
      12   * 2. Redistributions in binary form must reproduce the above copyright
      13   *    notice, this list of conditions and the following disclaimer in the
      14   *    documentation and/or other materials provided with the distribution.
      15   * 3. The name of the author may not be used to endorse or promote
      16   *    products derived from this software without specific prior
      17   *    written permission.
      18   *
      19   * ALTERNATIVELY, this product may be distributed under the terms of
      20   * the GNU Public License, in which case the provisions of the GPL are
      21   * required INSTEAD OF the above restrictions.  (This clause is
      22   * necessary due to a potential bad interaction between the GPL and
      23   * the restrictions contained in a BSD-style copyright.)
      24   *
      25   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
      26   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
      27   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
      28   * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
      29   * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
      30   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
      31   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
      32   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
      33   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
      34   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
      35   * OF THE POSSIBILITY OF SUCH DAMAGE.
      36   *
      37   */
      38  
      39  #include "config.h"
      40  
      41  #ifdef WITH_OPENSSL
      42  
      43  #include <sys/stat.h>
      44  #include <fcntl.h>
      45  #include <syslog.h>
      46  #include <unistd.h>
      47  #include <string.h>
      48  #include <errno.h>
      49  #include <openssl/evp.h>
      50  #include <openssl/params.h>
      51  #include <openssl/core_names.h>
      52  
      53  #include <security/pam_ext.h>
      54  #include <security/pam_modutil.h>
      55  
      56  #include "hmac_openssl_wrapper.h"
      57  #include "pam_inline.h"
      58  
      59  #define LOGIN_DEFS          "/etc/login.defs"
      60  #define CRYPTO_KEY          "HMAC_CRYPTO_ALGO"
      61  #define DEFAULT_ALGORITHM   "SHA512"
      62  #define MAX_HMAC_LENGTH     512
      63  #define MAX_KEY_LENGTH      EVP_MAX_KEY_LENGTH
      64  
      65  static char *
      66  get_crypto_algorithm(pam_handle_t *pamh, int debug){
      67      char *config_value = NULL;
      68  
      69      config_value = pam_modutil_search_key(pamh, LOGIN_DEFS, CRYPTO_KEY);
      70  
      71      if (config_value == NULL) {
      72          config_value = strdup(DEFAULT_ALGORITHM);
      73          if (debug) {
      74              pam_syslog(pamh, LOG_DEBUG,
      75                     "Key [%s] not found, falling back to default algorithm [%s]\n",
      76                     CRYPTO_KEY, DEFAULT_ALGORITHM);
      77          }
      78      }
      79  
      80      return config_value;
      81  }
      82  
      83  static int
      84  generate_key(pam_handle_t *pamh, char **key, size_t key_size)
      85  {
      86      int fd = 0;
      87      size_t bytes_read = 0;
      88      char * tmp = NULL;
      89  
      90      fd = open("/dev/urandom", O_RDONLY);
      91      if (fd == -1) {
      92          pam_syslog(pamh, LOG_ERR, "Cannot open /dev/urandom: %m");
      93          return PAM_AUTH_ERR;
      94      }
      95  
      96      tmp = malloc(key_size);
      97      if (!tmp) {
      98          pam_syslog(pamh, LOG_CRIT, "Not enough memory");
      99          close(fd);
     100          return PAM_AUTH_ERR;
     101      }
     102  
     103      bytes_read = pam_modutil_read(fd, tmp, key_size);
     104      close(fd);
     105  
     106      if (bytes_read < key_size) {
     107          pam_syslog(pamh, LOG_ERR, "Short read on random device");
     108          free(tmp);
     109          return PAM_AUTH_ERR;
     110      }
     111  
     112      *key = tmp;
     113  
     114      return PAM_SUCCESS;
     115  }
     116  
     117  static int
     118  read_file(pam_handle_t *pamh, int fd, char **text, size_t *text_length)
     119  {
     120      struct stat st;
     121      size_t bytes_read = 0;
     122      char *tmp = NULL;
     123  
     124      if (fstat(fd, &st) == -1) {
     125          pam_syslog(pamh, LOG_ERR, "Unable to stat file: %m");
     126          close(fd);
     127          return PAM_AUTH_ERR;
     128      }
     129  
     130      if (st.st_size == 0) {
     131          pam_syslog(pamh, LOG_ERR, "Key file size cannot be 0");
     132          close(fd);
     133          return PAM_AUTH_ERR;
     134      }
     135  
     136      tmp = malloc(st.st_size);
     137      if (!tmp) {
     138          pam_syslog(pamh, LOG_CRIT, "Not enough memory");
     139          close(fd);
     140          return PAM_AUTH_ERR;
     141      }
     142  
     143      bytes_read = pam_modutil_read(fd, tmp, st.st_size);
     144      close(fd);
     145  
     146      if (bytes_read < (size_t)st.st_size) {
     147          pam_syslog(pamh, LOG_ERR, "Short read on key file");
     148          pam_overwrite_n(tmp, st.st_size);
     149          free(tmp);
     150          return PAM_AUTH_ERR;
     151      }
     152  
     153      *text = tmp;
     154      *text_length = st.st_size;
     155  
     156      return PAM_SUCCESS;
     157  }
     158  
     159  static int
     160  write_file(pam_handle_t *pamh, const char *file_name, char *text,
     161             size_t text_length, uid_t owner, gid_t group)
     162  {
     163      int fd = 0;
     164      size_t bytes_written = 0;
     165  
     166      fd = open(file_name,
     167                O_WRONLY | O_CREAT | O_TRUNC,
     168                S_IRUSR | S_IWUSR);
     169      if (fd == -1) {
     170          pam_syslog(pamh, LOG_ERR, "Unable to open [%s]: %m", file_name);
     171          pam_overwrite_n(text, text_length);
     172          free(text);
     173          return PAM_AUTH_ERR;
     174      }
     175  
     176      if (fchown(fd, owner, group) == -1) {
     177          pam_syslog(pamh, LOG_ERR, "Unable to change ownership [%s]: %m", file_name);
     178          pam_overwrite_n(text, text_length);
     179          free(text);
     180          close(fd);
     181          return PAM_AUTH_ERR;
     182      }
     183  
     184      bytes_written = pam_modutil_write(fd, text, text_length);
     185      close(fd);
     186  
     187      if (bytes_written < text_length) {
     188          pam_syslog(pamh, LOG_ERR, "Short write on %s", file_name);
     189          free(text);
     190          return PAM_AUTH_ERR;
     191      }
     192  
     193      return PAM_SUCCESS;
     194  }
     195  
     196  static int
     197  key_management(pam_handle_t *pamh, const char *file_name, char **text,
     198                  size_t text_length, uid_t owner, gid_t group)
     199  {
     200      int fd = 0;
     201  
     202      fd = open(file_name, O_RDONLY | O_NOFOLLOW);
     203      if (fd == -1) {
     204          if (errno == ENOENT) {
     205              if (generate_key(pamh, text, text_length)) {
     206                  pam_syslog(pamh, LOG_ERR, "Unable to generate key");
     207                  return PAM_AUTH_ERR;
     208              }
     209  
     210              if (write_file(pamh, file_name, *text, text_length, owner, group)) {
     211                  pam_syslog(pamh, LOG_ERR, "Unable to write key");
     212                  return PAM_AUTH_ERR;
     213              }
     214          } else {
     215              pam_syslog(pamh, LOG_ERR, "Unable to open %s: %m", file_name);
     216              return PAM_AUTH_ERR;
     217          }
     218      } else {
     219          if (read_file(pamh, fd, text, &text_length)) {
     220              pam_syslog(pamh, LOG_ERR, "Error reading key file %s\n", file_name);
     221              return PAM_AUTH_ERR;
     222          }
     223      }
     224  
     225      return PAM_SUCCESS;
     226  }
     227  
     228  static int
     229  hmac_management(pam_handle_t *pamh, int debug, void **out, size_t *out_length,
     230                  char *key, size_t key_length,
     231                  const void *text, size_t text_length)
     232  {
     233      int ret = PAM_AUTH_ERR;
     234      EVP_MAC *evp_mac = NULL;
     235      EVP_MAC_CTX *ctx = NULL;
     236      unsigned char *hmac_message = NULL;
     237      size_t hmac_length;
     238      char *algo = NULL;
     239      OSSL_PARAM subalg_param[] = { OSSL_PARAM_END, OSSL_PARAM_END };
     240  
     241      algo = get_crypto_algorithm(pamh, debug);
     242  
     243      subalg_param[0] = OSSL_PARAM_construct_utf8_string(OSSL_MAC_PARAM_DIGEST,
     244                                                         algo,
     245                                                         0);
     246  
     247      evp_mac = EVP_MAC_fetch(NULL, "HMAC", NULL);
     248      if (evp_mac == NULL) {
     249          pam_syslog(pamh, LOG_ERR, "Unable to create hmac implementation");
     250          goto done;
     251      }
     252  
     253      ctx = EVP_MAC_CTX_new(evp_mac);
     254      if (ctx == NULL) {
     255          pam_syslog(pamh, LOG_ERR, "Unable to create hmac context");
     256          goto done;
     257      }
     258  
     259      ret = EVP_MAC_init(ctx, (const unsigned char *)key, key_length, subalg_param);
     260      if (ret == 0) {
     261          pam_syslog(pamh, LOG_ERR, "Unable to initialize hmac context");
     262          goto done;
     263      }
     264  
     265      ret = EVP_MAC_update(ctx, (const unsigned char *)text, text_length);
     266      if (ret == 0) {
     267          pam_syslog(pamh, LOG_ERR, "Unable to update hmac context");
     268          goto done;
     269      }
     270  
     271      hmac_message = (unsigned char*)malloc(sizeof(unsigned char) * MAX_HMAC_LENGTH);
     272      if (!hmac_message) {
     273          pam_syslog(pamh, LOG_CRIT, "Not enough memory");
     274          goto done;
     275      }
     276  
     277      ret = EVP_MAC_final(ctx, hmac_message, &hmac_length, MAX_HMAC_LENGTH);
     278      if (ret == 0) {
     279          pam_syslog(pamh, LOG_ERR, "Unable to calculate hmac message");
     280          goto done;
     281      }
     282  
     283      *out_length = hmac_length;
     284      *out = malloc(*out_length);
     285      if (*out == NULL) {
     286          pam_syslog(pamh, LOG_CRIT, "Not enough memory");
     287          goto done;
     288      }
     289  
     290      memcpy(*out, hmac_message, *out_length);
     291      ret = PAM_SUCCESS;
     292  
     293  done:
     294      if (hmac_message != NULL) {
     295          free(hmac_message);
     296      }
     297      if (key != NULL) {
     298          pam_overwrite_n(key, key_length);
     299          free(key);
     300      }
     301      if (ctx != NULL) {
     302          EVP_MAC_CTX_free(ctx);
     303      }
     304      if (evp_mac != NULL) {
     305          EVP_MAC_free(evp_mac);
     306      }
     307      free(algo);
     308  
     309      return ret;
     310  }
     311  
     312  int
     313  hmac_size(pam_handle_t *pamh, int debug, size_t *hmac_length)
     314  {
     315      int ret = PAM_AUTH_ERR;
     316      EVP_MAC *evp_mac = NULL;
     317      EVP_MAC_CTX *ctx = NULL;
     318      const unsigned char key[] = "ThisIsJustAKey";
     319      size_t key_length = MAX_KEY_LENGTH;
     320      char *algo = NULL;
     321      OSSL_PARAM subalg_param[] = { OSSL_PARAM_END, OSSL_PARAM_END };
     322  
     323      algo = get_crypto_algorithm(pamh, debug);
     324  
     325      subalg_param[0] = OSSL_PARAM_construct_utf8_string(OSSL_MAC_PARAM_DIGEST,
     326                                                         algo,
     327                                                         0);
     328  
     329      evp_mac = EVP_MAC_fetch(NULL, "HMAC", NULL);
     330      if (evp_mac == NULL) {
     331          pam_syslog(pamh, LOG_ERR, "Unable to create hmac implementation");
     332          goto done;
     333      }
     334  
     335      ctx = EVP_MAC_CTX_new(evp_mac);
     336      if (ctx == NULL) {
     337          pam_syslog(pamh, LOG_ERR, "Unable to create hmac context");
     338          goto done;
     339      }
     340  
     341      ret = EVP_MAC_init(ctx, key, key_length, subalg_param);
     342      if (ret == 0) {
     343          pam_syslog(pamh, LOG_ERR, "Unable to initialize hmac context");
     344          goto done;
     345      }
     346  
     347      *hmac_length = EVP_MAC_CTX_get_mac_size(ctx);
     348      ret = PAM_SUCCESS;
     349  
     350  done:
     351      if (ctx != NULL) {
     352          EVP_MAC_CTX_free(ctx);
     353      }
     354      if (evp_mac != NULL) {
     355          EVP_MAC_free(evp_mac);
     356      }
     357      free(algo);
     358  
     359      return ret;
     360  }
     361  
     362  int
     363  hmac_generate(pam_handle_t *pamh, int debug, void **mac, size_t *mac_length,
     364                const char *key_file, uid_t owner, gid_t group,
     365                const void *text, size_t text_length)
     366  {
     367      char *key = NULL;
     368      size_t key_length = MAX_KEY_LENGTH;
     369  
     370      if (key_management(pamh, key_file, &key, key_length, owner, group)) {
     371          return PAM_AUTH_ERR;
     372      }
     373  
     374      if (hmac_management(pamh, debug, mac, mac_length, key, key_length,
     375                          text, text_length)) {
     376          return PAM_AUTH_ERR;
     377      }
     378  
     379      return PAM_SUCCESS;
     380  }
     381  
     382  #endif /* WITH_OPENSSL */