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