1 /*        $NetBSD: ssl_init.c,v 1.14 2024/08/18 20:47:13 christos Exp $         */
2 
3 /*
4  * ssl_init.c       Common OpenSSL initialization code for the various
5  *                  programs which use it.
6  *
7  * Moved from ntpd/ntp_crypto.c crypto_setup()
8  */
9 #ifdef HAVE_CONFIG_H
10 # include <config.h>
11 #endif
12 #include <ctype.h>
13 #include <ntp.h>
14 #include <ntp_debug.h>
15 #include <lib_strbuf.h>
16 
17 #ifdef OPENSSL
18 # include <openssl/crypto.h>
19 # include <openssl/err.h>
20 # include <openssl/evp.h>
21 # include <openssl/opensslv.h>
22 # include "libssl_compat.h"
23 # ifdef HAVE_OPENSSL_CMAC_H
24 #  include <openssl/cmac.h>
25 #  define CMAC_LENGTH         16
26 #  define CMAC                "AES128CMAC"
27 # endif /*HAVE_OPENSSL_CMAC_H*/
28 
29 EVP_MD_CTX *digest_ctx;
30 
31 
32 static void
atexit_ssl_cleanup(void)33 atexit_ssl_cleanup(void)
34 {
35           if (NULL == digest_ctx) {
36                     return;
37           }
38           EVP_MD_CTX_free(digest_ctx);
39           digest_ctx = NULL;
40 #if OPENSSL_VERSION_NUMBER < 0x10100000L
41           EVP_cleanup();
42           ERR_free_strings();
43 #endif    /* OpenSSL < 1.1 */
44 }
45 
46 
47 void
ssl_init(void)48 ssl_init(void)
49 {
50           init_lib();
51 
52           if (NULL == digest_ctx) {
53 #if OPENSSL_VERSION_NUMBER < 0x10100000L
54                     ERR_load_crypto_strings();
55                     OpenSSL_add_all_algorithms();
56 #endif    /* OpenSSL < 1.1 */
57                     digest_ctx = EVP_MD_CTX_new();
58                     INSIST(digest_ctx != NULL);
59                     atexit(&atexit_ssl_cleanup);
60           }
61 }
62 
63 
64 void
ssl_check_version(void)65 ssl_check_version(void)
66 {
67           u_long    v;
68           char *  buf;
69 
70           v = OpenSSL_version_num();
71           if ((v ^ OPENSSL_VERSION_NUMBER) & ~0xff0L) {
72                     LIB_GETBUF(buf);
73                     snprintf(buf, LIB_BUFLENGTH,
74                                "OpenSSL version mismatch."
75                                "Built against %lx, you have %lx\n",
76                                (u_long)OPENSSL_VERSION_NUMBER, v);
77                     msyslog(LOG_WARNING, "%s", buf);
78                     fputs(buf, stderr);
79           }
80           INIT_SSL();
81 }
82 #endif    /* OPENSSL */
83 
84 
85 /*
86  * keytype_from_text          returns OpenSSL NID for digest by name, and
87  *                            optionally the associated digest length.
88  *
89  * Used by ntpd authreadkeys(), ntpq and ntpdc keytype()
90  */
91 int
keytype_from_text(const char * text,size_t * pdigest_len)92 keytype_from_text(
93           const char *        text,
94           size_t *  pdigest_len
95           )
96 {
97           int                 key_type;
98           u_int               digest_len;
99 #ifdef OPENSSL      /* --*-- OpenSSL code --*-- */
100           const u_long        max_digest_len = MAX_MDG_LEN;
101           char *              upcased;
102           char *              pch;
103           EVP_MD const *      md;
104 
105           /*
106            * OpenSSL digest short names are capitalized, so uppercase the
107            * digest name before passing to OBJ_sn2nid().  If it is not
108            * recognized but matches our CMAC string use NID_cmac, or if
109            * it begins with 'M' or 'm' use NID_md5 to be consistent with
110            * past behavior.
111            */
112           INIT_SSL();
113 
114           /* get name in uppercase */
115           LIB_GETBUF(upcased);
116           strlcpy(upcased, text, LIB_BUFLENGTH);
117 
118           for (pch = upcased; '\0' != *pch; pch++) {
119                     *pch = (char)toupper((unsigned char)*pch);
120           }
121 
122           key_type = OBJ_sn2nid(upcased);
123 
124 #   ifdef ENABLE_CMAC
125           if (!key_type && !strncmp(CMAC, upcased, strlen(CMAC) + 1)) {
126                     key_type = NID_cmac;
127 
128                     if (debug) {
129                               fprintf(stderr, "%s:%d:%s():%s:key\n",
130                                         __FILE__, __LINE__, __func__, CMAC);
131                     }
132           }
133 #   endif /*ENABLE_CMAC*/
134 #else
135 
136           key_type = 0;
137 #endif
138 
139           if (!key_type && 'm' == tolower((unsigned char)text[0])) {
140                     key_type = NID_md5;
141           }
142 
143           if (!key_type) {
144                     return 0;
145           }
146 
147           if (NULL != pdigest_len) {
148 #ifdef OPENSSL
149                     md = EVP_get_digestbynid(key_type);
150                     digest_len = (md) ? EVP_MD_size(md) : 0;
151 
152                     if (!md || digest_len <= 0) {
153 #   ifdef ENABLE_CMAC
154                         if (key_type == NID_cmac) {
155                               digest_len = CMAC_LENGTH;
156 
157                               if (debug) {
158                                         fprintf(stderr, "%s:%d:%s():%s:len\n",
159                                                   __FILE__, __LINE__, __func__, CMAC);
160                               }
161                         } else
162 #   endif /*ENABLE_CMAC*/
163                         {
164                               fprintf(stderr,
165                                         "key type %s is not supported by OpenSSL\n",
166                                         keytype_name(key_type));
167                               msyslog(LOG_ERR,
168                                         "key type %s is not supported by OpenSSL\n",
169                                         keytype_name(key_type));
170                               return 0;
171                         }
172                     }
173 
174                     if (digest_len > max_digest_len) {
175                         fprintf(stderr,
176                                   "key type %s %u octet digests are too big, max %lu\n",
177                                   keytype_name(key_type), digest_len,
178                                   max_digest_len);
179                         msyslog(LOG_ERR,
180                                   "key type %s %u octet digests are too big, max %lu",
181                                   keytype_name(key_type), digest_len,
182                                   max_digest_len);
183                         return 0;
184                     }
185 #else
186                     digest_len = MD5_LENGTH;
187 #endif
188                     *pdigest_len = digest_len;
189           }
190 
191           return key_type;
192 }
193 
194 
195 /*
196  * keytype_name               returns OpenSSL short name for digest by NID.
197  *
198  * Used by ntpq and ntpdc keytype()
199  */
200 const char *
keytype_name(int type)201 keytype_name(
202           int type
203           )
204 {
205           static const char unknown_type[] = "(unknown key type)";
206           const char *name;
207 
208 #ifdef OPENSSL
209           INIT_SSL();
210           name = OBJ_nid2sn(type);
211 
212 #   ifdef ENABLE_CMAC
213           if (NID_cmac == type) {
214                     name = CMAC;
215           } else
216 #   endif /*ENABLE_CMAC*/
217           if (NULL == name) {
218                     name = unknown_type;
219           }
220 #else     /* !OPENSSL follows */
221           if (NID_md5 == type)
222                     name = "MD5";
223           else
224                     name = unknown_type;
225 #endif
226           return name;
227 }
228 
229 
230 /*
231  * Use getpassphrase() if configure.ac detected it, as Suns that
232  * have it truncate the password in getpass() to 8 characters.
233  */
234 #ifdef HAVE_GETPASSPHRASE
235 # define  getpass(str)        getpassphrase(str)
236 #endif
237 
238 /*
239  * getpass_keytype() -- shared between ntpq and ntpdc, only vaguely
240  *                            related to the rest of ssl_init.c.
241  */
242 char *
getpass_keytype(int type)243 getpass_keytype(
244           int       type
245           )
246 {
247           char      pass_prompt[64 + 11 + 1]; /* 11 for " Password: " */
248 
249           snprintf(pass_prompt, sizeof(pass_prompt),
250                      "%.64s Password: ", keytype_name(type));
251 
252           return getpass(pass_prompt);
253 }
254 
255