1 /*
2  * ProFTPD - mod_auth_otp OpenSSL interface
3  * Copyright (c) 2015-2018 TJ Saunders
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
18  *
19  * As a special exemption, TJ Saunders and other respective copyright holders
20  * give permission to link this program with OpenSSL, and distribute the
21  * resulting executable, without including the source code for OpenSSL in the
22  * source distribution.
23  */
24 
25 #include "mod_auth_otp.h"
26 #include "crypto.h"
27 
auth_otp_crypto_init(void)28 int auth_otp_crypto_init(void) {
29   return 0;
30 }
31 
auth_otp_crypto_free(int flags)32 void auth_otp_crypto_free(int flags) {
33   /* Only call EVP_cleanup() et al if other OpenSSL-using modules are not
34    * present.  If we called EVP_cleanup() here during a restart,
35    * and other modules want to use OpenSSL, we may be depriving those modules
36    * of OpenSSL functionality.
37    *
38    * At the moment, the modules known to use OpenSSL are mod_ldap,
39    * mod_sftp, mod_sql, and mod_sql_passwd, and mod_tls.
40    */
41   if (pr_module_get("mod_digest.c") == NULL &&
42       pr_module_get("mod_ldap.c") == NULL &&
43       pr_module_get("mod_proxy.c") == NULL &&
44       pr_module_get("mod_radius.c") == NULL &&
45       pr_module_get("mod_sftp.c") == NULL &&
46       pr_module_get("mod_sql.c") == NULL &&
47       pr_module_get("mod_sql_passwd.c") == NULL &&
48       pr_module_get("mod_tls.c") == NULL) {
49 
50     ERR_free_strings();
51 
52 #if OPENSSL_VERSION_NUMBER >= 0x10000001L
53 # if OPENSSL_VERSION_NUMBER >= 0x10100000L
54     /* The ERR_remove_state(0) usage is deprecated due to thread ID
55      * differences among platforms; see the OpenSSL-1.0.0c CHANGES file
56      * for details.  So for new enough OpenSSL installations, use the
57      * proper way to clear the error queue state.
58      */
59     ERR_remove_thread_state(NULL);
60 # endif /* OpenSSL-1.1.x and later */
61 #else
62     ERR_remove_state(0);
63 #endif /* OpenSSL prior to 1.0.0-beta1 */
64 
65     EVP_cleanup();
66     RAND_cleanup();
67   }
68 }
69 
auth_otp_crypto_get_errors(void)70 const char *auth_otp_crypto_get_errors(void) {
71   unsigned int count = 0;
72   unsigned long e = ERR_get_error();
73   BIO *bio = NULL;
74   char *data = NULL;
75   long datalen;
76   const char *str = "(unknown)";
77 
78   /* Use ERR_print_errors() and a memory BIO to build up a string with
79    * all of the error messages from the error queue.
80    */
81 
82   if (e) {
83     bio = BIO_new(BIO_s_mem());
84   }
85 
86   while (e) {
87     pr_signals_handle();
88     BIO_printf(bio, "\n  (%u) %s", ++count, ERR_error_string(e, NULL));
89     e = ERR_get_error();
90   }
91 
92   datalen = BIO_get_mem_data(bio, &data);
93   if (data) {
94     data[datalen] = '\0';
95     str = pstrndup(auth_otp_pool, data, datalen-1);
96   }
97 
98   if (bio) {
99     BIO_free(bio);
100   }
101 
102   return str;
103 }
104 
auth_otp_hmac(const EVP_MD * md,const unsigned char * key,size_t key_len,const unsigned char * data,size_t data_len,unsigned char * mac,size_t * mac_len)105 int auth_otp_hmac(const EVP_MD *md, const unsigned char *key, size_t key_len,
106     const unsigned char *data, size_t data_len, unsigned char *mac,
107     size_t *mac_len) {
108 
109   if ((key == NULL || key_len == 0) ||
110       (data == NULL || data_len == 0) ||
111       (mac == NULL || mac_len == NULL)) {
112     errno = EINVAL;
113     return -1;
114   }
115 
116   if (HMAC(md, key, key_len, data, data_len, mac,
117       (unsigned int *) mac_len) == NULL) {
118     (void) pr_log_writefile(auth_otp_logfd, MOD_AUTH_OTP_VERSION,
119       "HMAC error: %s", auth_otp_crypto_get_errors());
120     errno = EPERM;
121     return -1;
122   }
123 
124   return 0;
125 }
126