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