cfengine  3.15.4
About: CFEngine is a configuration management system for configuring and maintaining Unix-like computers (using an own high level policy language). Community version.
  Fossies Dox: cfengine-3.15.4.tar.gz  ("unofficial" and yet experimental doxygen-generated source code documentation)  

server_tls.c
Go to the documentation of this file.
1 /*
2  Copyright 2019 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 */
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  */
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 
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  {
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  */
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  */
218 {
219  assert(conn_info != NULL);
220 
221  assert(SSLSERVERCONTEXT != NULL);
222  assert(PRIVKEY != NULL);
223  assert(PUBKEY != NULL);
224 
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  {
241  "Peer closed TCP connection without sending data!");
242  return false;
243  }
244  else if (got < peek_size)
245  {
247  "Peer sent only %zd bytes! Considering the protocol as Classic",
248  got);
250  }
251  else if (memcmp(&buf[CF_INBAND_OFFSET], "CAUTH", strlen("CAUTH")) == 0)
252  {
254  "Peeked CAUTH in TCP stream, considering the protocol as Classic");
256  }
257  else /* got==peek_size && not "CAUTH" */
258  {
260  "Peeked nothing important in TCP stream, considering the protocol as 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  */
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  {
311  "Client closed connection early! He probably does not trust our key...");
312  return false;
313  }
314 
316  if (ProtocolIsUndefined(protocol))
317  {
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  {
328  "Client advertises disallowed protocol version: %d",
329  protocol);
330  return false;
331  }
332 
333  if (ProtocolIsTooNew(protocol))
334  {
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  {
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  assert(sizeof(word1) >= sizeof(input));
374  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 
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 >= sizeof(s) - len)
425  {
426  Log(LOG_LEVEL_NOTICE, "Sending OK WELCOME message truncated: %s", s);
427  return false;
428  }
429  len += ret;
430  }
431 
432  /* Overwrite the terminating '\0', we don't need it anyway. */
433  s[len] = '\n';
434  len++;
435 
436  ret = TLSSend(conn->conn_info->ssl, s, len);
437  if (ret == -1)
438  {
439  return false;
440  }
441 
442  return true;
443 }
444 
445 /**
446  * @brief Accept a TLS connection and authenticate and identify.
447  *
448  * Doesn't include code for verifying key and lastseen
449  *
450  * @param conn connection state
451  * @param ssl_ctx SSL context to use for the session (or %NULL to use the
452  * default SSLSERVERCONTEXT)
453  *
454  * @see ServerTLSSessionEstablish
455  * @return true for success false otherwise
456  */
458 {
460  {
461  return true;
462  }
463  if (ssl_ctx == NULL)
464  {
465  ssl_ctx = SSLSERVERCONTEXT;
466  }
467  assert(ConnectionInfoSSL(conn->conn_info) == NULL);
468  SSL *ssl = SSL_new(ssl_ctx);
469  if (ssl == NULL)
470  {
471  Log(LOG_LEVEL_ERR, "SSL_new: %s",
472  TLSErrorString(ERR_get_error()));
473  return false;
474  }
475  ConnectionInfoSetSSL(conn->conn_info, ssl);
476 
477  /* Pass conn_info inside the ssl struct for TLSVerifyCallback(). */
478  SSL_set_ex_data(ssl, CONNECTIONINFO_SSL_IDX, conn->conn_info);
479 
480  /* Now we are letting OpenSSL take over the open socket. */
481  SSL_set_fd(ssl, ConnectionInfoSocket(conn->conn_info));
482 
483  int remaining_tries = MAX_ACCEPT_RETRIES;
484  int ret = -1;
485  bool should_retry = true;
486  while ((ret < 0) && should_retry)
487  {
488  ret = SSL_accept(ssl);
489  if (ret < 0)
490  {
491  int code = TLSLogError(ssl, LOG_LEVEL_VERBOSE, "SSL accept failed", ret);
492  should_retry = ((remaining_tries > 0) &&
493  ((code == SSL_ERROR_WANT_READ) || (code == SSL_ERROR_WANT_WRITE)));
494 
495  }
496  if ((ret < 0) && should_retry)
497  {
498  sleep(1);
499  remaining_tries--;
500  }
501  }
502  if (ret <= 0)
503  {
505  "Failed to accept TLS connection", ret);
506  return false;
507  }
508 
509  Log(LOG_LEVEL_VERBOSE, "TLS version negotiated: %8s; Cipher: %s,%s",
510  SSL_get_version(ssl),
511  SSL_get_cipher_name(ssl),
512  SSL_get_cipher_version(ssl));
513 
514  return true;
515 }
516 
517 /**
518  * @brief Accept a TLS connection and authenticate and identify.
519  *
520  * This function uses trustkeys to trust new keys and updates lastseen
521  *
522  * @param conn connection state
523  * @param ssl_ctx SSL context to use for the session (or %NULL to use the
524  * default SSLSERVERCONTEXT)
525  *
526  * @see BasicServerTLSSessionEstablish
527  * @note Various fields in #conn are set, like username and keyhash.
528  * @return true for success false otherwise
529  */
531 {
533  {
534  return true;
535  }
536 
537  bool established = BasicServerTLSSessionEstablish(conn, ssl_ctx);
538  if (!established)
539  {
540  return false;
541  }
542 
543  Log(LOG_LEVEL_VERBOSE, "TLS session established, checking trust...");
544 
545  /* Send/Receive "CFE_v%d" version string, agree on version, receive
546  identity (username) of peer. */
547  char username[sizeof(conn->username)] = "";
548  bool id_success = ServerIdentificationDialog(conn->conn_info,
549  username, sizeof(username));
550  if (!id_success)
551  {
552  return false;
553  }
554 
555  /* We *now* (maybe a bit late) verify the key that the client sent us in
556  * the TLS handshake, since we need the username to do so. TODO in the
557  * future store keys irrelevant of username, so that we can match them
558  * before IDENTIFY. */
559  int ret = TLSVerifyPeer(conn->conn_info, conn->ipaddr, username);
560  if (ret == -1) /* error */
561  {
562  return false;
563  }
564 
565  if (ret == 1) /* trusted key */
566  {
568  "%s: Client is TRUSTED, public key MATCHES stored one.",
570  }
571 
572  if (ret == 0) /* untrusted key */
573  {
574  if ((SERVER_ACCESS.trustkeylist != NULL) &&
576  {
578  "Peer was found in \"trustkeysfrom\" list");
579  Log(LOG_LEVEL_NOTICE, "Trusting new key: %s",
581 
584  }
585  else
586  {
588  "TRUST FAILED, peer presented an untrusted key, dropping connection!");
590  "Add peer to \"trustkeysfrom\" if you really want to start trusting this new key.");
591  return false;
592  }
593  }
594 
595  /* All checks succeeded, set conn->uid (conn->sid for Windows)
596  * according to the received USERNAME identity. */
597  SetConnIdentity(conn, username);
598 
599  /* No CAUTH, SAUTH in non-classic protocol. */
600  conn->user_data_set = true;
601  conn->rsa_auth = true;
602 
605 
606  ServerSendWelcome(conn);
607  return true;
608 }
609 
610 //*******************************************************************
611 // COMMANDS
612 //*******************************************************************
613 
615 {
616  int i;
617  for (i = 0; PROTOCOL_NEW[i] != NULL; i++)
618  {
619  int cmdlen = strlen(PROTOCOL_NEW[i]);
620  if ((strncmp(str, PROTOCOL_NEW[i], cmdlen) == 0) &&
621  (str[cmdlen] == ' ' || str[cmdlen] == '\0'))
622  {
623  return i;
624  }
625  }
626  assert (i == PROTOCOL_COMMAND_BAD);
627  return i;
628 }
629 
630 
631 /**
632  * Currently this function returns false when we want the connection
633  * closed, and true, when we want to proceed further with requests.
634  *
635  * @TODO So we need this function to return more than true/false, because now
636  * we return true even when access is denied! E.g. return -1 for error, 0 on
637  * success, 1 on access denied. It can be an option if connection will close
638  * on denial.
639  */
641 {
642  assert(conn != NULL);
643 
644  /* The CF_BUFEXT extra space is there to ensure we're not *reading* out of
645  * bounds in commands that carry extra binary arguments, like MD5. */
646  char recvbuffer[CF_BUFSIZE + CF_BUFEXT] = { 0 };
647  /* This size is the max we can SendTransaction(). */
648  char sendbuffer[CF_BUFSIZE - CF_INBAND_OFFSET] = { 0 };
649  char filename[CF_BUFSIZE + 1]; /* +1 for appending slash sometimes */
650  ServerFileGetState get_args = { 0 };
651 
652  /* We already encrypt because of the TLS layer, no need to encrypt more. */
653  const int encrypted = 0;
654 
655  /* Legacy stuff only for old protocol. */
656  assert(conn->rsa_auth == true);
657  assert(conn->user_data_set == true);
658 
659  /* Receive up to CF_BUFSIZE - 1 bytes. */
660  const int received = ReceiveTransaction(conn->conn_info,
661  recvbuffer, NULL);
662 
663  if (received == -1)
664  {
665  /* Already Log()ged in case of error. */
666  return false;
667  }
668  if (received > CF_BUFSIZE - 1)
669  {
670  UnexpectedError("Received transaction of size %d", received);
671  return false;
672  }
673 
674  if (strlen(recvbuffer) == 0)
675  {
677  "Got NULL transmission (of size %d)", received);
678  return true;
679  }
680  /* Don't process request if we're signalled to exit. */
681  if (IsPendingTermination())
682  {
683  Log(LOG_LEVEL_VERBOSE, "Server must exit, closing connection");
684  return false;
685  }
686 
687  /* TODO break recvbuffer here: command, param1, param2 etc. */
688 
689  switch (GetCommandNew(recvbuffer))
690  {
692  {
693  const size_t EXEC_len = strlen(PROTOCOL_NEW[PROTOCOL_COMMAND_EXEC]);
694  /* Assert recvbuffer starts with EXEC. */
695  assert(strncmp(PROTOCOL_NEW[PROTOCOL_COMMAND_EXEC],
696  recvbuffer, EXEC_len) == 0);
697 
698  char *args = &recvbuffer[EXEC_len];
699  args += strspn(args, " \t"); /* bypass spaces */
700 
701  Log(LOG_LEVEL_VERBOSE, "%14s %7s %s",
702  "Received:", "EXEC", args);
703 
704  bool b = DoExec2(ctx, conn, args,
705  sendbuffer, sizeof(sendbuffer));
706 
707  /* In the end we might keep the connection open (return true) to be
708  * ready for next requests, but we must always send the TERMINATOR
709  * string so that the client can close the connection at will. */
710  Terminate(conn->conn_info);
711 
712  return b;
713  }
715 
716  snprintf(sendbuffer, sizeof(sendbuffer), "OK: %s", Version());
717  SendTransaction(conn->conn_info, sendbuffer, 0, CF_DONE);
718  return true;
719 
721  {
722  int ret = sscanf(recvbuffer, "GET %d %[^\n]",
723  &(get_args.buf_size), filename);
724 
725  if (ret != 2 ||
726  get_args.buf_size <= 0 || get_args.buf_size > CF_BUFSIZE)
727  {
728  goto protocol_error;
729  }
730 
731  Log(LOG_LEVEL_VERBOSE, "%14s %7s %s",
732  "Received:", "GET", filename);
733 
734  /* TODO batch all the following in one function since it's very
735  * similar in all of GET, OPENDIR and STAT. */
736 
737  size_t zret = ShortcutsExpand(filename, sizeof(filename),
739  conn->ipaddr, conn->revdns,
741  if (zret == (size_t) -1)
742  {
743  goto protocol_error;
744  }
745 
746  zret = PreprocessRequestPath(filename, sizeof(filename));
747  if (zret == (size_t) -1)
748  {
749  RefuseAccess(conn, recvbuffer);
750  return true;
751  }
752 
753  PathRemoveTrailingSlash(filename, strlen(filename));
754 
755  Log(LOG_LEVEL_VERBOSE, "%14s %7s %s",
756  "Translated to:", "GET", filename);
757 
758  if (acl_CheckPath(paths_acl, filename,
759  conn->ipaddr, conn->revdns,
761  == false)
762  {
763  Log(LOG_LEVEL_INFO, "access denied to GET: %s", filename);
764  RefuseAccess(conn, recvbuffer);
765  return true;
766  }
767 
768  memset(sendbuffer, 0, sizeof(sendbuffer));
769 
770  if (get_args.buf_size >= CF_BUFSIZE)
771  {
772  get_args.buf_size = 2048;
773  }
774 
775  /* TODO eliminate! */
776  get_args.conn = conn;
777  get_args.encrypt = false;
778  get_args.replybuff = sendbuffer;
779  get_args.replyfile = filename;
780 
781  CfGetFile(&get_args);
782 
783  return true;
784  }
786  {
787  memset(filename, 0, sizeof(filename));
788  int ret = sscanf(recvbuffer, "OPENDIR %[^\n]", filename);
789  if (ret != 1)
790  {
791  goto protocol_error;
792  }
793 
794  Log(LOG_LEVEL_VERBOSE, "%14s %7s %s",
795  "Received:", "OPENDIR", filename);
796 
797  /* sizeof()-1 because we need one extra byte for
798  appending '/' afterwards. */
799  size_t zret = ShortcutsExpand(filename, sizeof(filename) - 1,
801  conn->ipaddr, conn->revdns,
803  if (zret == (size_t) -1)
804  {
805  goto protocol_error;
806  }
807 
808  zret = PreprocessRequestPath(filename, sizeof(filename) - 1);
809  if (zret == (size_t) -1)
810  {
811  RefuseAccess(conn, recvbuffer);
812  return true;
813  }
814 
815  /* OPENDIR *must* be directory. */
816  PathAppendTrailingSlash(filename, strlen(filename));
817 
818  Log(LOG_LEVEL_VERBOSE, "%14s %7s %s",
819  "Translated to:", "OPENDIR", filename);
820 
821  if (acl_CheckPath(paths_acl, filename,
822  conn->ipaddr, conn->revdns,
824  == false)
825  {
826  Log(LOG_LEVEL_INFO, "access denied to OPENDIR: %s", filename);
827  RefuseAccess(conn, recvbuffer);
828  return true;
829  }
830 
831  CfOpenDirectory(conn, sendbuffer, filename);
832  return true;
833  }
835  {
836  long time_no_see = 0;
837  memset(filename, 0, sizeof(filename));
838  int ret = sscanf(recvbuffer, "SYNCH %ld STAT %[^\n]",
839  &time_no_see, filename);
840 
841  if (ret != 2 || filename[0] == '\0')
842  {
843  goto protocol_error;
844  }
845 
846  time_t tloc = time(NULL);
847  if (tloc == -1)
848  {
849  /* Should never happen. */
850  Log(LOG_LEVEL_ERR, "Couldn't read system clock. (time: %s)", GetErrorStr());
851  SendTransaction(conn->conn_info, "BAD: clocks out of synch", 0, CF_DONE);
852  return true;
853  }
854 
855  time_t trem = (time_t) time_no_see;
856  int drift = (int) (tloc - trem);
857 
858  Log(LOG_LEVEL_VERBOSE, "%14s %7s %s",
859  "Received:", "STAT", filename);
860 
861  /* sizeof()-1 because we need one extra byte for
862  appending '/' afterwards. */
863  size_t zret = ShortcutsExpand(filename, sizeof(filename) - 1,
865  conn->ipaddr, conn->revdns,
867  if (zret == (size_t) -1)
868  {
869  goto protocol_error;
870  }
871 
872  zret = PreprocessRequestPath(filename, sizeof(filename) - 1);
873  if (zret == (size_t) -1)
874  {
875  RefuseAccess(conn, recvbuffer);
876  return true;
877  }
878 
879  if (IsDirReal(filename))
880  {
881  PathAppendTrailingSlash(filename, strlen(filename));
882  }
883  else
884  {
885  PathRemoveTrailingSlash(filename, strlen(filename));
886  }
887 
888  Log(LOG_LEVEL_VERBOSE, "%14s %7s %s",
889  "Translated to:", "STAT", filename);
890 
891  if (acl_CheckPath(paths_acl, filename,
892  conn->ipaddr, conn->revdns,
894  == false)
895  {
896  Log(LOG_LEVEL_INFO, "access denied to STAT: %s", filename);
897  RefuseAccess(conn, recvbuffer);
898  return true;
899  }
900 
901  Log(LOG_LEVEL_DEBUG, "Clocks were off by %ld",
902  (long) tloc - (long) trem);
903 
904  if (DENYBADCLOCKS && (drift * drift > CLOCK_DRIFT * CLOCK_DRIFT))
905  {
906  snprintf(sendbuffer, sizeof(sendbuffer),
907  "BAD: Clocks are too far unsynchronized %ld/%ld",
908  (long) tloc, (long) trem);
909  Log(LOG_LEVEL_INFO, "denybadclocks %s", sendbuffer);
910  SendTransaction(conn->conn_info, sendbuffer, 0, CF_DONE);
911  return true;
912  }
913 
914  StatFile(conn, sendbuffer, filename);
915 
916  return true;
917  }
919  {
920  int ret = sscanf(recvbuffer, "MD5 %[^\n]", filename);
921  if (ret != 1)
922  {
923  goto protocol_error;
924  }
925 
926  Log(LOG_LEVEL_VERBOSE, "%14s %7s %s",
927  "Received:", "MD5", filename);
928 
929  /* TODO batch all the following in one function since it's very
930  * similar in all of GET, OPENDIR and STAT. */
931 
932  size_t zret = ShortcutsExpand(filename, sizeof(filename),
934  conn->ipaddr, conn->revdns,
936  if (zret == (size_t) -1)
937  {
938  goto protocol_error;
939  }
940 
941  zret = PreprocessRequestPath(filename, sizeof(filename));
942  if (zret == (size_t) -1)
943  {
944  RefuseAccess(conn, recvbuffer);
945  return true;
946  }
947 
948  PathRemoveTrailingSlash(filename, strlen(filename));
949 
950  Log(LOG_LEVEL_VERBOSE, "%14s %7s %s",
951  "Translated to:", "MD5", filename);
952 
953  if (acl_CheckPath(paths_acl, filename,
954  conn->ipaddr, conn->revdns,
956  == false)
957  {
958  Log(LOG_LEVEL_INFO, "access denied to file: %s", filename);
959  RefuseAccess(conn, recvbuffer);
960  return true;
961  }
962 
963  assert(CF_DEFAULT_DIGEST_LEN <= EVP_MAX_MD_SIZE);
964  unsigned char digest[EVP_MAX_MD_SIZE + 1];
965 
967  <= sizeof(recvbuffer));
968  memcpy(digest, recvbuffer + strlen(recvbuffer) + CF_SMALL_OFFSET,
970 
971  CompareLocalHash(filename, digest, sendbuffer);
972  SendTransaction(conn->conn_info, sendbuffer, 0, CF_DONE);
973 
974  return true;
975  }
977  {
978  char var[256];
979  int ret = sscanf(recvbuffer, "VAR %255[^\n]", var);
980  if (ret != 1)
981  {
982  goto protocol_error;
983  }
984 
985  /* TODO if this is literals_acl, then when should I check vars_acl? */
986  if (acl_CheckExact(literals_acl, var,
987  conn->ipaddr, conn->revdns,
989  == false)
990  {
991  Log(LOG_LEVEL_INFO, "access denied to variable: %s", var);
992  RefuseAccess(conn, recvbuffer);
993  return true;
994  }
995 
996  GetServerLiteral(ctx, conn, sendbuffer, recvbuffer, encrypted);
997  return true;
998  }
1000  {
1001  char client_regex[256];
1002  int ret = sscanf(recvbuffer, "CONTEXT %255[^\n]", client_regex);
1003  if (ret != 1)
1004  {
1005  goto protocol_error;
1006  }
1007 
1008  Log(LOG_LEVEL_VERBOSE, "%14s %7s %s",
1009  "Received:", "CONTEXT", client_regex);
1010 
1011  /* WARNING: this comes from legacy code and must be killed if we care
1012  * about performance. We should not accept regular expressions from
1013  * the client, but this will break backwards compatibility.
1014  *
1015  * I replicated the code in raw form here to emphasize complexity,
1016  * it's the only *slow* command currently in the protocol. */
1017 
1018  Item *persistent_classes = ListPersistentClasses();
1019  Item *matched_classes = NULL;
1020 
1021  /* For all persistent classes */
1022  for (Item *ip = persistent_classes; ip != NULL; ip = ip->next)
1023  {
1024  const char *class_name = ip->name;
1025 
1026  /* Does this class match the regex the client sent? */
1027  if (StringMatchFull(client_regex, class_name))
1028  {
1029  /* Is this class allowed to be given to the specific
1030  * host, according to the regexes in the ACLs? */
1031  if (acl_CheckRegex(classes_acl, class_name,
1032  conn->ipaddr, conn->revdns,
1034  NULL)
1035  == true)
1036  {
1037  Log(LOG_LEVEL_DEBUG, "Access granted to class: %s",
1038  class_name);
1039  PrependItem(&matched_classes, class_name, NULL);
1040  }
1041  }
1042  }
1043 
1044  if (matched_classes == NULL)
1045  {
1047  "No allowed classes for remoteclassesmatching: %s",
1048  client_regex);
1049  RefuseAccess(conn, recvbuffer);
1050  return true;
1051  }
1052 
1053  ReplyServerContext(conn, encrypted, matched_classes);
1054  return true;
1055  }
1057  {
1058  char query[256], name[128];
1059  int ret1 = sscanf(recvbuffer, "QUERY %255[^\n]", query);
1060  int ret2 = sscanf(recvbuffer, "QUERY %127s", name);
1061  if (ret1 != 1 || ret2 != 1)
1062  {
1063  goto protocol_error;
1064  }
1065 
1066  const char *hostkey = KeyPrintableHash(
1067  ConnectionInfoKey(conn->conn_info));
1068  const bool access_to_query = acl_CheckExact(
1069  query_acl, name, conn->ipaddr, conn->revdns, hostkey);
1070 
1071  if (!access_to_query)
1072  {
1073  Log(LOG_LEVEL_INFO, "access denied to query: %s", query);
1074  RefuseAccess(conn, recvbuffer);
1075  return true;
1076  }
1077 
1078  if (GetServerQuery(conn, recvbuffer, encrypted))
1079  {
1080  return true;
1081  }
1082 
1083  break;
1084  }
1086  {
1088  {
1089  goto protocol_error;
1090  }
1091 
1092  const char *hostkey = KeyPrintableHash(
1093  ConnectionInfoKey(conn->conn_info));
1094  const bool access_to_query_delta = acl_CheckExact(
1095  query_acl, "delta", conn->ipaddr, conn->revdns, hostkey);
1096 
1097  if (!access_to_query_delta)
1098  {
1099  Log(LOG_LEVEL_INFO, "access denied to cookie query: %s", recvbuffer);
1100  RefuseAccess(conn, recvbuffer);
1101  return true;
1102  }
1103 
1104  if (ReturnCookies(conn))
1105  {
1106  return true;
1107  }
1108 
1109  break;
1110  }
1112  /* Server side, handing the collect call off to cf-hub. */
1113 
1114  if (acl_CheckExact(query_acl, "collect_calls",
1115  conn->ipaddr, conn->revdns,
1117  == false)
1118  {
1120  "access denied to Call-Collect, check the ACL for class: collect_calls");
1121  return false;
1122  }
1123 
1124  ReceiveCollectCall(conn);
1125  /* On success that returned true; otherwise, it did all
1126  * relevant Log()ging. Either way, we're no longer busy with
1127  * it and our caller can close the connection: */
1128  return false;
1129 
1130  case PROTOCOL_COMMAND_BAD:
1131 
1132  Log(LOG_LEVEL_WARNING, "Unexpected protocol command: %s", recvbuffer);
1133  }
1134 
1135  /* We should only reach this point if something went really bad, and
1136  * close connection. In all other cases (like access denied) connection
1137  * shouldn't be closed.
1138  */
1139 
1140 protocol_error:
1141  strcpy(sendbuffer, "BAD: Request denied");
1142  SendTransaction(conn->conn_info, sendbuffer, 0, CF_DONE);
1144  "Closing connection due to illegal request: %s", recvbuffer);
1145  return false;
1146 }
bool ReceiveCollectCall(ServerConnectionState *conn)
bool ReturnCookies(ServerConnectionState *conn)
#define CF_SMALL_OFFSET
Definition: cf3.defs.h:107
int CF_DEFAULT_DIGEST_LEN
Definition: cf3globals.c:89
static int input(void)
Definition: cf3lex.c:2154
#define CF_DONE
Definition: cfnet.h:45
#define CF_INBAND_OFFSET
Definition: cfnet.h:51
void ConnectionInfoSetSSL(ConnectionInfo *info, SSL *ssl)
Sets the SSL structure.
SSL * ConnectionInfoSSL(const ConnectionInfo *info)
SSL structure.
const Key * ConnectionInfoKey(const ConnectionInfo *info)
RSA key.
void ConnectionInfoSetProtocolVersion(ConnectionInfo *info, ProtocolVersion version)
Sets the protocol version.
int ConnectionInfoSocket(const ConnectionInfo *info)
Connection socket.
ProtocolVersion ConnectionInfoProtocolVersion(const ConnectionInfo *info)
Protocol Version.
@ CONNECTIONINFO_STATUS_ESTABLISHED
bool SavePublicKey(const char *user, const char *digest, const RSA *key)
Definition: crypto.c:429
#define CF_BUFSIZE
Definition: definitions.h:50
#define CF_MAXVARSIZE
Definition: definitions.h:36
bool IsDirReal(const char *path)
Definition: file_lib.c:314
const char * Version(void)
#define NULL
Definition: getopt1.c:56
bool IsMatchItemIn(const Item *list, const char *item)
Definition: item_lib.c:780
Item * PrependItem(Item **liststart, const char *itemstring, const char *classes)
Definition: item_lib.c:372
const char * KeyPrintableHash(const Key *key)
Printable hash of the key.
Definition: key.c:84
RSA * KeyRSA(const Key *key)
Constant pointer to the key data.
Definition: key.c:70
void LastSaw1(const char *ipaddress, const char *hashstr, LastSeenRole role)
Same as LastSaw() but the digest parameter is the hash as a "SHA=..." string, to avoid converting twi...
Definition: lastseen.c:83
@ LAST_SEEN_ROLE_ACCEPT
Definition: lastseen.h:39
void LogRaw(LogLevel level, const char *prefix, const void *buf, size_t buflen)
Logs binary data in #buf, with unprintable bytes translated to '.'. Message is prefixed with #prefix.
Definition: logging.c:382
const char * GetErrorStr(void)
Definition: logging.c:275
void Log(LogLevel level, const char *fmt,...)
Definition: logging.c:409
@ LOG_LEVEL_ERR
Definition: logging.h:42
@ LOG_LEVEL_NOTICE
Definition: logging.h:44
@ LOG_LEVEL_DEBUG
Definition: logging.h:47
@ LOG_LEVEL_WARNING
Definition: logging.h:43
@ LOG_LEVEL_VERBOSE
Definition: logging.h:46
@ LOG_LEVEL_INFO
Definition: logging.h:45
void * memmem(const void *haystack, size_t haystacklen, const void *needle, size_t needlelen)
Definition: memmem.c:31
#define UnexpectedError(...)
Definition: misc_lib.h:38
int ReceiveTransaction(ConnectionInfo *conn_info, char *buffer, int *more)
Definition: net.c:149
int SendTransaction(ConnectionInfo *conn_info, const char *buffer, int len, char status)
Definition: net.c:61
unsigned int sleep(unsigned int seconds)
ProtocolVersion ParseProtocolVersionNetwork(const char *const s)
static bool ProtocolIsKnown(const ProtocolVersion p)
static bool ProtocolIsTooNew(const ProtocolVersion p)
ProtocolVersion
@ CF_PROTOCOL_CLASSIC
@ CF_PROTOCOL_COOKIE
@ CF_PROTOCOL_UNDEFINED
@ CF_PROTOCOL_TLS
#define CF_PROTOCOL_LATEST
static bool ProtocolIsUndefined(const ProtocolVersion p)
static bool ProtocolIsClassic(const ProtocolVersion p)
bool StringMatchFull(const char *regex, const char *str)
Definition: regex.c:106
bool DENYBADCLOCKS
Definition: server.c:72
ServerAccess SERVER_ACCESS
Definition: server.c:79
#define CLOCK_DRIFT
Definition: server.h:147
bool acl_CheckRegex(const struct acl *acl, const char *req_string, const char *ipaddr, const char *hostname, const char *key, const char *username)
bool acl_CheckPath(const struct acl *acl, const char *reqpath, const char *ipaddr, const char *hostname, const char *key)
bool acl_CheckExact(const struct acl *acl, const char *req_string, const char *ipaddr, const char *hostname, const char *key)
struct acl * classes_acl
Definition: server_access.c:40
struct acl * paths_acl
Definition: server_access.c:39
struct acl * query_acl
Definition: server_access.c:43
struct acl * literals_acl
Definition: server_access.c:42
@ PROTOCOL_COMMAND_CONTEXT
@ PROTOCOL_COMMAND_OPENDIR
@ PROTOCOL_COMMAND_EXEC
@ PROTOCOL_COMMAND_MD5
@ PROTOCOL_COMMAND_VERSION
@ PROTOCOL_COMMAND_GET
@ PROTOCOL_COMMAND_VAR
@ PROTOCOL_COMMAND_CALL_ME_BACK
@ PROTOCOL_COMMAND_BAD
Item * ListPersistentClasses()
Definition: server_common.c:96
int StatFile(ServerConnectionState *conn, char *sendbuffer, char *ofilename)
bool CompareLocalHash(const char *filename, const char digest[EVP_MAX_MD_SIZE+1], char sendbuffer[sizeof("CFD_FALSE")])
void CfGetFile(ServerFileGetState *args)
void SetConnIdentity(ServerConnectionState *conn, const char *username)
bool PathAppendTrailingSlash(char *s, size_t s_len)
bool DoExec2(const EvalContext *ctx, ServerConnectionState *conn, char *exec_args, char *sendbuf, size_t sendbuf_size)
size_t ShortcutsExpand(char *path, size_t path_size, const StringMap *shortcuts, const char *ipaddr, const char *hostname, const char *key)
void Terminate(ConnectionInfo *connection)
void ReplyServerContext(ServerConnectionState *conn, int encrypted, Item *classes)
bool IsUserNameValid(const char *username)
Definition: server_common.c:71
bool PathRemoveTrailingSlash(char *s, size_t s_len)
bool GetServerQuery(ServerConnectionState *conn, char *recvbuffer, int encrypt)
size_t PreprocessRequestPath(char *reqpath, size_t reqpath_size)
void GetServerLiteral(EvalContext *ctx, ServerConnectionState *conn, char *sendbuffer, char *recvbuffer, int encrypted)
int CfOpenDirectory(ServerConnectionState *conn, char *sendbuffer, char *oldDirname)
void RefuseAccess(ServerConnectionState *conn, char *errmesg)
Definition: server_common.c:61
#define CF_BUFEXT
Definition: server_common.h:30
bool BasicServerTLSSessionEstablish(ServerConnectionState *conn, SSL_CTX *ssl_ctx)
Accept a TLS connection and authenticate and identify.
Definition: server_tls.c:457
static SSL_CTX * SSLSERVERCONTEXT
Definition: server_tls.c:48
bool ServerTLSSessionEstablish(ServerConnectionState *conn, SSL_CTX *ssl_ctx)
Accept a TLS connection and authenticate and identify.
Definition: server_tls.c:530
#define MAX_ACCEPT_RETRIES
Definition: server_tls.c:50
void ServerTLSDeInitialize(RSA **priv_key, RSA **pub_key, SSL_CTX **ssl_ctx)
Definition: server_tls.c:173
bool BusyWithNewProtocol(EvalContext *ctx, ServerConnectionState *conn)
Definition: server_tls.c:640
bool ServerTLSInitialize(RSA *priv_key, RSA *pub_key, SSL_CTX **ssl_ctx)
Definition: server_tls.c:59
bool ServerIdentificationDialog(ConnectionInfo *conn_info, char *username, size_t username_size)
Definition: server_tls.c:287
bool ServerTLSPeek(ConnectionInfo *conn_info)
Set the connection type to CLASSIC or TLS.
Definition: server_tls.c:217
ProtocolCommandNew GetCommandNew(char *str)
Definition: server_tls.c:614
bool ServerSendWelcome(const ServerConnectionState *conn)
Definition: server_tls.c:413
ProtocolCommandNew
Definition: server_tls.h:37
@ PROTOCOL_COMMAND_SYNCH
Definition: server_tls.h:41
@ PROTOCOL_COMMAND_QUERY
Definition: server_tls.h:46
@ PROTOCOL_COMMAND_COOKIE
Definition: server_tls.h:48
static const char *const PROTOCOL_NEW[PROTOCOL_COMMAND_BAD+1]
Definition: server_tls.h:52
bool IsPendingTermination(void)
Definition: signals.c:35
ProtocolVersion protocol
ConnectionStatus status
Definition: item_lib.h:33
Item * trustkeylist
Definition: server.h:66
StringMap * path_shortcuts
Definition: server.h:87
char * allowciphers
Definition: server.h:68
char * allowtlsversion
Definition: server.h:69
char username[1024]
Definition: server.h:107
char revdns[1025]
Definition: server.h:99
ConnectionInfo * conn_info
Definition: server.h:94
ServerConnectionState * conn
Definition: server.h:129
char * replyfile
Definition: server.h:133
char * replybuff
Definition: server.h:132
RSA * PRIVKEY
Definition: cf3globals.c:72
RSA * PUBKEY
Definition: tls_client.c:44
int TLSVerifyPeer(ConnectionInfo *conn_info, const char *remoteip, const char *username)
Definition: tls_generic.c:325
int TLSRecvLines(SSL *ssl, char *buf, size_t buf_size)
Repeat receiving until last byte received is ' '.
Definition: tls_generic.c:824
X509 * TLSGenerateCertFromPrivKey(RSA *privkey)
Generate and return a dummy in-memory X509 certificate signed with the private key passed....
Definition: tls_generic.c:447
const char * TLSErrorString(intmax_t errcode)
Definition: tls_generic.c:87
bool TLSSetCipherList(SSL_CTX *ssl_ctx, const char *cipher_list)
Definition: tls_generic.c:987
void TLSSetDefaultOptions(SSL_CTX *ssl_ctx, const char *min_version)
Definition: tls_generic.c:869
bool TLSGenericInitialize()
Definition: tls_generic.c:93
int TLSLogError(SSL *ssl, LogLevel level, const char *prepend, int retcode)
OpenSSL is missing an SSL_reason_error_string() like ERR_reason_error_string(). Provide missing funct...
Definition: tls_generic.c:577
int TLSSend(SSL *ssl, const char *buffer, int length)
Sends the data stored on the buffer using a TLS session.
Definition: tls_generic.c:669
int CONNECTIONINFO_SSL_IDX
Definition: tls_generic.c:82