]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/journal/microhttpd-util.c
journal/compress: return early in uncompress_startswith
[thirdparty/systemd.git] / src / journal / microhttpd-util.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2012 Lennart Poettering
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>
25 #include <string.h>
26
27 #include "microhttpd-util.h"
28 #include "log.h"
29 #include "macro.h"
30 #include "util.h"
31
32 #ifdef HAVE_GNUTLS
33 #include <gnutls/gnutls.h>
34 #include <gnutls/x509.h>
35 #endif
36
37 void microhttpd_logger(void *arg, const char *fmt, va_list ap) {
38 char *f;
39
40 f = strappenda("microhttpd: ", fmt);
41
42 DISABLE_WARNING_FORMAT_NONLITERAL;
43 log_metav(LOG_INFO, NULL, 0, NULL, f, ap);
44 REENABLE_WARNING;
45 }
46
47
48 static int mhd_respond_internal(struct MHD_Connection *connection,
49 enum MHD_RequestTerminationCode code,
50 char *buffer,
51 size_t size,
52 enum MHD_ResponseMemoryMode mode) {
53 struct MHD_Response *response;
54 int r;
55
56 assert(connection);
57
58 response = MHD_create_response_from_buffer(size, buffer, mode);
59 if (!response)
60 return MHD_NO;
61
62 log_debug("Queing response %u: %s", code, buffer);
63 MHD_add_response_header(response, "Content-Type", "text/plain");
64 r = MHD_queue_response(connection, code, response);
65 MHD_destroy_response(response);
66
67 return r;
68 }
69
70 int mhd_respond(struct MHD_Connection *connection,
71 enum MHD_RequestTerminationCode code,
72 const char *message) {
73
74 return mhd_respond_internal(connection, code,
75 (char*) message, strlen(message),
76 MHD_RESPMEM_PERSISTENT);
77 }
78
79 int mhd_respond_oom(struct MHD_Connection *connection) {
80 return mhd_respond(connection, MHD_HTTP_SERVICE_UNAVAILABLE, "Out of memory.\n");
81 }
82
83 int mhd_respondf(struct MHD_Connection *connection,
84 enum MHD_RequestTerminationCode code,
85 const char *format, ...) {
86
87 char *m;
88 int r;
89 va_list ap;
90
91 assert(connection);
92 assert(format);
93
94 va_start(ap, format);
95 r = vasprintf(&m, format, ap);
96 va_end(ap);
97
98 if (r < 0)
99 return respond_oom(connection);
100
101 return mhd_respond_internal(connection, code, m, r, MHD_RESPMEM_MUST_FREE);
102 }
103
104 #ifdef HAVE_GNUTLS
105
106 static int log_level_map[] = {
107 LOG_DEBUG,
108 LOG_WARNING, /* gnutls session audit */
109 LOG_DEBUG, /* gnutls debug log */
110 LOG_WARNING, /* gnutls assert log */
111 LOG_INFO, /* gnutls handshake log */
112 LOG_DEBUG, /* gnutls record log */
113 LOG_DEBUG, /* gnutls dtls log */
114 LOG_DEBUG,
115 LOG_DEBUG,
116 LOG_DEBUG,
117 LOG_DEBUG, /* gnutls hard log */
118 LOG_DEBUG, /* gnutls read log */
119 LOG_DEBUG, /* gnutls write log */
120 LOG_DEBUG, /* gnutls io log */
121 LOG_DEBUG, /* gnutls buffers log */
122 };
123
124 void log_func_gnutls(int level, const char *message) {
125 int ourlevel;
126
127 assert_se(message);
128
129 if (0 <= level && level < (int) ELEMENTSOF(log_level_map))
130 ourlevel = log_level_map[level];
131 else
132 ourlevel = LOG_DEBUG;
133
134 log_meta(ourlevel, NULL, 0, NULL, "gnutls: %s", message);
135 }
136
137 static int verify_cert_authorized(gnutls_session_t session) {
138 unsigned status;
139 gnutls_certificate_type_t type;
140 gnutls_datum_t out;
141 int r;
142
143 r = gnutls_certificate_verify_peers2(session, &status);
144 if (r < 0) {
145 log_error("gnutls_certificate_verify_peers2 failed: %s", strerror(-r));
146 return r;
147 }
148
149 type = gnutls_certificate_type_get(session);
150 r = gnutls_certificate_verification_status_print(status, type, &out, 0);
151 if (r < 0) {
152 log_error("gnutls_certificate_verification_status_print failed: %s", strerror(-r));
153 return r;
154 }
155
156 log_info("Certificate status: %s", out.data);
157
158 return status == 0 ? 0 : -EPERM;
159 }
160
161 static int get_client_cert(gnutls_session_t session, gnutls_x509_crt_t *client_cert) {
162 const gnutls_datum_t *pcert;
163 unsigned listsize;
164 gnutls_x509_crt_t cert;
165 int r;
166
167 assert(session);
168 assert(client_cert);
169
170 pcert = gnutls_certificate_get_peers(session, &listsize);
171 if (!pcert || !listsize) {
172 log_error("Failed to retrieve certificate chain");
173 return -EINVAL;
174 }
175
176 r = gnutls_x509_crt_init(&cert);
177 if (r < 0) {
178 log_error("Failed to initialize client certificate");
179 return r;
180 }
181
182 /* Note that by passing values between 0 and listsize here, you
183 can get access to the CA's certs */
184 r = gnutls_x509_crt_import(cert, &pcert[0], GNUTLS_X509_FMT_DER);
185 if (r < 0) {
186 log_error("Failed to import client certificate");
187 gnutls_x509_crt_deinit(cert);
188 return r;
189 }
190
191 *client_cert = cert;
192 return 0;
193 }
194
195 static int get_auth_dn(gnutls_x509_crt_t client_cert, char **buf) {
196 size_t len = 0;
197 int r;
198
199 assert(buf);
200 assert(*buf == NULL);
201
202 r = gnutls_x509_crt_get_dn(client_cert, NULL, &len);
203 if (r != GNUTLS_E_SHORT_MEMORY_BUFFER) {
204 log_error("gnutls_x509_crt_get_dn failed");
205 return r;
206 }
207
208 *buf = malloc(len);
209 if (!*buf)
210 return log_oom();
211
212 gnutls_x509_crt_get_dn(client_cert, *buf, &len);
213 return 0;
214 }
215
216 int check_permissions(struct MHD_Connection *connection, int *code) {
217 const union MHD_ConnectionInfo *ci;
218 gnutls_session_t session;
219 gnutls_x509_crt_t client_cert;
220 _cleanup_free_ char *buf = NULL;
221 int r;
222
223 assert(connection);
224 assert(code);
225
226 *code = 0;
227
228 ci = MHD_get_connection_info(connection,
229 MHD_CONNECTION_INFO_GNUTLS_SESSION);
230 if (!ci) {
231 log_error("MHD_get_connection_info failed: session is unencrypted");
232 *code = mhd_respond(connection, MHD_HTTP_FORBIDDEN,
233 "Encrypted connection is required");
234 return -EPERM;
235 }
236 session = ci->tls_session;
237 assert(session);
238
239 r = get_client_cert(session, &client_cert);
240 if (r < 0) {
241 *code = mhd_respond(connection, MHD_HTTP_UNAUTHORIZED,
242 "Authorization through certificate is required");
243 return -EPERM;
244 }
245
246 r = get_auth_dn(client_cert, &buf);
247 if (r < 0) {
248 *code = mhd_respond(connection, MHD_HTTP_UNAUTHORIZED,
249 "Failed to determine distinguished name from certificate");
250 return -EPERM;
251 }
252
253 log_info("Connection from %s", buf);
254
255 r = verify_cert_authorized(session);
256 if (r < 0) {
257 log_warning("Client is not authorized");
258 *code = mhd_respond(connection, MHD_HTTP_UNAUTHORIZED,
259 "Client certificate not signed by recognized authority");
260 }
261 return r;
262 }
263
264 #else
265 int check_permissions(struct MHD_Connection *connection, int *code) {
266 return -EPERM;
267 }
268 #endif