]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/journal-remote/microhttpd-util.c
util-lib: split our string related calls from util.[ch] into its own file string...
[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
07630cea
LP
32#include "log.h"
33#include "macro.h"
34#include "string-util.h"
35#include "strv.h"
36#include "util.h"
37#include "microhttpd-util.h"
38
e64690a8 39void microhttpd_logger(void *arg, const char *fmt, va_list ap) {
4dd5da7f 40 char *f;
bcfce235 41
63c372cb 42 f = strjoina("microhttpd: ", fmt);
bcfce235
LP
43
44 DISABLE_WARNING_FORMAT_NONLITERAL;
79008bdd 45 log_internalv(LOG_INFO, 0, NULL, 0, NULL, f, ap);
bcfce235 46 REENABLE_WARNING;
e64690a8 47}
cafc7f91 48
f12be7e8 49
e7216d11
ZJS
50static int mhd_respond_internal(struct MHD_Connection *connection,
51 enum MHD_RequestTerminationCode code,
52 char *buffer,
53 size_t size,
54 enum MHD_ResponseMemoryMode mode) {
f12be7e8 55 struct MHD_Response *response;
e7216d11 56 int r;
f12be7e8
ZJS
57
58 assert(connection);
59
e7216d11 60 response = MHD_create_response_from_buffer(size, buffer, mode);
f12be7e8
ZJS
61 if (!response)
62 return MHD_NO;
63
e7216d11 64 log_debug("Queing response %u: %s", code, buffer);
f12be7e8 65 MHD_add_response_header(response, "Content-Type", "text/plain");
e7216d11 66 r = MHD_queue_response(connection, code, response);
f12be7e8
ZJS
67 MHD_destroy_response(response);
68
e7216d11 69 return r;
f12be7e8
ZJS
70}
71
e7216d11
ZJS
72int mhd_respond(struct MHD_Connection *connection,
73 enum MHD_RequestTerminationCode code,
74 const char *message) {
75
76 return mhd_respond_internal(connection, code,
77 (char*) message, strlen(message),
78 MHD_RESPMEM_PERSISTENT);
79}
80
81int mhd_respond_oom(struct MHD_Connection *connection) {
82 return mhd_respond(connection, MHD_HTTP_SERVICE_UNAVAILABLE, "Out of memory.\n");
83}
84
85int mhd_respondf(struct MHD_Connection *connection,
86 enum MHD_RequestTerminationCode code,
87 const char *format, ...) {
f12be7e8 88
f12be7e8
ZJS
89 char *m;
90 int r;
91 va_list ap;
92
93 assert(connection);
94 assert(format);
95
96 va_start(ap, format);
97 r = vasprintf(&m, format, ap);
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
cafc7f91
ZJS
106#ifdef HAVE_GNUTLS
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
8201af08
ZJS
304 if (hostname) {
305 *hostname = buf;
306 buf = NULL;
307 }
308
f12be7e8
ZJS
309 r = verify_cert_authorized(session);
310 if (r < 0) {
cc64d017 311 log_warning("Client is not authorized");
e7216d11
ZJS
312 *code = mhd_respond(connection, MHD_HTTP_UNAUTHORIZED,
313 "Client certificate not signed by recognized authority");
f12be7e8
ZJS
314 }
315 return r;
316}
317
318#else
93c0969c 319int check_permissions(struct MHD_Connection *connection, int *code, char **hostname) {
f12be7e8
ZJS
320 return -EPERM;
321}
d357562c
ZJS
322
323int setup_gnutls_logger(char **categories) {
324 if (categories)
325 log_notice("Ignoring specified gnutls logging categories — gnutls not available.");
326 return 0;
327}
cafc7f91 328#endif