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