Line | Count | Source |
1 | | /** |
2 | | * XML Security Library (http://www.aleksey.com/xmlsec). |
3 | | * |
4 | | * This is free software; see the Copyright file in the source distribution for precise wording. |
5 | | * |
6 | | * Copyright (C) 2002-2026 Aleksey Sanin <aleksey@aleksey.com>. All Rights Reserved. |
7 | | */ |
8 | | /** |
9 | | * @addtogroup xmlsec_core_errors |
10 | | * @brief Error reporting and logging functions. |
11 | | */ |
12 | | #include "globals.h" |
13 | | |
14 | | #include <stdlib.h> |
15 | | #include <stdio.h> |
16 | | #include <stdarg.h> |
17 | | #include <time.h> |
18 | | #include <string.h> |
19 | | |
20 | | #include <libxml/tree.h> |
21 | | |
22 | | #include <xmlsec/xmlsec.h> |
23 | | #include <xmlsec/xmltree.h> |
24 | | #include <xmlsec/private.h> |
25 | | #include <xmlsec/errors.h> |
26 | | |
27 | | /* Must be bigger than fatal_error */ |
28 | | #define XMLSEC_ERRORS_BUFFER_SIZE 1024 |
29 | | |
30 | | /* Must fit into xmlChar[XMLSEC_ERRORS_BUFFER_SIZE] */ |
31 | | static const xmlChar fatal_error[] = "Can not format error message"; |
32 | | |
33 | | typedef struct _xmlSecErrorDescription xmlSecErrorDescription, *xmlSecErrorDescriptionPtr; |
34 | | struct _xmlSecErrorDescription { |
35 | | int errorCode; |
36 | | const char* errorMsg; |
37 | | }; |
38 | | |
39 | | static const xmlSecErrorDescription xmlSecErrorsTable[XMLSEC_ERRORS_MAX_NUMBER + 1] = { |
40 | | { XMLSEC_ERRORS_R_XMLSEC_FAILED, "xmlsec library function failed" }, |
41 | | { XMLSEC_ERRORS_R_MALLOC_FAILED, "malloc function failed" }, |
42 | | { XMLSEC_ERRORS_R_STRDUP_FAILED, "strdup function failed" }, |
43 | | { XMLSEC_ERRORS_R_CRYPTO_FAILED, "crypto library function failed" }, |
44 | | { XMLSEC_ERRORS_R_XML_FAILED, "libxml2 library function failed" }, |
45 | | { XMLSEC_ERRORS_R_XSLT_FAILED, "libxslt library function failed" }, |
46 | | { XMLSEC_ERRORS_R_IO_FAILED, "io function failed" }, |
47 | | { XMLSEC_ERRORS_R_DISABLED, "feature is disabled" }, |
48 | | { XMLSEC_ERRORS_R_NOT_IMPLEMENTED, "feature is not implemented" }, |
49 | | { XMLSEC_ERRORS_R_INVALID_CONFIG, "invalid configuration" }, |
50 | | { XMLSEC_ERRORS_R_INVALID_SIZE, "invalid size" }, |
51 | | { XMLSEC_ERRORS_R_INVALID_DATA, "invalid data" }, |
52 | | { XMLSEC_ERRORS_R_INVALID_RESULT, "invalid result" }, |
53 | | { XMLSEC_ERRORS_R_INVALID_TYPE, "invalid type" }, |
54 | | { XMLSEC_ERRORS_R_INVALID_OPERATION, "invalid operation" }, |
55 | | { XMLSEC_ERRORS_R_INVALID_STATUS, "invalid status" }, |
56 | | { XMLSEC_ERRORS_R_INVALID_FORMAT, "invalid format" }, |
57 | | { XMLSEC_ERRORS_R_DATA_NOT_MATCH, "data do not match" }, |
58 | | { XMLSEC_ERRORS_R_INVALID_VERSION, "invalid version" }, |
59 | | { XMLSEC_ERRORS_R_INVALID_NODE, "invalid node" }, |
60 | | { XMLSEC_ERRORS_R_INVALID_NODE_CONTENT, "invalid node content" }, |
61 | | { XMLSEC_ERRORS_R_INVALID_NODE_ATTRIBUTE, "invalid node attribute" }, |
62 | | { XMLSEC_ERRORS_R_MISSING_NODE_ATTRIBUTE, "missing node attribute" }, |
63 | | { XMLSEC_ERRORS_R_NODE_ALREADY_PRESENT, "node already present" }, |
64 | | { XMLSEC_ERRORS_R_UNEXPECTED_NODE, "unexpected node" }, |
65 | | { XMLSEC_ERRORS_R_NODE_NOT_FOUND, "node not found" }, |
66 | | { XMLSEC_ERRORS_R_INVALID_TRANSFORM, "invalid transform" }, |
67 | | { XMLSEC_ERRORS_R_INVALID_TRANSFORM_KEY, "invalid transform key" }, |
68 | | { XMLSEC_ERRORS_R_INVALID_URI_TYPE, "invalid URI type" }, |
69 | | { XMLSEC_ERRORS_R_TRANSFORM_SAME_DOCUMENT_REQUIRED, "same document is required for transform" }, |
70 | | { XMLSEC_ERRORS_R_TRANSFORM_DISABLED, "transform is disabled" }, |
71 | | { XMLSEC_ERRORS_R_INVALID_ALGORITHM, "invalid or unsupported algorithm" }, |
72 | | { XMLSEC_ERRORS_R_INVALID_KEY_DATA, "invalid key data" }, |
73 | | { XMLSEC_ERRORS_R_KEY_DATA_NOT_FOUND, "key data is not found" }, |
74 | | { XMLSEC_ERRORS_R_KEY_DATA_ALREADY_EXIST, "key data already exist" }, |
75 | | { XMLSEC_ERRORS_R_INVALID_KEY_DATA_SIZE, "invalid key data size" }, |
76 | | { XMLSEC_ERRORS_R_KEY_NOT_FOUND, "key is not found" }, |
77 | | { XMLSEC_ERRORS_R_KEYDATA_DISABLED, "key data is disabled" }, |
78 | | { XMLSEC_ERRORS_R_MAX_RETRIEVALS_LEVEL, "maximum key retrieval level" }, |
79 | | { XMLSEC_ERRORS_R_MAX_RETRIEVAL_TYPE_MISMATCH,"key retrieval type mismatch" }, |
80 | | { XMLSEC_ERRORS_R_MAX_KEYINFOREFERENCE_LEVEL, "maximum KeyInfoReference level" }, |
81 | | { XMLSEC_ERRORS_R_MAX_ENCKEY_LEVEL, "maximum encrypted key level" }, |
82 | | { XMLSEC_ERRORS_R_CERT_VERIFY_FAILED, "certificate verification failed" }, |
83 | | { XMLSEC_ERRORS_R_CERT_NOT_FOUND, "certificate is not found" }, |
84 | | { XMLSEC_ERRORS_R_CERT_REVOKED, "certificate is revoked" }, |
85 | | { XMLSEC_ERRORS_R_CERT_ISSUER_FAILED, "certificate issuer check failed" }, |
86 | | { XMLSEC_ERRORS_R_CERT_NOT_YET_VALID, "certificate is not yet valid" }, |
87 | | { XMLSEC_ERRORS_R_CERT_HAS_EXPIRED, "certificate has expired" }, |
88 | | { XMLSEC_ERRORS_R_CRL_VERIFY_FAILED, "CRL verification failed" }, |
89 | | { XMLSEC_ERRORS_R_CRL_NOT_YET_VALID, "CRL is not yet valid" }, |
90 | | { XMLSEC_ERRORS_R_CRL_HAS_EXPIRED, "CRL has expired" }, |
91 | | { XMLSEC_ERRORS_R_DSIG_NO_REFERENCES, "Reference nodes are not found" }, |
92 | | { XMLSEC_ERRORS_R_DSIG_INVALID_REFERENCE, "Reference verification failed" }, |
93 | | { XMLSEC_ERRORS_R_ASSERTION, "assertion" }, |
94 | | { 0, NULL} |
95 | | }; |
96 | | |
97 | | /* We have system callback that can be set by the xmlsec-crypto library and user callback |
98 | | * that user can set. We always prioritize user callback if set. |
99 | | * |
100 | | * NOTE: The global state below (the callbacks, xmlSecPrintErrorMessages and |
101 | | * gXmlSecErrorsPrintCryptoLibraryLogOnExitIsEnabled) is plain mutable state that is read |
102 | | * and written without any synchronization. These functions are therefore NOT thread-safe: |
103 | | * a concurrent setter while another thread reports an error is a data race. Applications |
104 | | * must serialize access to the setters (e.g. configure them before spawning threads). */ |
105 | | static xmlSecErrorsCallback xmlSecErrorsSystemClbk = xmlSecErrorsDefaultCallback; |
106 | | static xmlSecErrorsCallback xmlSecErrorsUserClbk = NULL; |
107 | | static int xmlSecErrorsClbkIsSetByUser = 0; |
108 | | |
109 | | static int xmlSecPrintErrorMessages = 1; /* whether the error messages will be printed immediately */ |
110 | | |
111 | | static int gXmlSecErrorsPrintCryptoLibraryLogOnExitIsEnabled = 0; |
112 | | |
113 | | /** |
114 | | * @brief Initializes the errors reporting. |
115 | | * @details Initializes the errors reporting. It is called from #xmlSecInit function. |
116 | | * and applications must not call this function directly. |
117 | | */ |
118 | | void |
119 | 0 | xmlSecErrorsInit(void) { |
120 | 0 | } |
121 | | |
122 | | /** |
123 | | * @brief Cleanups the errors reporting. |
124 | | * @details Cleanups the errors reporting. It is called from #xmlSecShutdown function. |
125 | | * and applications must not call this function directly. |
126 | | */ |
127 | | void |
128 | 0 | xmlSecErrorsShutdown(void) { |
129 | 0 | } |
130 | | |
131 | | /** |
132 | | * @brief Sets the errors callback function. |
133 | | * @details Sets the errors callback function to @p callback that will be called |
134 | | * every time an error occurs. If @p callback is NULL then the errors output will be supressed. |
135 | | * |
136 | | * Note that this function is not thread-safe (see the module notes). |
137 | | * @param callback the new errors callback function, or NULL to fall back to the default. |
138 | | */ |
139 | | void |
140 | 0 | xmlSecErrorsSetCallback(xmlSecErrorsCallback callback) { |
141 | 0 | xmlSecErrorsUserClbk = callback; |
142 | 0 | xmlSecErrorsClbkIsSetByUser = 1; |
143 | 0 | } |
144 | | |
145 | | /** |
146 | | * @brief Clears the custom errors callback function. |
147 | | * @details Clears the custom errors callback function and restores default. |
148 | | * Note that this function is not thread-safe (see the module notes). |
149 | | */ |
150 | | void |
151 | 0 | xmlSecErrorsClearCallback(void) { |
152 | 0 | xmlSecErrorsUserClbk = NULL; |
153 | 0 | xmlSecErrorsClbkIsSetByUser = 0; |
154 | 0 | } |
155 | | |
156 | | |
157 | | /** |
158 | | * @brief Sets the system errors callback function. |
159 | | * @details Sets the system errors callback function to @p callback that will be called |
160 | | * every time an error occurs. |
161 | | * Note that this function is not thread-safe (see the module notes). |
162 | | * @param callback the new system errors callback function. |
163 | | */ |
164 | | void |
165 | 0 | xmlSecErrorsSetSystemCallback(xmlSecErrorsCallback callback) { |
166 | 0 | xmlSecErrorsSystemClbk = callback; |
167 | 0 | } |
168 | | |
169 | | /** |
170 | | * @brief The default error reporting callback using LibXML. |
171 | | * @details The default error reporting callback that utilizes LibXML |
172 | | * error reporting xmlGenericError function. |
173 | | * @param file the error location file name (__FILE__ macro). |
174 | | * @param line the error location line number (__LINE__ macro). |
175 | | * @param func the error location function name (__FUNCTION__ macro). |
176 | | * @param errorObject the error specific error object |
177 | | * @param errorSubject the error specific error subject. |
178 | | * @param reason the error code. |
179 | | * @param msg the additional error message. |
180 | | */ |
181 | | void |
182 | | xmlSecErrorsDefaultCallback(const char* file, int line, const char* func, |
183 | | const char* errorObject, const char* errorSubject, |
184 | 20.6k | int reason, const char* msg) { |
185 | 20.6k | if(xmlSecPrintErrorMessages) { |
186 | 20.6k | const char* error_msg = NULL; |
187 | 20.6k | xmlSecSize i; |
188 | | |
189 | 103k | for(i = 0; (i < XMLSEC_ERRORS_MAX_NUMBER) && (xmlSecErrorsGetMsg(i) != NULL); ++i) { |
190 | 103k | if(xmlSecErrorsGetCode(i) == reason) { |
191 | 20.6k | error_msg = xmlSecErrorsGetMsg(i); |
192 | 20.6k | break; |
193 | 20.6k | } |
194 | 103k | } |
195 | 20.6k | xmlGenericError(xmlGenericErrorContext, |
196 | 20.6k | "func=%s:file=%s:line=%d:obj=%s:subj=%s:error=%d:%s:%s\n", |
197 | 20.6k | (func != NULL) ? func : "unknown", |
198 | 20.6k | (file != NULL) ? file : "unknown", |
199 | 20.6k | line, |
200 | 20.6k | (errorObject != NULL) ? errorObject : "unknown", |
201 | 20.6k | (errorSubject != NULL) ? errorSubject : "unknown", |
202 | 20.6k | reason, |
203 | 20.6k | (error_msg != NULL) ? error_msg : "", |
204 | 20.6k | (msg != NULL) ? msg : ""); |
205 | 20.6k | } |
206 | 20.6k | } |
207 | | |
208 | | /** |
209 | | * @brief Enables or disables output from the default errors callback. |
210 | | * @details Enables or disables calling LibXML2 callback from the default |
211 | | * errors callback. |
212 | | * @param enabled the flag. |
213 | | */ |
214 | | void |
215 | 0 | xmlSecErrorsDefaultCallbackEnableOutput(int enabled) { |
216 | 0 | xmlSecPrintErrorMessages = enabled; |
217 | 0 | } |
218 | | |
219 | | /** |
220 | | * @brief Gets the known error code at position @p pos. |
221 | | * @param pos the error position. |
222 | | * @return the known error code or 0 if @p pos is greater than |
223 | | * total number of known error codes. |
224 | | */ |
225 | | int |
226 | 103k | xmlSecErrorsGetCode(xmlSecSize pos) { |
227 | | /* could not use asserts here! */ |
228 | 103k | if(pos < sizeof(xmlSecErrorsTable) / sizeof(xmlSecErrorsTable[0])) { |
229 | 103k | return(xmlSecErrorsTable[pos].errorCode); |
230 | 103k | } |
231 | 0 | return(0); |
232 | 103k | } |
233 | | |
234 | | /** |
235 | | * @brief Gets the known error message at position @p pos. |
236 | | * @param pos the error position. |
237 | | * @return the known error message or NULL if @p pos is greater than |
238 | | * total number of known error codes. |
239 | | */ |
240 | | const char* |
241 | 123k | xmlSecErrorsGetMsg(xmlSecSize pos) { |
242 | | /* could not use asserts here! */ |
243 | 123k | if(pos < sizeof(xmlSecErrorsTable) / sizeof(xmlSecErrorsTable[0])) { |
244 | 123k | return(xmlSecErrorsTable[pos].errorMsg); |
245 | 123k | } |
246 | 0 | return(NULL); |
247 | 123k | } |
248 | | |
249 | | /** |
250 | | * @brief Reports an error to the error callback. |
251 | | * @details Reports an error to the default (#xmlSecErrorsDefaultCallback) or |
252 | | * application specific callback installed using #xmlSecErrorsSetCallback |
253 | | * function. |
254 | | * @param file the error location filename (__FILE__). |
255 | | * @param line the error location line number (__LINE__). |
256 | | * @param func the error location function (__FUNCTION__). |
257 | | * @param errorObject the error specific error object (e.g. transform, key data, etc). |
258 | | * @param errorSubject the error specific error subject (e.g. failed function name). |
259 | | * @param reason the error code. |
260 | | * @param msg the error message in printf format. |
261 | | * @param ... the parameters for the @p msg. |
262 | | */ |
263 | | void |
264 | | xmlSecError(const char* file, int line, const char* func, |
265 | | const char* errorObject, const char* errorSubject, |
266 | | int reason, const char* msg, ... |
267 | 20.6k | ) { |
268 | 20.6k | xmlSecErrorsCallback callback = (xmlSecErrorsClbkIsSetByUser != 0) ? xmlSecErrorsUserClbk : xmlSecErrorsSystemClbk; |
269 | 20.6k | if(callback != NULL) { |
270 | 20.6k | xmlChar error_msg[XMLSEC_ERRORS_BUFFER_SIZE]; |
271 | 20.6k | int ret; |
272 | | |
273 | 20.6k | if(msg != NULL) { |
274 | | /* Make coverity + C23 happy */ |
275 | | #if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L |
276 | | va_list va = {0}; |
277 | | #else /* defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L */ |
278 | 20.6k | va_list va; |
279 | 20.6k | #endif /* defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202311L */ |
280 | | |
281 | 20.6k | va_start(va, msg); |
282 | 20.6k | ret = xmlStrVPrintf(error_msg, sizeof(error_msg), msg, va); |
283 | 20.6k | if(ret < 0) { |
284 | | /* Can't really report an error from an error callback */ |
285 | 0 | memcpy(error_msg, fatal_error, sizeof(fatal_error)); |
286 | 0 | } |
287 | 20.6k | error_msg[sizeof(error_msg) - 1] = '\0'; /* just in case */ |
288 | 20.6k | va_end(va); |
289 | 20.6k | } else { |
290 | 0 | error_msg[0] = '\0'; |
291 | 0 | } |
292 | 20.6k | callback(file, line, func, errorObject, errorSubject, reason, (char*)error_msg); |
293 | 20.6k | } |
294 | 20.6k | } |
295 | | |
296 | | /** |
297 | | * @brief Enables or disables the crypto library error log dump on exit. |
298 | | * @details Enables or disables the crypto library error log dump on exit (only supported by OpenSSL). |
299 | | * @param enabled the flag |
300 | | */ |
301 | | void |
302 | 0 | xmlSecErrorsPrintCryptoLibraryLogOnExitSet(int enabled) { |
303 | 0 | gXmlSecErrorsPrintCryptoLibraryLogOnExitIsEnabled = enabled; |
304 | 0 | } |
305 | | |
306 | | /** |
307 | | * @brief Returns 1 if the crypto library error log dump on exit is enabled. |
308 | | * @details Returns 1 if the crypto library error log dump on exit is enabled or 0 otherwise (only supported by OpenSSL). |
309 | | */ |
310 | | int |
311 | 0 | xmlSecErrorsPrintCryptoLibraryLogOnExitIsEnabled(void) { |
312 | 0 | return(gXmlSecErrorsPrintCryptoLibraryLogOnExitIsEnabled); |
313 | 0 | } |