1 /*
2 Copyright 2021 Northern.tech AS
3
4 This file is part of CFEngine 3 - written and maintained by Northern.tech AS.
5
6 This program is free software; you can redistribute it and/or modify it
7 under the terms of the GNU General Public License as published by the
8 Free Software Foundation; version 3.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
18
19 To the extent this program is licensed as part of the Enterprise
20 versions of CFEngine, the applicable Commercial Open Source License
21 (COSL) may apply to this file if you as a licensee so wish it. See
22 included file COSL.txt.
23 */
24
25
26 #include <server_tls.h>
27 #include <server_common.h>
28 #include <protocol.h> // ParseProtocolVersionNetwork()
29
30 #include <openssl/err.h> /* ERR_get_error */
31
32 #include <crypto.h> /* DecryptString */
33 #include <conversion.h>
34 #include <signals.h>
35 #include <item_lib.h> /* IsMatchItemIn */
36 #include <lastseen.h> /* LastSaw1 */
37 #include <net.h> /* SendTransaction,ReceiveTransaction */
38 #include <tls_generic.h> /* TLSSend */
39 #include <cf-serverd-enterprise-stubs.h>
40 #include <connection_info.h>
41 #include <regex.h> /* StringMatchFull */
42 #include <known_dirs.h>
43 #include <file_lib.h> /* IsDirReal */
44
45 #include "server_access.h" /* access_CheckResource, acl_CheckExact */
46
47
48 static SSL_CTX *SSLSERVERCONTEXT = NULL;
49
50 #define MAX_ACCEPT_RETRIES 5
51
52 /**
53 * @param[in] priv_key private key to use (or %NULL to use the global PRIVKEY)
54 * @param[in] pub_key public key to use (or %NULL to use the global PUBKEY)
55 * @param[out] ssl_ctx place to store the SSL context (or %NULL to use the
56 * global SSL_CTX)
57 * @warning Make sure you've called CryptoInitialize() first!
58 */
ServerTLSInitialize(RSA * priv_key,RSA * pub_key,SSL_CTX ** ssl_ctx)59 bool ServerTLSInitialize(RSA *priv_key, RSA *pub_key, SSL_CTX **ssl_ctx)
60 {
61 int ret;
62
63 if (priv_key == NULL)
64 {
65 /* private key not specified, use the global one */
66 priv_key = PRIVKEY;
67 }
68 if (pub_key == NULL)
69 {
70 /* public key not specified, use the global one */
71 pub_key = PUBKEY;
72 }
73
74 if (priv_key == NULL || pub_key == NULL)
75 {
76 Log(LOG_LEVEL_ERR, "Public/private key pair not loaded,"
77 " please create one using cf-key");
78 return false;
79 }
80
81 if (!TLSGenericInitialize())
82 {
83 return false;
84 }
85
86 if (ssl_ctx == NULL)
87 {
88 ssl_ctx = &SSLSERVERCONTEXT;
89 }
90 assert(*ssl_ctx == NULL);
91 *ssl_ctx = SSL_CTX_new(SSLv23_server_method());
92 if (*ssl_ctx == NULL)
93 {
94 Log(LOG_LEVEL_ERR, "SSL_CTX_new: %s",
95 TLSErrorString(ERR_get_error()));
96 return false;
97 }
98
99 TLSSetDefaultOptions(*ssl_ctx, SERVER_ACCESS.allowtlsversion);
100
101 /*
102 * CFEngine is not a web server so it does not need to support many
103 * ciphers. It only allows a safe but very common subset by default,
104 * extensible via "allowciphers" in body server control. By default
105 * the server side allows:
106 *
107 * AES256-GCM-SHA384: most high-grade RSA-based cipher from TLSv1.2
108 * AES256-SHA: most backwards compatible but high-grade, from SSLv3
109 * TLS_AES_256_GCM_SHA384: most high-grade RSA-based cipher from TLSv1.3
110 *
111 * Client side is using the OpenSSL's defaults by default.
112 */
113 const char *cipher_list = SERVER_ACCESS.allowciphers;
114 if (cipher_list == NULL)
115 {
116 #ifdef SSL_OP_NO_TLSv1_3 /* defined if TLS 1.3 is supported */
117 cipher_list = "AES256-GCM-SHA384:AES256-SHA:TLS_AES_256_GCM_SHA384";
118 #else
119 cipher_list = "AES256-GCM-SHA384:AES256-SHA";
120 #endif
121 }
122
123 if (!TLSSetCipherList(*ssl_ctx, cipher_list))
124 {
125 goto err;
126 }
127
128 /* Create cert into memory and load it into SSL context. */
129 X509 *cert = TLSGenerateCertFromPrivKey(priv_key);
130 if (cert == NULL)
131 {
132 Log(LOG_LEVEL_ERR,
133 "Failed to generate in-memory certificate from private key");
134 goto err;
135 }
136
137 SSL_CTX_use_certificate(*ssl_ctx, cert);
138 X509_free(cert);
139
140 ret = SSL_CTX_use_RSAPrivateKey(*ssl_ctx, priv_key);
141 if (ret != 1)
142 {
143 Log(LOG_LEVEL_ERR, "Failed to use RSA private key: %s",
144 TLSErrorString(ERR_get_error()));
145 goto err;
146 }
147
148 /* Verify cert consistency. */
149 ret = SSL_CTX_check_private_key(*ssl_ctx);
150 if (ret != 1)
151 {
152 Log(LOG_LEVEL_ERR, "Inconsistent key and TLS cert: %s",
153 TLSErrorString(ERR_get_error()));
154 goto err;
155 }
156
157 return true;
158
159 err:
160 SSL_CTX_free(*ssl_ctx);
161 *ssl_ctx = NULL;
162 return false;
163 }
164
165 /**
166 * @param[in,out] priv_key private key to deinitalize (or %NULL to use the
167 * global PRIVKEY)
168 * @param[in,out] pub_key public key to deinitialize (or %NULL to use the
169 * global PUBKEY)
170 * @param[in,out] ssl_ctx the SSL context to deinitialize (or %NULL to use the
171 * global SSL_CTX)
172 */
ServerTLSDeInitialize(RSA ** priv_key,RSA ** pub_key,SSL_CTX ** ssl_ctx)173 void ServerTLSDeInitialize(RSA **priv_key, RSA **pub_key, SSL_CTX **ssl_ctx)
174 {
175 if (priv_key == NULL)
176 {
177 priv_key = &PRIVKEY;
178 }
179 if (pub_key == NULL)
180 {
181 pub_key = &PUBKEY;
182 }
183 if (ssl_ctx == NULL)
184 {
185 ssl_ctx = &SSLSERVERCONTEXT;
186 }
187
188 if (*pub_key)
189 {
190 RSA_free(*pub_key);
191 *pub_key = NULL;
192 }
193
194 if (*priv_key)
195 {
196 RSA_free(*priv_key);
197 *priv_key = NULL;
198 }
199
200 if (*ssl_ctx != NULL)
201 {
202 SSL_CTX_free(*ssl_ctx);
203 *ssl_ctx = NULL;
204 }
205 }
206
207 /**
208 * @brief Set the connection type to CLASSIC or TLS.
209
210 * It is performed by peeking into the TLS connection to read the first bytes,
211 * and if it's a CAUTH protocol command use the old protocol loop, else use
212 * the TLS protocol loop.
213 * This must be the first thing we run on an accepted connection.
214 *
215 * @return true for success, false otherwise.
216 */
ServerTLSPeek(ConnectionInfo * conn_info)217 bool ServerTLSPeek(ConnectionInfo *conn_info)
218 {
219 assert(conn_info != NULL);
220
221 assert(SSLSERVERCONTEXT != NULL);
222 assert(PRIVKEY != NULL);
223 assert(PUBKEY != NULL);
224
225 assert(ConnectionInfoProtocolVersion(conn_info) == CF_PROTOCOL_UNDEFINED);
226
227 const int peek_size = CF_INBAND_OFFSET + sizeof("CAUTH");
228
229 char buf[peek_size];
230 ssize_t got = recv(ConnectionInfoSocket(conn_info), buf, sizeof(buf), MSG_PEEK);
231 assert(got <= peek_size);
232 if (got < 0)
233 {
234 assert(got == -1);
235 Log(LOG_LEVEL_ERR, "TCP receive error: %s", GetErrorStr());
236 return false;
237 }
238 else if (got == 0)
239 {
240 Log(LOG_LEVEL_INFO,
241 "Peer closed TCP connection without sending data!");
242 return false;
243 }
244 else if (got < peek_size)
245 {
246 Log(LOG_LEVEL_INFO,
247 "Peer sent only %zd bytes! Considering the protocol as Classic",
248 got);
249 ConnectionInfoSetProtocolVersion(conn_info, CF_PROTOCOL_CLASSIC);
250 }
251 else if (memcmp(&buf[CF_INBAND_OFFSET], "CAUTH", strlen("CAUTH")) == 0)
252 {
253 Log(LOG_LEVEL_VERBOSE,
254 "Peeked CAUTH in TCP stream, considering the protocol as Classic");
255 ConnectionInfoSetProtocolVersion(conn_info, CF_PROTOCOL_CLASSIC);
256 }
257 else /* got==peek_size && not "CAUTH" */
258 {
259 Log(LOG_LEVEL_VERBOSE,
260 "Peeked nothing important in TCP stream, considering the protocol as TLS");
261 ConnectionInfoSetProtocolVersion(conn_info, CF_PROTOCOL_TLS);
262 }
263 LogRaw(LOG_LEVEL_DEBUG, "Peeked data: ", buf, got);
264
265 return true;
266 }
267
268 /**
269 * 1. Send "CFE_v%d" server hello.
270 * 2. Receive two lines: One "CFE_v%d" with the protocol version the client
271 * wishes to have, and one "IDENTITY USERNAME=blah ..." with identification
272 * information for the client.
273 *
274 * @note For Identification dialog to end successfully, one "OK WELCOME" line
275 * must be sent right after this function, after identity is verified.
276 *
277 * @TODO More protocol identity. E.g.
278 * IDENTITY USERNAME=xxx HOSTNAME=xxx CUSTOMNAME=xxx
279 *
280 * @retval true if protocol version was successfully negotiated and IDENTITY
281 * command was parsed correctly. Identity fields (only #username for
282 * now) have the respective string values, or they are empty if field
283 * was not on IDENTITY line. #conn_info->protocol has been updated
284 * with the negotiated protocol version.
285 * @retval false in case of error.
286 */
ServerIdentificationDialog(ConnectionInfo * conn_info,char * username,size_t username_size)287 bool ServerIdentificationDialog(ConnectionInfo *conn_info,
288 char *username, size_t username_size)
289 {
290 int ret;
291 char input[1024] = "";
292
293 /* Send "CFE_v%d cf-serverd version". */
294 char version_string[CF_MAXVARSIZE];
295 int len = snprintf(version_string, sizeof(version_string),
296 "CFE_v%d cf-serverd %s\n",
297 CF_PROTOCOL_LATEST, VERSION);
298
299 ret = TLSSend(conn_info->ssl, version_string, len);
300 if (ret != len)
301 {
302 Log(LOG_LEVEL_NOTICE, "Connection was hung up!");
303 return false;
304 }
305
306 /* Receive CFE_v%d ... \n IDENTITY USERNAME=... */
307 int input_len = TLSRecvLines(conn_info->ssl, input, sizeof(input));
308 if (input_len <= 0)
309 {
310 Log(LOG_LEVEL_NOTICE,
311 "Client closed connection early! He probably does not trust our key...");
312 return false;
313 }
314
315 const ProtocolVersion protocol = ParseProtocolVersionNetwork(input);
316 if (ProtocolIsUndefined(protocol))
317 {
318 Log(LOG_LEVEL_NOTICE,
319 "Protocol version negotiation failed! Received: %s",
320 input);
321 return false;
322 }
323
324 /* This is already inside TLS code, so TLS is required at this point*/
325 if (ProtocolIsClassic(protocol))
326 {
327 Log(LOG_LEVEL_NOTICE,
328 "Client advertises disallowed protocol version: %d",
329 protocol);
330 return false;
331 }
332
333 if (ProtocolIsTooNew(protocol))
334 {
335 Log(LOG_LEVEL_NOTICE,
336 "Client attempted a protocol version which is too new for us: %d",
337 protocol);
338 return false;
339 }
340
341 assert(ProtocolIsKnown(protocol));
342
343 /* Did we receive 2nd line or do we need to receive again? */
344 const char id_line[] = "\nIDENTITY ";
345 char *line2 = memmem(input, input_len, id_line, strlen(id_line));
346 if (line2 == NULL)
347 {
348 /* Wait for 2nd line to arrive. */
349 input_len = TLSRecvLines(conn_info->ssl, input, sizeof(input));
350 if (input_len <= 0)
351 {
352 Log(LOG_LEVEL_NOTICE,
353 "Client closed connection during identification dialog!");
354 return false;
355 }
356 line2 = input;
357 }
358 else
359 {
360 line2++; /* skip '\n' */
361 }
362
363 /***** Parse all IDENTITY fields from line2 *****/
364
365 char word1[1024], word2[1024];
366 int line2_pos = 0, chars_read = 0;
367
368 /* Reset all identity variables, we'll set them according to fields
369 * on IDENTITY line. For now only "username" setting exists... */
370 username[0] = '\0';
371
372 /* Assert sscanf() is safe to use. */
373 nt_static_assert(sizeof(word1) >= sizeof(input));
374 nt_static_assert(sizeof(word2) >= sizeof(input));
375
376 ret = sscanf(line2, "IDENTITY %[^=]=%s%n", word1, word2, &chars_read);
377 while (ret >= 2)
378 {
379 /* Found USERNAME identity setting */
380 if (strcmp(word1, "USERNAME") == 0)
381 {
382 if ((strlen(word2) < username_size) && (IsUserNameValid(word2) == true))
383 {
384 strcpy(username, word2);
385 }
386 else
387 {
388 Log(LOG_LEVEL_NOTICE, "Received invalid IDENTITY: %s=%s",
389 word1, word2);
390 return false;
391 }
392 Log(LOG_LEVEL_VERBOSE, "Setting IDENTITY: %s=%s",
393 word1, word2);
394 }
395 /* ... else if (strcmp()) for other acceptable IDENTITY parameters. */
396 else
397 {
398 Log(LOG_LEVEL_VERBOSE, "Received unknown IDENTITY parameter: %s=%s",
399 word1, word2);
400 }
401
402 line2_pos += chars_read;
403 ret = sscanf(&line2[line2_pos], " %[^=]=%s%n", word1, word2, &chars_read);
404 }
405
406 /* Version client and server agreed on. */
407 assert(ProtocolIsKnown(protocol));
408 conn_info->protocol = protocol;
409
410 return true;
411 }
412
ServerSendWelcome(const ServerConnectionState * conn)413 bool ServerSendWelcome(const ServerConnectionState *conn)
414 {
415 char s[1024] = "OK WELCOME";
416 size_t len = strlen(s);
417 int ret;
418
419 /* "OK WELCOME" is the important part. The rest is just extra verbosity. */
420 if (conn->username[0] != '\0')
421 {
422 ret = snprintf(&s[len], sizeof(s) - len, " %s=%s",
423 "USERNAME", conn->username);
424 if (ret < 0)
425 {
426 Log(LOG_LEVEL_ERR,
427 "Unexpected failure from snprintf (%d - %s) while "
428 "constructing OK WELCOME message (ServerSendWelcome)",
429 errno, GetErrorStr());
430 return false;
431 }
432 else if ((size_t) ret >= sizeof(s) - len)
433 {
434 Log(LOG_LEVEL_NOTICE, "Sending OK WELCOME message truncated: %s", s);
435 return false;
436 }
437 len += ret;
438 }
439
440 /* Overwrite the terminating '\0', we don't need it anyway. */
441 s[len] = '\n';
442 len++;
443
444 ret = TLSSend(conn->conn_info->ssl, s, len);
445 if (ret == -1)
446 {
447 return false;
448 }
449
450 return true;
451 }
452
453 /**
454 * @brief Accept a TLS connection and authenticate and identify.
455 *
456 * Doesn't include code for verifying key and lastseen
457 *
458 * @param conn connection state
459 * @param ssl_ctx SSL context to use for the session (or %NULL to use the
460 * default SSLSERVERCONTEXT)
461 *
462 * @see ServerTLSSessionEstablish
463 * @return true for success false otherwise
464 */
BasicServerTLSSessionEstablish(ServerConnectionState * conn,SSL_CTX * ssl_ctx)465 bool BasicServerTLSSessionEstablish(ServerConnectionState *conn, SSL_CTX *ssl_ctx)
466 {
467 if (conn->conn_info->status == CONNECTIONINFO_STATUS_ESTABLISHED)
468 {
469 return true;
470 }
471 if (ssl_ctx == NULL)
472 {
473 ssl_ctx = SSLSERVERCONTEXT;
474 }
475 assert(ConnectionInfoSSL(conn->conn_info) == NULL);
476 SSL *ssl = SSL_new(ssl_ctx);
477 if (ssl == NULL)
478 {
479 Log(LOG_LEVEL_ERR, "SSL_new: %s",
480 TLSErrorString(ERR_get_error()));
481 return false;
482 }
483 ConnectionInfoSetSSL(conn->conn_info, ssl);
484
485 /* Pass conn_info inside the ssl struct for TLSVerifyCallback(). */
486 SSL_set_ex_data(ssl, CONNECTIONINFO_SSL_IDX, conn->conn_info);
487
488 /* Now we are letting OpenSSL take over the open socket. */
489 SSL_set_fd(ssl, ConnectionInfoSocket(conn->conn_info));
490
491 int remaining_tries = MAX_ACCEPT_RETRIES;
492 int ret = -1;
493 bool should_retry = true;
494 while ((ret < 0) && should_retry)
495 {
496 ret = SSL_accept(ssl);
497 if (ret < 0)
498 {
499 int code = TLSLogError(ssl, LOG_LEVEL_VERBOSE, "SSL accept failed", ret);
500 should_retry = ((remaining_tries > 0) &&
501 ((code == SSL_ERROR_WANT_READ) || (code == SSL_ERROR_WANT_WRITE)));
502
503 }
504 if ((ret < 0) && should_retry)
505 {
506 sleep(1);
507 remaining_tries--;
508 }
509 }
510 if (ret <= 0)
511 {
512 TLSLogError(ssl, LOG_LEVEL_ERR,
513 "Failed to accept TLS connection", ret);
514 return false;
515 }
516
517 Log(LOG_LEVEL_VERBOSE, "TLS version negotiated: %8s; Cipher: %s,%s",
518 SSL_get_version(ssl),
519 SSL_get_cipher_name(ssl),
520 SSL_get_cipher_version(ssl));
521
522 return true;
523 }
524
525 /**
526 * @brief Accept a TLS connection and authenticate and identify.
527 *
528 * This function uses trustkeys to trust new keys and updates lastseen
529 *
530 * @param conn connection state
531 * @param ssl_ctx SSL context to use for the session (or %NULL to use the
532 * default SSLSERVERCONTEXT)
533 *
534 * @see BasicServerTLSSessionEstablish
535 * @note Various fields in #conn are set, like username and keyhash.
536 * @return true for success false otherwise
537 */
ServerTLSSessionEstablish(ServerConnectionState * conn,SSL_CTX * ssl_ctx)538 bool ServerTLSSessionEstablish(ServerConnectionState *conn, SSL_CTX *ssl_ctx)
539 {
540 if (conn->conn_info->status == CONNECTIONINFO_STATUS_ESTABLISHED)
541 {
542 return true;
543 }
544
545 bool established = BasicServerTLSSessionEstablish(conn, ssl_ctx);
546 if (!established)
547 {
548 return false;
549 }
550
551 Log(LOG_LEVEL_VERBOSE, "TLS session established, checking trust...");
552
553 /* Send/Receive "CFE_v%d" version string, agree on version, receive
554 identity (username) of peer. */
555 char username[sizeof(conn->username)] = "";
556 bool id_success = ServerIdentificationDialog(conn->conn_info,
557 username, sizeof(username));
558 if (!id_success)
559 {
560 return false;
561 }
562
563 /* We *now* (maybe a bit late) verify the key that the client sent us in
564 * the TLS handshake, since we need the username to do so. TODO in the
565 * future store keys irrelevant of username, so that we can match them
566 * before IDENTIFY. */
567 int ret = TLSVerifyPeer(conn->conn_info, conn->ipaddr, username);
568 if (ret == -1) /* error */
569 {
570 return false;
571 }
572
573 if (ret == 1) /* trusted key */
574 {
575 Log(LOG_LEVEL_VERBOSE,
576 "%s: Client is TRUSTED, public key MATCHES stored one.",
577 KeyPrintableHash(ConnectionInfoKey(conn->conn_info)));
578 }
579
580 if (ret == 0) /* untrusted key */
581 {
582 if ((SERVER_ACCESS.trustkeylist != NULL) &&
583 (IsMatchItemIn(SERVER_ACCESS.trustkeylist, conn->ipaddr)))
584 {
585 Log(LOG_LEVEL_VERBOSE,
586 "Peer was found in \"trustkeysfrom\" list");
587 Log(LOG_LEVEL_NOTICE, "Trusting new key: %s",
588 KeyPrintableHash(ConnectionInfoKey(conn->conn_info)));
589
590 SavePublicKey(username, KeyPrintableHash(conn->conn_info->remote_key),
591 KeyRSA(ConnectionInfoKey(conn->conn_info)));
592 }
593 else
594 {
595 Log(LOG_LEVEL_NOTICE,
596 "TRUST FAILED, peer presented an untrusted key, dropping connection!");
597 Log(LOG_LEVEL_VERBOSE,
598 "Add peer to \"trustkeysfrom\" if you really want to start trusting this new key.");
599 return false;
600 }
601 }
602
603 /* All checks succeeded, set conn->uid (conn->sid for Windows)
604 * according to the received USERNAME identity. */
605 SetConnIdentity(conn, username);
606
607 /* No CAUTH, SAUTH in non-classic protocol. */
608 conn->user_data_set = true;
609 conn->rsa_auth = true;
610
611 LastSaw1(conn->ipaddr, KeyPrintableHash(ConnectionInfoKey(conn->conn_info)),
612 LAST_SEEN_ROLE_ACCEPT);
613
614 ServerSendWelcome(conn);
615 return true;
616 }
617
618 //*******************************************************************
619 // COMMANDS
620 //*******************************************************************
621
GetCommandNew(char * str)622 ProtocolCommandNew GetCommandNew(char *str)
623 {
624 int i;
625 for (i = 0; PROTOCOL_NEW[i] != NULL; i++)
626 {
627 int cmdlen = strlen(PROTOCOL_NEW[i]);
628 if ((strncmp(str, PROTOCOL_NEW[i], cmdlen) == 0) &&
629 (str[cmdlen] == ' ' || str[cmdlen] == '\0'))
630 {
631 return i;
632 }
633 }
634 assert (i == PROTOCOL_COMMAND_BAD);
635 return i;
636 }
637
638
639 /**
640 * Currently this function returns false when we want the connection
641 * closed, and true, when we want to proceed further with requests.
642 *
643 * @TODO So we need this function to return more than true/false, because now
644 * we return true even when access is denied! E.g. return -1 for error, 0 on
645 * success, 1 on access denied. It can be an option if connection will close
646 * on denial.
647 */
BusyWithNewProtocol(EvalContext * ctx,ServerConnectionState * conn)648 bool BusyWithNewProtocol(EvalContext *ctx, ServerConnectionState *conn)
649 {
650 assert(conn != NULL);
651
652 /* The CF_BUFEXT extra space is there to ensure we're not *reading* out of
653 * bounds in commands that carry extra binary arguments, like MD5. */
654 char recvbuffer[CF_BUFSIZE + CF_BUFEXT] = { 0 };
655 /* This size is the max we can SendTransaction(). */
656 char sendbuffer[CF_BUFSIZE - CF_INBAND_OFFSET] = { 0 };
657 char filename[CF_BUFSIZE + 1]; /* +1 for appending slash sometimes */
658 ServerFileGetState get_args = { 0 };
659
660 /* We already encrypt because of the TLS layer, no need to encrypt more. */
661 const int encrypted = 0;
662
663 /* Legacy stuff only for old protocol. */
664 assert(conn->rsa_auth == true);
665 assert(conn->user_data_set == true);
666
667 /* Receive up to CF_BUFSIZE - 1 bytes. */
668 const int received = ReceiveTransaction(conn->conn_info,
669 recvbuffer, NULL);
670
671 if (received == -1)
672 {
673 /* Already Log()ged in case of error. */
674 return false;
675 }
676 if (received > CF_BUFSIZE - 1)
677 {
678 UnexpectedError("Received transaction of size %d", received);
679 return false;
680 }
681
682 if (strlen(recvbuffer) == 0)
683 {
684 Log(LOG_LEVEL_WARNING,
685 "Got NULL transmission (of size %d)", received);
686 return true;
687 }
688 /* Don't process request if we're signalled to exit. */
689 if (IsPendingTermination())
690 {
691 Log(LOG_LEVEL_VERBOSE, "Server must exit, closing connection");
692 return false;
693 }
694
695 /* TODO break recvbuffer here: command, param1, param2 etc. */
696
697 switch (GetCommandNew(recvbuffer))
698 {
699 case PROTOCOL_COMMAND_EXEC:
700 {
701 const size_t EXEC_len = strlen(PROTOCOL_NEW[PROTOCOL_COMMAND_EXEC]);
702 /* Assert recvbuffer starts with EXEC. */
703 assert(strncmp(PROTOCOL_NEW[PROTOCOL_COMMAND_EXEC],
704 recvbuffer, EXEC_len) == 0);
705
706 char *args = &recvbuffer[EXEC_len];
707 args += strspn(args, " \t"); /* bypass spaces */
708
709 Log(LOG_LEVEL_VERBOSE, "%14s %7s %s",
710 "Received:", "EXEC", args);
711
712 bool b = DoExec2(ctx, conn, args,
713 sendbuffer, sizeof(sendbuffer));
714
715 /* In the end we might keep the connection open (return true) to be
716 * ready for next requests, but we must always send the TERMINATOR
717 * string so that the client can close the connection at will. */
718 Terminate(conn->conn_info);
719
720 return b;
721 }
722 case PROTOCOL_COMMAND_VERSION:
723
724 snprintf(sendbuffer, sizeof(sendbuffer), "OK: %s", Version());
725 SendTransaction(conn->conn_info, sendbuffer, 0, CF_DONE);
726 return true;
727
728 case PROTOCOL_COMMAND_GET:
729 {
730 int ret = sscanf(recvbuffer, "GET %d %[^\n]",
731 &(get_args.buf_size), filename);
732
733 if (ret != 2 ||
734 get_args.buf_size <= 0 || get_args.buf_size > CF_BUFSIZE)
735 {
736 goto protocol_error;
737 }
738
739 Log(LOG_LEVEL_VERBOSE, "%14s %7s %s",
740 "Received:", "GET", filename);
741
742 /* TODO batch all the following in one function since it's very
743 * similar in all of GET, OPENDIR and STAT. */
744
745 size_t zret = ShortcutsExpand(filename, sizeof(filename),
746 SERVER_ACCESS.path_shortcuts,
747 conn->ipaddr, conn->revdns,
748 KeyPrintableHash(ConnectionInfoKey(conn->conn_info)));
749 if (zret == (size_t) -1)
750 {
751 goto protocol_error;
752 }
753
754 zret = PreprocessRequestPath(filename, sizeof(filename));
755 if (zret == (size_t) -1)
756 {
757 RefuseAccess(conn, recvbuffer);
758 return true;
759 }
760
761 PathRemoveTrailingSlash(filename, strlen(filename));
762
763 Log(LOG_LEVEL_VERBOSE, "%14s %7s %s",
764 "Translated to:", "GET", filename);
765
766 if (acl_CheckPath(paths_acl, filename,
767 conn->ipaddr, conn->revdns,
768 KeyPrintableHash(ConnectionInfoKey(conn->conn_info)))
769 == false)
770 {
771 Log(LOG_LEVEL_INFO, "access denied to GET: %s", filename);
772 RefuseAccess(conn, recvbuffer);
773 return true;
774 }
775
776 memset(sendbuffer, 0, sizeof(sendbuffer));
777
778 if (get_args.buf_size >= CF_BUFSIZE)
779 {
780 get_args.buf_size = 2048;
781 }
782
783 /* TODO eliminate! */
784 get_args.conn = conn;
785 get_args.encrypt = false;
786 get_args.replybuff = sendbuffer;
787 get_args.replyfile = filename;
788
789 CfGetFile(&get_args);
790
791 return true;
792 }
793 case PROTOCOL_COMMAND_OPENDIR:
794 {
795 memset(filename, 0, sizeof(filename));
796 int ret = sscanf(recvbuffer, "OPENDIR %[^\n]", filename);
797 if (ret != 1)
798 {
799 goto protocol_error;
800 }
801
802 Log(LOG_LEVEL_VERBOSE, "%14s %7s %s",
803 "Received:", "OPENDIR", filename);
804
805 /* sizeof()-1 because we need one extra byte for
806 appending '/' afterwards. */
807 size_t zret = ShortcutsExpand(filename, sizeof(filename) - 1,
808 SERVER_ACCESS.path_shortcuts,
809 conn->ipaddr, conn->revdns,
810 KeyPrintableHash(ConnectionInfoKey(conn->conn_info)));
811 if (zret == (size_t) -1)
812 {
813 goto protocol_error;
814 }
815
816 zret = PreprocessRequestPath(filename, sizeof(filename) - 1);
817 if (zret == (size_t) -1)
818 {
819 RefuseAccess(conn, recvbuffer);
820 return true;
821 }
822
823 /* OPENDIR *must* be directory. */
824 PathAppendTrailingSlash(filename, strlen(filename));
825
826 Log(LOG_LEVEL_VERBOSE, "%14s %7s %s",
827 "Translated to:", "OPENDIR", filename);
828
829 if (acl_CheckPath(paths_acl, filename,
830 conn->ipaddr, conn->revdns,
831 KeyPrintableHash(ConnectionInfoKey(conn->conn_info)))
832 == false)
833 {
834 Log(LOG_LEVEL_INFO, "access denied to OPENDIR: %s", filename);
835 RefuseAccess(conn, recvbuffer);
836 return true;
837 }
838
839 CfOpenDirectory(conn, sendbuffer, filename);
840 return true;
841 }
842 case PROTOCOL_COMMAND_SYNCH:
843 {
844 long time_no_see = 0;
845 memset(filename, 0, sizeof(filename));
846 int ret = sscanf(recvbuffer, "SYNCH %ld STAT %[^\n]",
847 &time_no_see, filename);
848
849 if (ret != 2 || filename[0] == '\0')
850 {
851 goto protocol_error;
852 }
853
854 time_t tloc = time(NULL);
855 if (tloc == -1)
856 {
857 /* Should never happen. */
858 Log(LOG_LEVEL_ERR, "Couldn't read system clock. (time: %s)", GetErrorStr());
859 SendTransaction(conn->conn_info, "BAD: clocks out of synch", 0, CF_DONE);
860 return true;
861 }
862
863 time_t trem = (time_t) time_no_see;
864 int drift = (int) (tloc - trem);
865
866 Log(LOG_LEVEL_VERBOSE, "%14s %7s %s",
867 "Received:", "STAT", filename);
868
869 /* sizeof()-1 because we need one extra byte for
870 appending '/' afterwards. */
871 size_t zret = ShortcutsExpand(filename, sizeof(filename) - 1,
872 SERVER_ACCESS.path_shortcuts,
873 conn->ipaddr, conn->revdns,
874 KeyPrintableHash(ConnectionInfoKey(conn->conn_info)));
875 if (zret == (size_t) -1)
876 {
877 goto protocol_error;
878 }
879
880 zret = PreprocessRequestPath(filename, sizeof(filename) - 1);
881 if (zret == (size_t) -1)
882 {
883 RefuseAccess(conn, recvbuffer);
884 return true;
885 }
886
887 if (IsDirReal(filename))
888 {
889 PathAppendTrailingSlash(filename, strlen(filename));
890 }
891 else
892 {
893 PathRemoveTrailingSlash(filename, strlen(filename));
894 }
895
896 Log(LOG_LEVEL_VERBOSE, "%14s %7s %s",
897 "Translated to:", "STAT", filename);
898
899 if (acl_CheckPath(paths_acl, filename,
900 conn->ipaddr, conn->revdns,
901 KeyPrintableHash(ConnectionInfoKey(conn->conn_info)))
902 == false)
903 {
904 Log(LOG_LEVEL_INFO, "access denied to STAT: %s", filename);
905 RefuseAccess(conn, recvbuffer);
906 return true;
907 }
908
909 Log(LOG_LEVEL_DEBUG, "Clocks were off by %ld",
910 (long) tloc - (long) trem);
911
912 if (DENYBADCLOCKS && (drift * drift > CLOCK_DRIFT * CLOCK_DRIFT))
913 {
914 snprintf(sendbuffer, sizeof(sendbuffer),
915 "BAD: Clocks are too far unsynchronized %ld/%ld",
916 (long) tloc, (long) trem);
917 Log(LOG_LEVEL_INFO, "denybadclocks %s", sendbuffer);
918 SendTransaction(conn->conn_info, sendbuffer, 0, CF_DONE);
919 return true;
920 }
921
922 StatFile(conn, sendbuffer, filename);
923
924 return true;
925 }
926 case PROTOCOL_COMMAND_MD5:
927 {
928 int ret = sscanf(recvbuffer, "MD5 %[^\n]", filename);
929 if (ret != 1)
930 {
931 goto protocol_error;
932 }
933
934 Log(LOG_LEVEL_VERBOSE, "%14s %7s %s",
935 "Received:", "MD5", filename);
936
937 /* TODO batch all the following in one function since it's very
938 * similar in all of GET, OPENDIR and STAT. */
939
940 size_t zret = ShortcutsExpand(filename, sizeof(filename),
941 SERVER_ACCESS.path_shortcuts,
942 conn->ipaddr, conn->revdns,
943 KeyPrintableHash(ConnectionInfoKey(conn->conn_info)));
944 if (zret == (size_t) -1)
945 {
946 goto protocol_error;
947 }
948
949 zret = PreprocessRequestPath(filename, sizeof(filename));
950 if (zret == (size_t) -1)
951 {
952 RefuseAccess(conn, recvbuffer);
953 return true;
954 }
955
956 PathRemoveTrailingSlash(filename, strlen(filename));
957
958 Log(LOG_LEVEL_VERBOSE, "%14s %7s %s",
959 "Translated to:", "MD5", filename);
960
961 if (acl_CheckPath(paths_acl, filename,
962 conn->ipaddr, conn->revdns,
963 KeyPrintableHash(ConnectionInfoKey(conn->conn_info)))
964 == false)
965 {
966 Log(LOG_LEVEL_INFO, "access denied to file: %s", filename);
967 RefuseAccess(conn, recvbuffer);
968 return true;
969 }
970
971 assert(CF_DEFAULT_DIGEST_LEN <= EVP_MAX_MD_SIZE);
972 unsigned char digest[EVP_MAX_MD_SIZE + 1];
973
974 assert(CF_BUFSIZE + CF_SMALL_OFFSET + (size_t) CF_DEFAULT_DIGEST_LEN
975 <= sizeof(recvbuffer));
976 memcpy(digest, recvbuffer + strlen(recvbuffer) + CF_SMALL_OFFSET,
977 CF_DEFAULT_DIGEST_LEN);
978
979 CompareLocalHash(filename, digest, sendbuffer);
980 SendTransaction(conn->conn_info, sendbuffer, 0, CF_DONE);
981
982 return true;
983 }
984 case PROTOCOL_COMMAND_VAR:
985 {
986 char var[256];
987 int ret = sscanf(recvbuffer, "VAR %255[^\n]", var);
988 if (ret != 1)
989 {
990 goto protocol_error;
991 }
992
993 /* TODO if this is literals_acl, then when should I check vars_acl? */
994 if (acl_CheckExact(literals_acl, var,
995 conn->ipaddr, conn->revdns,
996 KeyPrintableHash(ConnectionInfoKey(conn->conn_info)))
997 == false)
998 {
999 Log(LOG_LEVEL_INFO, "access denied to variable: %s", var);
1000 RefuseAccess(conn, recvbuffer);
1001 return true;
1002 }
1003
1004 GetServerLiteral(ctx, conn, sendbuffer, recvbuffer, encrypted);
1005 return true;
1006 }
1007 case PROTOCOL_COMMAND_CONTEXT:
1008 {
1009 char client_regex[256];
1010 int ret = sscanf(recvbuffer, "CONTEXT %255[^\n]", client_regex);
1011 if (ret != 1)
1012 {
1013 goto protocol_error;
1014 }
1015
1016 Log(LOG_LEVEL_VERBOSE, "%14s %7s %s",
1017 "Received:", "CONTEXT", client_regex);
1018
1019 /* WARNING: this comes from legacy code and must be killed if we care
1020 * about performance. We should not accept regular expressions from
1021 * the client, but this will break backwards compatibility.
1022 *
1023 * I replicated the code in raw form here to emphasize complexity,
1024 * it's the only *slow* command currently in the protocol. */
1025
1026 Item *persistent_classes = ListPersistentClasses();
1027 Item *matched_classes = NULL;
1028
1029 /* For all persistent classes */
1030 for (Item *ip = persistent_classes; ip != NULL; ip = ip->next)
1031 {
1032 const char *class_name = ip->name;
1033
1034 /* Does this class match the regex the client sent? */
1035 if (StringMatchFull(client_regex, class_name))
1036 {
1037 /* Is this class allowed to be given to the specific
1038 * host, according to the regexes in the ACLs? */
1039 if (acl_CheckRegex(classes_acl, class_name,
1040 conn->ipaddr, conn->revdns,
1041 KeyPrintableHash(ConnectionInfoKey(conn->conn_info)),
1042 NULL)
1043 == true)
1044 {
1045 Log(LOG_LEVEL_DEBUG, "Access granted to class: %s",
1046 class_name);
1047 PrependItem(&matched_classes, class_name, NULL);
1048 }
1049 }
1050 }
1051
1052 if (matched_classes == NULL)
1053 {
1054 Log(LOG_LEVEL_INFO,
1055 "No allowed classes for remoteclassesmatching: %s",
1056 client_regex);
1057 RefuseAccess(conn, recvbuffer);
1058 return true;
1059 }
1060
1061 ReplyServerContext(conn, encrypted, matched_classes);
1062 return true;
1063 }
1064 case PROTOCOL_COMMAND_QUERY:
1065 {
1066 char query[256], name[128];
1067 int ret1 = sscanf(recvbuffer, "QUERY %255[^\n]", query);
1068 int ret2 = sscanf(recvbuffer, "QUERY %127s", name);
1069 if (ret1 != 1 || ret2 != 1)
1070 {
1071 goto protocol_error;
1072 }
1073
1074 const char *hostkey = KeyPrintableHash(
1075 ConnectionInfoKey(conn->conn_info));
1076 const bool access_to_query = acl_CheckExact(
1077 query_acl, name, conn->ipaddr, conn->revdns, hostkey);
1078
1079 if (!access_to_query)
1080 {
1081 Log(LOG_LEVEL_INFO, "access denied to query: %s", query);
1082 RefuseAccess(conn, recvbuffer);
1083 return true;
1084 }
1085
1086 if (GetServerQuery(conn, recvbuffer, encrypted))
1087 {
1088 return true;
1089 }
1090
1091 break;
1092 }
1093 case PROTOCOL_COMMAND_COOKIE:
1094 {
1095 if (ConnectionInfoProtocolVersion(conn->conn_info) < CF_PROTOCOL_COOKIE)
1096 {
1097 goto protocol_error;
1098 }
1099
1100 const char *hostkey = KeyPrintableHash(
1101 ConnectionInfoKey(conn->conn_info));
1102 const bool access_to_query_delta = acl_CheckExact(
1103 query_acl, "delta", conn->ipaddr, conn->revdns, hostkey);
1104
1105 if (!access_to_query_delta)
1106 {
1107 Log(LOG_LEVEL_INFO, "access denied to cookie query: %s", recvbuffer);
1108 RefuseAccess(conn, recvbuffer);
1109 return true;
1110 }
1111
1112 if (ReturnCookies(conn))
1113 {
1114 return true;
1115 }
1116
1117 break;
1118 }
1119 case PROTOCOL_COMMAND_CALL_ME_BACK:
1120 /* Server side, handing the collect call off to cf-hub. */
1121
1122 if (acl_CheckExact(query_acl, "collect_calls",
1123 conn->ipaddr, conn->revdns,
1124 KeyPrintableHash(ConnectionInfoKey(conn->conn_info)))
1125 == false)
1126 {
1127 Log(LOG_LEVEL_INFO,
1128 "access denied to Call-Collect, check the ACL for class: collect_calls");
1129 return false;
1130 }
1131
1132 ReceiveCollectCall(conn);
1133 /* On success that returned true; otherwise, it did all
1134 * relevant Log()ging. Either way, we're no longer busy with
1135 * it and our caller can close the connection: */
1136 return false;
1137
1138 case PROTOCOL_COMMAND_BAD:
1139
1140 Log(LOG_LEVEL_WARNING, "Unexpected protocol command: %s", recvbuffer);
1141 }
1142
1143 /* We should only reach this point if something went really bad, and
1144 * close connection. In all other cases (like access denied) connection
1145 * shouldn't be closed.
1146 */
1147
1148 protocol_error:
1149 strcpy(sendbuffer, "BAD: Request denied");
1150 SendTransaction(conn->conn_info, sendbuffer, 0, CF_DONE);
1151 Log(LOG_LEVEL_INFO,
1152 "Closing connection due to illegal request: %s", recvbuffer);
1153 return false;
1154 }
1155