Simplify memory allocation in read_file
[platform/core/security/yaca.git] / src / debug.c
1 /*
2  *  Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  *  Contact: Krzysztof Jackiewicz <k.jackiewicz@samsung.com>
5  *
6  *  Licensed under the Apache License, Version 2.0 (the "License");
7  *  you may not use this file except in compliance with the License.
8  *  You may obtain a copy of the License at
9  *
10  *      http://www.apache.org/licenses/LICENSE-2.0
11  *
12  *  Unless required by applicable law or agreed to in writing, software
13  *  distributed under the License is distributed on an "AS IS" BASIS,
14  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  *  See the License for the specific language governing permissions and
16  *  limitations under the License
17  */
18
19 /**
20  * @file debug.c
21  * @brief
22  */
23
24 #include <stdbool.h>
25 #include <string.h>
26
27 #include <openssl/err.h>
28 #include <openssl/pem.h>
29
30 #include <yaca_error.h>
31
32 #include "internal.h"
33 #include "debug.h"
34
35 // TODO any better idea than to use __thread?
36 static __thread yaca_error_cb error_cb = NULL;
37 static bool error_strings_loaded = false;
38
39 API void yaca_debug_set_error_cb(yaca_error_cb fn)
40 {
41         error_cb = fn;
42 }
43
44 char *error_translate(yaca_error_e err)
45 {
46         switch (err) {
47         case YACA_ERROR_NONE:
48                 return "YACA_ERROR_NONE";
49         case YACA_ERROR_INVALID_ARGUMENT:
50                 return "YACA_ERROR_INVALID_ARGUMENT";
51         case YACA_ERROR_OUT_OF_MEMORY:
52                 return "YACA_ERROR_OUT_OF_MEMORY";
53         case YACA_ERROR_INTERNAL:
54                 return "YACA_ERROR_INTERNAL";
55         case YACA_ERROR_DATA_MISMATCH:
56                 return "YACA_ERROR_DATA_MISMATCH";
57         case YACA_ERROR_PASSWORD_INVALID:
58                 return "YACA_ERROR_PASSWORD_INVALID";
59         default:
60                 return NULL;
61         }
62 }
63
64 void error_dump(const char *file, int line, const char *function, int code)
65 {
66         if (error_cb == NULL) {
67                 ERR_clear_error();
68                 return;
69         }
70
71         static const size_t BUF_SIZE = 512;
72         static const char ELLIPSIS[] = "...\n";
73         static const size_t ELLIPSIS_SIZE = sizeof(ELLIPSIS) / sizeof(ELLIPSIS[0]);
74         char buf[BUF_SIZE];
75         unsigned long err;
76         size_t written;
77         const char *err_str = error_translate(code);
78
79         written = snprintf(buf, BUF_SIZE, "%s:%d %s() API error: ", file, line, function);
80         if (err_str != NULL)
81                 written += snprintf(buf + written, BUF_SIZE - written, "%s\n", err_str);
82         else
83                 written += snprintf(buf + written, BUF_SIZE - written, "0x%X\n", code);
84
85         while ((err = ERR_get_error()) != 0 && written < BUF_SIZE - 1) {
86                 if (!error_strings_loaded) {
87                         /*
88                          * This function is thread-safe as long as static locks are
89                          * installed according to doc so calling it twice won't break
90                          * anything and I don't want to use synchronization mechanisms
91                          * here.
92                          */
93                         ERR_load_crypto_strings();
94                         error_strings_loaded = true;
95                 }
96
97                 ERR_error_string_n(err, buf + written, BUF_SIZE - written);
98                 written = strlen(buf); /* I trust you, openssl */
99                 if (written < BUF_SIZE - 1) {
100                         buf[written] = '\n';
101                         written++;
102                 }
103         }
104
105         if (written >= BUF_SIZE - 1) {
106                 strncpy(buf + BUF_SIZE - ELLIPSIS_SIZE, ELLIPSIS, ELLIPSIS_SIZE);
107                 written = BUF_SIZE - 1;
108                 ERR_clear_error();
109         }
110         buf[written] = '\0';
111
112         (*error_cb)(buf);
113 }
114
115 int error_handle(const char *file, int line, const char *function)
116 {
117         int ret;
118         unsigned long err = ERR_peek_error();
119
120         /* fatal errors */
121         if (ERR_FATAL_ERROR(err) > 0) {
122                 switch (ERR_GET_REASON(err)) {
123                 case ERR_R_MALLOC_FAILURE:
124                         ret = YACA_ERROR_OUT_OF_MEMORY;
125                         break;
126                 case ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED:
127                 case ERR_R_PASSED_NULL_PARAMETER:
128                         ret = YACA_ERROR_INVALID_ARGUMENT;
129                         break;
130                 case ERR_R_INTERNAL_ERROR:
131                 case ERR_R_DISABLED:
132                 default:
133                         ret = YACA_ERROR_INTERNAL;
134                 }
135         /* known errors */
136         } else {
137                 switch (err) {
138                 case ERR_PACK(ERR_LIB_RSA, RSA_F_PKEY_RSA_CTRL, RSA_R_INVALID_KEYBITS):
139                 case ERR_PACK(ERR_LIB_EVP, EVP_F_EVP_PKEY_CTX_CTRL, EVP_R_COMMAND_NOT_SUPPORTED):
140                         ret = YACA_ERROR_INVALID_ARGUMENT;
141                         break;
142                 case ERR_PACK(ERR_LIB_PEM, PEM_F_PEM_DO_HEADER, PEM_R_BAD_DECRYPT):
143                 case ERR_PACK(ERR_LIB_EVP, EVP_F_EVP_DECRYPTFINAL_EX, EVP_R_BAD_DECRYPT):
144                         ret = YACA_ERROR_PASSWORD_INVALID;
145                         break;
146                 default:
147                         error_dump(file, line, function, YACA_ERROR_INTERNAL);
148                         ret = YACA_ERROR_INTERNAL;
149                 }
150         }
151
152         /* remove all errors from queue */
153         ERR_clear_error();
154         return ret;
155 }