1 /*
2  * ck - C11 Annex K wrappers  (selected functions; not complete)
3  *
4  * ck is also an abbreviation for "check".
5  * These are validating, checking functions.
6  *
7  * Copyright(c) 2016 Glenn Strauss gstrauss()gluelogic.com  All rights reserved
8  * License: BSD 3-clause (same as lighttpd)
9  */
10 #ifndef __STDC_WANT_LIB_EXT1__
11 #define __STDC_WANT_LIB_EXT1__ 1
12 #endif
13 #ifndef _XOPEN_SOURCE
14 #define _XOPEN_SOURCE 700
15 #endif
16 #ifndef _NETBSD_SOURCE
17 #define _NETBSD_SOURCE
18 #endif
19 #ifdef __OpenBSD__
20 #ifndef _BSD_SOURCE
21 #define _BSD_SOURCE
22 #endif
23 #endif
24 #include "first.h"
25 
26 #include "ck.h"
27 
28 #include <stdlib.h>     /* abort() getenv() getenv_s() */
29 #include <string.h>     /* memcpy() memset() memset_s() explicit_bzero()
30                          * strerror() strerror_r() strerror_s() strlen() */
31 
32 #ifdef __STDC_LIB_EXT1__
33 #ifndef HAVE_MEMSET_S
34 #define HAVE_MEMSET_S
35 #endif
36 #else
37 #include <errno.h>
38 #include <stdio.h>      /* snprintf() */
39 #endif
40 
41 #ifndef HAVE_MEMSET_S
42 
43 #ifdef _WIN32
44 #define VC_EXTRALEAN
45 #define WIN32_LEAN_AND_MEAN
46 #include <windows.h>    /* SecureZeroMemory() */
47 /*(Windows XP and later provide SecureZeroMemory())*/
48 #define HAVE_SECUREZEROMEMORY
49 #else /* !_WIN32 */
50 #ifdef HAVE_SIGNAL
51 #include <signal.h>     /* sig_atomic_t */
52 #else
53 typedef int sig_atomic_t;
54 #endif
55 /*#include <plasma/plasma_membar.h>*/  /* plasma_membar_ccfence() */
56 #endif
57 
58 #endif /* !HAVE_MEMSET_S */
59 
60 
61 #if !defined(HAVE_MEMSET_S)        \
62  && !defined(HAVE_EXPLICIT_BZERO)  \
63  && !defined(HAVE_EXPLICIT_MEMSET) \
64  && !defined(HAVE_SECUREZEROMEMORY)
65 
66 typedef void *(*ck_memclear_func_t)(void *, int, size_t);
67 extern volatile ck_memclear_func_t ck_memclear_func;
68 volatile ck_memclear_func_t ck_memclear_func = memset;
69 
70 #ifdef HAVE_WEAK_SYMBOLS
71 /* it seems weak functions are never inlined, even for static builds */
72 __attribute__((__weak__))
73 void ck_memclear_s_hook (void *buf, rsize_t len);
ck_memclear_s_hook(void * buf __attribute_unused__,rsize_t len __attribute_unused__)74 void ck_memclear_s_hook (void *buf    __attribute_unused__,
75                          rsize_t len  __attribute_unused__)
76 {
77     /*(application might define func to call OPENSSL_cleanse(), if available)*/
78     (void)(buf); /* UNUSED */
79     (void)(len); /* UNUSED */
80 }
81 #endif /* HAVE_WEAK_SYMBOLS */
82 
83 static void *
ck_memset_compat(void * s,int c,size_t n)84 ck_memset_compat(void *s, int c, size_t n)
85 {
86     /* attempt to inhibit compiler/linker heuristics which might elide memset()
87      * - insert compiler optimization fences around memset()
88      * - access s through volatile pointer at volatile index after memset()
89      * - pass s to weak (overridable) func to create additional data dependency
90      */
91 
92     if (0 == n)    /*(must check n > 0 since s[0] will be accessed)*/
93         return s;
94 
95     static volatile sig_atomic_t vzero;
96     volatile unsigned char *vs = (volatile unsigned char *)s;
97     do {
98         /*plasma_membar_ccfence();*/
99         ck_memclear_func(s, c, n);
100         /*plasma_membar_ccfence();*/
101     } while (vs[vzero] != c);
102 
103   #ifdef HAVE_WEAK_SYMBOLS
104     ck_memclear_s_hook(s, n);
105   #endif
106 
107     return s;
108 }
109 
110 #endif
111 
112 
113 errno_t
ck_memclear_s(void * const s,const rsize_t smax,rsize_t n)114 ck_memclear_s (void * const s, const rsize_t smax, rsize_t n)
115 {
116   #ifdef HAVE_MEMSET_S
117 
118     return memset_s(s, smax, 0, n);
119 
120   #else
121 
122     if (NULL == s)
123         /* runtime constraint violation */
124         return EINVAL;
125     if (RSIZE_MAX < smax)
126         /* runtime constraint violation */
127         return E2BIG;
128 
129     errno_t rc = 0;
130     if (RSIZE_MAX < n) {
131         /* runtime constraint violation */
132         rc = EINVAL;
133         n = smax;
134     }
135     if (smax < n) {
136         /* runtime constraint violation */
137         rc = EOVERFLOW;
138         n = smax;
139     }
140 
141    #if defined(HAVE_EXPLICIT_BZERO)
142     explicit_bzero(s, n);
143    #elif defined(HAVE_EXPLICIT_MEMSET)
144     explicit_memset(s, 0, n);
145    #elif defined(HAVE_SECUREZEROMEMORY)
146     SecureZeroMemory(s, n);
147    #else
148     ck_memset_compat(s, 0, n);
149    #endif
150 
151     return rc;
152 
153   #endif
154 }
155 
156 
157 #if 0 /*(not currently used in lighttpd; lighttpd process env is stable)*/
158 errno_t
159 ck_getenv_s (size_t * const restrict len,
160              char * const restrict value, const rsize_t maxsize,
161              const char * const restrict name)
162 {
163   #ifdef __STDC_LIB_EXT1__
164 
165     return getenv_s(len, value, maxsize, name);
166 
167   #else
168 
169     if (NULL == name || RSIZE_MAX < maxsize || (0 != maxsize && NULL == value)){
170         /* runtime constraint violation */
171         if (NULL != len)
172             *len = 0;
173         if (NULL != value && maxsize)
174             *value = '\0';
175         return EINVAL;
176     }
177 
178     const char * const v = getenv(name);
179     if (NULL != v) {
180         const size_t vlen = strlen(v);
181         if (NULL != len)
182             *len = vlen;
183         if (vlen < maxsize) {
184             memcpy(value, v, vlen+1);
185             return 0;
186         }
187         else {
188             if (maxsize)
189                 *value = '\0';
190             return ERANGE;
191         }
192     }
193     else {
194         if (NULL != len)
195             *len = 0;
196         if (maxsize)
197             *value = '\0';
198       #ifdef ENODATA
199         return ENODATA;
200       #else
201         return ENOENT;
202       #endif
203     }
204 
205   #endif
206 }
207 #endif
208 
209 
210 errno_t
ck_strerror_s(char * const s,const rsize_t maxsize,const errno_t errnum)211 ck_strerror_s (char * const s, const rsize_t maxsize, const errno_t errnum)
212 {
213   #ifdef __STDC_LIB_EXT1__
214 
215     return strerror_s(s, maxsize, errnum);
216 
217   #else
218 
219     if (NULL == s || 0 == maxsize || RSIZE_MAX < maxsize) {
220         /* runtime constraint violation */
221         return EINVAL;
222     }
223 
224     /*(HAVE_STRERROR_R defined after tests by configure.ac or SConstruct)*/
225   #if !defined(HAVE_STRERROR_R) && !defined(HAVE_CONFIG_H)
226   #define HAVE_STRERROR_R 1
227   #endif /*(assume strerror_r() available if no config.h)*/
228 
229   #ifdef HAVE_STRERROR_R
230     char buf[1024];
231    #if defined(_GNU_SOURCE) && defined(__GLIBC__)
232     const char *errstr = strerror_r(errnum,buf,sizeof(buf));
233    #else /* XSI-compliant strerror_r() */
234     const char *errstr = (0 == strerror_r(errnum,buf,sizeof(buf))) ? buf : NULL;
235    #endif
236   #else /* !HAVE_STRERROR_R */
237     const char *errstr = strerror(errnum);
238   #endif
239     if (NULL != errstr) {
240         const size_t errlen = strlen(errstr);
241         if (errlen < maxsize) {
242             memcpy(s, errstr, errlen+1);
243             return 0;
244         }
245         else {
246             memcpy(s, errstr, maxsize-1);
247             s[maxsize-1] = '\0';
248             /*(fall through; not enough space to store entire error string)*/
249         }
250     }
251     else {
252         if ((rsize_t)snprintf(s, maxsize, "Unknown error %d", errnum) < maxsize)
253             return 0;
254         /*(else fall through; not enough space to store entire error string)*/
255     }
256 
257     /*(not enough space to store entire error string)*/
258     if (maxsize > 3)
259         memcpy(s+maxsize-4, "...", 3);
260     return ERANGE;
261 
262   #endif
263 }
264 
265 
266 int
ck_memeq_const_time(const void * a,size_t alen,const void * b,size_t blen)267 ck_memeq_const_time (const void *a, size_t alen, const void *b, size_t blen)
268 {
269     /* constant time memory compare for equality */
270     /* rounds to next multiple of 64 to avoid potentially leaking exact
271      * string lengths when subject to high precision timing attacks
272      */
273     /* Note: some libs provide similar funcs but might not obscure length, e.g.
274      * OpenSSL:
275      *   int CRYPTO_memcmp(const void * in_a, const void * in_b, size_t len)
276      * Note: some OS provide similar funcs but might not obscure length, e.g.
277      * OpenBSD: int timingsafe_bcmp(const void *b1, const void *b2, size_t len)
278      * NetBSD: int consttime_memequal(void *b1, void *b2, size_t len)
279      */
280     const volatile unsigned char * const av =
281       (const unsigned char *)(alen ? a : "");
282     const volatile unsigned char * const bv =
283       (const unsigned char *)(blen ? b : "");
284     size_t lim = ((alen >= blen ? alen : blen) + 0x3F) & ~0x3F;
285     int diff = (alen != blen); /*(never match if string length mismatch)*/
286     alen -= (alen != 0);
287     blen -= (blen != 0);
288     for (size_t i = 0, j = 0; lim; --lim) {
289         diff |= (av[i] ^ bv[j]);
290         i += (i < alen);
291         j += (j < blen);
292     }
293     return (0 == diff);
294 }
295 
296 
297 int
ck_memeq_const_time_fixed_len(const void * a,const void * b,const size_t len)298 ck_memeq_const_time_fixed_len (const void *a, const void *b, const size_t len)
299 {
300     /* constant time memory compare for equality for fixed len (e.g. digests)
301      * (padding not necessary for digests, which have fixed, defined lengths) */
302     /* caller should prefer ck_memeq_const_time() if not operating on digests */
303     const volatile unsigned char * const av = (const unsigned char *)a;
304     const volatile unsigned char * const bv = (const unsigned char *)b;
305     int diff = 0;
306     for (size_t i = 0; i < len; ++i) {
307         diff |= (av[i] ^ bv[i]);
308     }
309     return (0 == diff);
310 }
311 
312 
313 
314 
315 #include <stdio.h>      /* fflush() fprintf() snprintf() */
316 
317 #ifdef HAVE_LIBUNWIND
318 #define UNW_LOCAL_ONLY
319 #include <libunwind.h>
320 __attribute_cold__
321 __attribute_noinline__
322 static void
ck_backtrace(FILE * fp)323 ck_backtrace (FILE *fp)
324 {
325     int rc;
326     unsigned int frame = 0;
327     unw_word_t ip;
328     unw_word_t offset;
329     unw_cursor_t cursor;
330     unw_context_t context;
331     unw_proc_info_t procinfo;
332     char name[256];
333 
334     rc = unw_getcontext(&context);
335     if (0 != rc) goto error;
336     rc = unw_init_local(&cursor, &context);
337     if (0 != rc) goto error;
338 
339     fprintf(fp, "Backtrace:\n");
340     while (0 < (rc = unw_step(&cursor))) {
341         ++frame;
342         ip = 0;
343         rc = unw_get_reg(&cursor, UNW_REG_IP, &ip);
344         if (0 != rc) break;
345         if (0 == ip) {
346             /* without an IP the other functions are useless;
347              * unw_get_proc_name would return UNW_EUNSPEC */
348             fprintf(fp, "%u: (nil)\n", frame);
349             continue;
350         }
351 
352         rc = unw_get_proc_info(&cursor, &procinfo);
353         if (0 != rc) break;
354 
355         offset = 0;
356         rc = unw_get_proc_name(&cursor, name, sizeof(name), &offset);
357         if (0 != rc) {
358             switch (-rc) {
359               case UNW_ENOMEM:
360                 memcpy(name + sizeof(name) - 4, "...", 4);
361                 break;
362               case UNW_ENOINFO:
363                 name[0] = '?';
364                 name[1] = '\0';
365                 break;
366               default:
367                 snprintf(name, sizeof(name),
368                          "?? (unw_get_proc_name error %d)", -rc);
369                 break;
370             }
371         }
372 
373         fprintf(fp, "%.2u: [%.012lx] (+%04x) %s\n",
374                 frame,(long unsigned)(uintptr_t)ip,(unsigned int)offset,name);
375     }
376     if (0 == rc)
377         return;
378 
379 error:
380     fprintf(fp, "Error while generating backtrace: unwind error %i\n",(int)-rc);
381 }
382 #endif
383 
384 
385 __attribute_noinline__
__attribute_nonnull__()386 __attribute_nonnull__()
387 static void
388 ck_bt_stderr (const char *filename, unsigned int line, const char *msg, const char *fmt)
389 {
390     fprintf(stderr, fmt, filename, line, msg);
391   #ifdef HAVE_LIBUNWIND
392     ck_backtrace(stderr);
393   #endif
394     fflush(stderr);
395 }
396 
397 
398 void
ck_bt(const char * filename,unsigned int line,const char * msg)399 ck_bt (const char *filename, unsigned int line, const char *msg)
400 {
401     ck_bt_stderr(filename, line, msg, "%s.%u: %s\n");
402 }
403 
404 
405 __attribute_noreturn__
406 void
ck_bt_abort(const char * filename,unsigned int line,const char * msg)407 ck_bt_abort (const char *filename, unsigned int line, const char *msg)
408 {
409     ck_bt(filename, line, msg);
410     abort();
411 }
412 
413 
414 __attribute_noreturn__
ck_assert_failed(const char * filename,unsigned int line,const char * msg)415 void ck_assert_failed(const char *filename, unsigned int line, const char *msg)
416 {
417     /* same as ck_bt_abort() but add "assertion failed: " prefix here
418      * to avoid bloating string tables in callers */
419     ck_bt_stderr(filename, line, msg, "%s.%u: assertion failed: %s\n");
420     abort();
421 }
422