]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/journal-remote/microhttpd-util.c
Merge pull request #11827 from keszybz/pkgconfig-variables
[thirdparty/systemd.git] / src / journal-remote / microhttpd-util.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
e64690a8
ZJS
2
3#include <stddef.h>
4#include <stdio.h>
f12be7e8 5#include <string.h>
e64690a8 6
349cc4a5 7#if HAVE_GNUTLS
f12be7e8
ZJS
8#include <gnutls/gnutls.h>
9#include <gnutls/x509.h>
10#endif
11
b5efdb8a 12#include "alloc-util.h"
07630cea
LP
13#include "log.h"
14#include "macro.h"
cf0fbc49 15#include "microhttpd-util.h"
07630cea
LP
16#include "string-util.h"
17#include "strv.h"
18#include "util.h"
07630cea 19
e64690a8 20void microhttpd_logger(void *arg, const char *fmt, va_list ap) {
4dd5da7f 21 char *f;
bcfce235 22
63c372cb 23 f = strjoina("microhttpd: ", fmt);
bcfce235
LP
24
25 DISABLE_WARNING_FORMAT_NONLITERAL;
79008bdd 26 log_internalv(LOG_INFO, 0, NULL, 0, NULL, f, ap);
bcfce235 27 REENABLE_WARNING;
e64690a8 28}
cafc7f91 29
e7216d11
ZJS
30static int mhd_respond_internal(struct MHD_Connection *connection,
31 enum MHD_RequestTerminationCode code,
f5e757f1 32 const char *buffer,
e7216d11
ZJS
33 size_t size,
34 enum MHD_ResponseMemoryMode mode) {
f12be7e8
ZJS
35 assert(connection);
36
d101fb24
ZJS
37 _cleanup_(MHD_destroy_responsep) struct MHD_Response *response
38 = MHD_create_response_from_buffer(size, (char*) buffer, mode);
f12be7e8
ZJS
39 if (!response)
40 return MHD_NO;
41
595bfe7d 42 log_debug("Queueing response %u: %s", code, buffer);
f12be7e8 43 MHD_add_response_header(response, "Content-Type", "text/plain");
d101fb24 44 return MHD_queue_response(connection, code, response);
f12be7e8
ZJS
45}
46
e7216d11
ZJS
47int mhd_respond(struct MHD_Connection *connection,
48 enum MHD_RequestTerminationCode code,
49 const char *message) {
50
f5e757f1
ZJS
51 const char *fmt;
52
53 fmt = strjoina(message, "\n");
54
e7216d11 55 return mhd_respond_internal(connection, code,
f5e757f1 56 fmt, strlen(message) + 1,
e7216d11
ZJS
57 MHD_RESPMEM_PERSISTENT);
58}
59
60int mhd_respond_oom(struct MHD_Connection *connection) {
f5e757f1 61 return mhd_respond(connection, MHD_HTTP_SERVICE_UNAVAILABLE, "Out of memory.");
e7216d11
ZJS
62}
63
64int mhd_respondf(struct MHD_Connection *connection,
1b4cd646 65 int error,
e7216d11
ZJS
66 enum MHD_RequestTerminationCode code,
67 const char *format, ...) {
f12be7e8 68
f5e757f1 69 const char *fmt;
f12be7e8
ZJS
70 char *m;
71 int r;
72 va_list ap;
73
74 assert(connection);
75 assert(format);
76
1b4cd646
ZJS
77 if (error < 0)
78 error = -error;
79 errno = -error;
f5e757f1 80 fmt = strjoina(format, "\n");
f12be7e8 81 va_start(ap, format);
c8304ba9
ZJS
82#pragma GCC diagnostic push
83#pragma GCC diagnostic ignored "-Wformat-nonliteral"
f5e757f1 84 r = vasprintf(&m, fmt, ap);
c8304ba9 85#pragma GCC diagnostic pop
f12be7e8
ZJS
86 va_end(ap);
87
88 if (r < 0)
89 return respond_oom(connection);
90
4dd5da7f 91 return mhd_respond_internal(connection, code, m, r, MHD_RESPMEM_MUST_FREE);
f12be7e8
ZJS
92}
93
349cc4a5 94#if HAVE_GNUTLS
cafc7f91 95
5937a4d4
ZJS
96static struct {
97 const char *const names[4];
98 int level;
99 bool enabled;
100} gnutls_log_map[] = {
101 { {"0"}, LOG_DEBUG },
102 { {"1", "audit"}, LOG_WARNING, true}, /* gnutls session audit */
103 { {"2", "assert"}, LOG_DEBUG }, /* gnutls assert log */
104 { {"3", "hsk", "ext"}, LOG_DEBUG }, /* gnutls handshake log */
105 { {"4", "rec"}, LOG_DEBUG }, /* gnutls record log */
106 { {"5", "dtls"}, LOG_DEBUG }, /* gnutls DTLS log */
107 { {"6", "buf"}, LOG_DEBUG },
108 { {"7", "write", "read"}, LOG_DEBUG },
109 { {"8"}, LOG_DEBUG },
110 { {"9", "enc", "int"}, LOG_DEBUG },
cafc7f91
ZJS
111};
112
d357562c 113static void log_func_gnutls(int level, const char *message) {
cafc7f91
ZJS
114 assert_se(message);
115
5937a4d4
ZJS
116 if (0 <= level && level < (int) ELEMENTSOF(gnutls_log_map)) {
117 if (gnutls_log_map[level].enabled)
79008bdd 118 log_internal(gnutls_log_map[level].level, 0, NULL, 0, NULL, "gnutls %d/%s: %s", level, gnutls_log_map[level].names[1], message);
5937a4d4
ZJS
119 } else {
120 log_debug("Received GNUTLS message with unknown level %d.", level);
79008bdd 121 log_internal(LOG_DEBUG, 0, NULL, 0, NULL, "gnutls: %s", message);
5937a4d4
ZJS
122 }
123}
124
d357562c
ZJS
125static void log_reset_gnutls_level(void) {
126 int i;
127
128 for (i = ELEMENTSOF(gnutls_log_map) - 1; i >= 0; i--)
129 if (gnutls_log_map[i].enabled) {
130 log_debug("Setting gnutls log level to %d", i);
131 gnutls_global_set_log_level(i);
132 break;
133 }
134}
135
136static int log_enable_gnutls_category(const char *cat) {
5937a4d4
ZJS
137 unsigned i;
138
139 if (streq(cat, "all")) {
140 for (i = 0; i < ELEMENTSOF(gnutls_log_map); i++)
141 gnutls_log_map[i].enabled = true;
142 log_reset_gnutls_level();
143 return 0;
144 } else
145 for (i = 0; i < ELEMENTSOF(gnutls_log_map); i++)
146 if (strv_contains((char**)gnutls_log_map[i].names, cat)) {
147 gnutls_log_map[i].enabled = true;
148 log_reset_gnutls_level();
149 return 0;
150 }
baaa35ad 151 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No such log category: %s", cat);
5937a4d4
ZJS
152}
153
d357562c
ZJS
154int setup_gnutls_logger(char **categories) {
155 char **cat;
156 int r;
cafc7f91 157
d357562c
ZJS
158 gnutls_global_set_log_function(log_func_gnutls);
159
160 if (categories) {
161 STRV_FOREACH(cat, categories) {
162 r = log_enable_gnutls_category(*cat);
163 if (r < 0)
164 return r;
5937a4d4 165 }
d357562c
ZJS
166 } else
167 log_reset_gnutls_level();
168
169 return 0;
cafc7f91
ZJS
170}
171
f12be7e8
ZJS
172static int verify_cert_authorized(gnutls_session_t session) {
173 unsigned status;
174 gnutls_certificate_type_t type;
175 gnutls_datum_t out;
176 int r;
177
178 r = gnutls_certificate_verify_peers2(session, &status);
23bbb0de
MS
179 if (r < 0)
180 return log_error_errno(r, "gnutls_certificate_verify_peers2 failed: %m");
f12be7e8
ZJS
181
182 type = gnutls_certificate_type_get(session);
183 r = gnutls_certificate_verification_status_print(status, type, &out, 0);
23bbb0de
MS
184 if (r < 0)
185 return log_error_errno(r, "gnutls_certificate_verification_status_print failed: %m");
f12be7e8 186
0e72da6f 187 log_debug("Certificate status: %s", out.data);
9c3cf969 188 gnutls_free(out.data);
f12be7e8
ZJS
189
190 return status == 0 ? 0 : -EPERM;
191}
192
193static int get_client_cert(gnutls_session_t session, gnutls_x509_crt_t *client_cert) {
194 const gnutls_datum_t *pcert;
195 unsigned listsize;
196 gnutls_x509_crt_t cert;
197 int r;
198
199 assert(session);
200 assert(client_cert);
201
202 pcert = gnutls_certificate_get_peers(session, &listsize);
baaa35ad
ZJS
203 if (!pcert || !listsize)
204 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
205 "Failed to retrieve certificate chain");
f12be7e8
ZJS
206
207 r = gnutls_x509_crt_init(&cert);
208 if (r < 0) {
209 log_error("Failed to initialize client certificate");
210 return r;
211 }
212
213 /* Note that by passing values between 0 and listsize here, you
214 can get access to the CA's certs */
215 r = gnutls_x509_crt_import(cert, &pcert[0], GNUTLS_X509_FMT_DER);
216 if (r < 0) {
217 log_error("Failed to import client certificate");
218 gnutls_x509_crt_deinit(cert);
219 return r;
220 }
221
222 *client_cert = cert;
223 return 0;
224}
225
226static int get_auth_dn(gnutls_x509_crt_t client_cert, char **buf) {
227 size_t len = 0;
228 int r;
229
230 assert(buf);
231 assert(*buf == NULL);
232
233 r = gnutls_x509_crt_get_dn(client_cert, NULL, &len);
234 if (r != GNUTLS_E_SHORT_MEMORY_BUFFER) {
235 log_error("gnutls_x509_crt_get_dn failed");
236 return r;
237 }
238
239 *buf = malloc(len);
240 if (!*buf)
241 return log_oom();
242
243 gnutls_x509_crt_get_dn(client_cert, *buf, &len);
244 return 0;
245}
246
a1e92eee 247static void gnutls_x509_crt_deinitp(gnutls_x509_crt_t *p) {
32c3d714
MS
248 gnutls_x509_crt_deinit(*p);
249}
250
8201af08 251int check_permissions(struct MHD_Connection *connection, int *code, char **hostname) {
f12be7e8
ZJS
252 const union MHD_ConnectionInfo *ci;
253 gnutls_session_t session;
32c3d714 254 _cleanup_(gnutls_x509_crt_deinitp) gnutls_x509_crt_t client_cert = NULL;
c8b32e11 255 _cleanup_free_ char *buf = NULL;
f12be7e8
ZJS
256 int r;
257
258 assert(connection);
259 assert(code);
260
261 *code = 0;
262
263 ci = MHD_get_connection_info(connection,
264 MHD_CONNECTION_INFO_GNUTLS_SESSION);
265 if (!ci) {
cc64d017 266 log_error("MHD_get_connection_info failed: session is unencrypted");
e7216d11
ZJS
267 *code = mhd_respond(connection, MHD_HTTP_FORBIDDEN,
268 "Encrypted connection is required");
cc64d017 269 return -EPERM;
f12be7e8
ZJS
270 }
271 session = ci->tls_session;
272 assert(session);
273
274 r = get_client_cert(session, &client_cert);
275 if (r < 0) {
e7216d11
ZJS
276 *code = mhd_respond(connection, MHD_HTTP_UNAUTHORIZED,
277 "Authorization through certificate is required");
f12be7e8
ZJS
278 return -EPERM;
279 }
280
281 r = get_auth_dn(client_cert, &buf);
282 if (r < 0) {
e7216d11
ZJS
283 *code = mhd_respond(connection, MHD_HTTP_UNAUTHORIZED,
284 "Failed to determine distinguished name from certificate");
f12be7e8
ZJS
285 return -EPERM;
286 }
287
0e72da6f 288 log_debug("Connection from %s", buf);
f12be7e8 289
1cc6c93a
YW
290 if (hostname)
291 *hostname = TAKE_PTR(buf);
8201af08 292
f12be7e8
ZJS
293 r = verify_cert_authorized(session);
294 if (r < 0) {
cc64d017 295 log_warning("Client is not authorized");
e7216d11
ZJS
296 *code = mhd_respond(connection, MHD_HTTP_UNAUTHORIZED,
297 "Client certificate not signed by recognized authority");
f12be7e8
ZJS
298 }
299 return r;
300}
301
302#else
93c0969c 303int check_permissions(struct MHD_Connection *connection, int *code, char **hostname) {
f12be7e8
ZJS
304 return -EPERM;
305}
d357562c
ZJS
306
307int setup_gnutls_logger(char **categories) {
308 if (categories)
309 log_notice("Ignoring specified gnutls logging categories — gnutls not available.");
310 return 0;
311}
cafc7f91 312#endif