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.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 #include <server.h>
26 
27 #include <item_lib.h>
28 #include <crypto.h>
29 #include <hash.h>
30 #include <eval_context.h>
31 #include <lastseen.h>
32 #include <conversion.h>
33 #include <string_lib.h>
34 #include <signals.h>
35 #include <mutex.h>
36 #include <global_mutex.h>
37 #include <net.h> /* SendTransaction,ReceiveTransaction */
38 #include <tls_generic.h> /* TLSVerifyPeer */
39 #include <rlist.h>
40 #include <misc_lib.h>
42 #include <audit.h>
43 #include <cfnet.h>
44 #include <protocol.h> /* ProtocolIsTLS() */
45 #include <server_tls.h> /* ServerTLS* */
46 #include <server_common.h>
47 #include <connection_info.h>
48 #include <cf-windows-functions.h>
49 #include <logging_priv.h> /* LoggingPrivSetContext */
50 #include <printsize.h>
51 
52 #include "server_classic.h" /* BusyWithClassicConnection */
53 
54 
55 /*
56  The only exported function in this file is the following, used only in
57  cf-serverd-functions.c.
58 
59  void ServerEntryPoint(EvalContext *ctx, const char *ipaddr, ConnectionInfo *info);
60 
61  TODO move this file to cf-serverd-functions.c or most probably server_common.c.
62 */
63 
64 
65 //******************************************************************
66 // GLOBAL STATE
67 //******************************************************************
68 
69 int ACTIVE_THREADS = 0; /* GLOBAL_X */
70 
71 int CFD_MAXPROCESSES = 0; /* GLOBAL_P */
72 bool DENYBADCLOCKS = true; /* GLOBAL_P */
73 int MAXTRIES = 5; /* GLOBAL_P */
74 bool LOGENCRYPT = false; /* GLOBAL_P */
75 int COLLECT_INTERVAL = 0; /* GLOBAL_P */
76 int COLLECT_WINDOW = 30; /* GLOBAL_P */
77 bool SERVER_LISTEN = true; /* GLOBAL_P */
78 
79 ServerAccess SERVER_ACCESS = { 0 }; /* GLOBAL_P */
80 
81 char CFRUNCOMMAND[CF_MAXVARSIZE] = { 0 }; /* GLOBAL_P */
82 
83 /******************************************************************/
84 
85 static void SpawnConnection(EvalContext *ctx, const char *ipaddr, ConnectionInfo *info);
86 static void PurgeOldConnections(Item **list, time_t now);
87 static void *HandleConnection(void *conn);
89 static void DeleteConn(ServerConnectionState *conn);
90 
91 /****************************************************************************/
92 
93 void ServerEntryPoint(EvalContext *ctx, const char *ipaddr, ConnectionInfo *info)
94 {
96  "Obtained IP address of '%s' on socket %d from accept",
97  ipaddr, ConnectionInfoSocket(info));
98 
99  /* TODO change nonattackerlist, attackerlist and especially connectionlist
100  * to binary searched lists, or remove them from the main thread! */
103  {
105  "Remote host '%s' not in allowconnects, denying connection",
106  ipaddr);
107  }
108  else if (IsMatchItemIn(SERVER_ACCESS.attackerlist, ipaddr))
109  {
111  "Remote host '%s' is in denyconnects, denying connection",
112  ipaddr);
113  }
114  else
115  {
116  time_t now = time(NULL);
117  if (now == -1)
118  {
119  now = 0;
120  }
121 
123 
124  bool allow = IsMatchItemIn(SERVER_ACCESS.multiconnlist, ipaddr);
125  if (!allow)
126  {
128  /* At most one connection allowed for this host: */
129  allow = !IsItemIn(SERVER_ACCESS.connectionlist, ipaddr);
131 
132  if (!allow) /* Duplicate. */
133  {
135  "Remote host '%s' is not in allowallconnects, denying second simultaneous connection",
136  ipaddr);
137  }
138  }
139 
140  if (allow)
141  {
142  char intime[PRINTSIZE(now)];
143  xsnprintf(intime, sizeof(intime), "%jd", (intmax_t) now);
144 
146  PrependItem(&SERVER_ACCESS.connectionlist, ipaddr, intime);
148 
149  SpawnConnection(ctx, ipaddr, info);
150  return; /* Success */
151  }
152  }
153  /* Tidy up on failure: */
154 
155  if (info->is_call_collect)
156  {
158  }
160  ConnectionInfoDestroy(&info);
161 }
162 
163 /**********************************************************************/
164 
165 static void PurgeOldConnections(Item **list, time_t now)
166  /* Some connections might not terminate properly. These should be cleaned
167  every couple of hours. That should be enough to prevent spamming. */
168 {
169  assert(list != NULL);
170 
171  Log(LOG_LEVEL_DEBUG, "Purging Old Connections...");
172 
174  Item *next;
175  for (Item *ip = *list; ip != NULL; ip = next)
176  {
177  int then = 0;
178  sscanf(ip->classes, "%d", &then);
179 
180  next = ip->next;
181 
182  if (now > then + 2 * SECONDS_PER_HOUR)
183  {
185  "IP address '%s' has been more than two hours in connection list, purging",
186  ip->name);
187  DeleteItem(list, ip);
188  }
189  }
191 
192  Log(LOG_LEVEL_DEBUG, "Done purging old connections");
193 }
194 
195 /*********************************************************************/
196 
197 static void SpawnConnection(EvalContext *ctx, const char *ipaddr, ConnectionInfo *info)
198 {
199  ServerConnectionState *conn = NULL;
200  int ret;
201  pthread_t tid;
202  pthread_attr_t threadattrs;
203 
204  conn = NewConn(ctx, info); /* freed in HandleConnection */
205  int sd_accepted = ConnectionInfoSocket(info);
206  strlcpy(conn->ipaddr, ipaddr, CF_MAX_IP_LEN );
207 
209  "New connection (from %s, sd %d), spawning new thread...",
210  conn->ipaddr, sd_accepted);
211 
212  ret = pthread_attr_init(&threadattrs);
213  if (ret != 0)
214  {
216  "SpawnConnection: Unable to initialize thread attributes (%s)",
217  GetErrorStr());
218  goto err;
219  }
220  ret = pthread_attr_setdetachstate(&threadattrs, PTHREAD_CREATE_DETACHED);
221  if (ret != 0)
222  {
224  "SpawnConnection: Unable to set thread to detached state (%s).",
225  GetErrorStr());
226  goto cleanup;
227  }
228  ret = pthread_attr_setstacksize(&threadattrs, 1024 * 1024);
229  if (ret != 0)
230  {
232  "SpawnConnection: Unable to set thread stack size (%s).",
233  GetErrorStr());
234  /* Continue with default thread stack size. */
235  }
236 
237  ret = pthread_create(&tid, &threadattrs, HandleConnection, conn);
238  if (ret != 0)
239  {
240  errno = ret;
242  "Unable to spawn worker thread. (pthread_create: %s)",
243  GetErrorStr());
244  goto cleanup;
245  }
246 
247  cleanup:
248  pthread_attr_destroy(&threadattrs);
249  err:
250  if (ret != 0)
251  {
252  Log(LOG_LEVEL_WARNING, "Thread is being handled from main loop!");
253  HandleConnection(conn);
254  }
255 }
256 
257 /*********************************************************************/
258 
259 static void DisableSendDelays(int sockfd)
260 {
261  int yes = 1;
262 
263  if (setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, (void *) &yes, sizeof(yes)) == -1)
264  {
265  Log(LOG_LEVEL_INFO, "Unable to disable Nagle algorithm, expect performance problems. (setsockopt(TCP_NODELAY): %s)", GetErrorStr());
266  }
267 }
268 
269 /*********************************************************************/
270 
271 /* TODO performance fix this to avoid the StringConcatenate() reallocations,
272  * if only we could set a static logging prefix. */
273 static char *LogHook(LoggingPrivContext *log_ctx,
274  ARG_UNUSED LogLevel level, const char *message)
275 {
276  const char *aligned_ipaddr = log_ctx->param;
277  return StringConcatenate(2, aligned_ipaddr, message);
278 }
279 
280 /* TRIES: counts the number of consecutive connections dropped. */
281 static int TRIES = 0;
282 
283 static void *HandleConnection(void *c)
284 {
285  ServerConnectionState *conn = c;
286  int ret;
287 
288  /* Set logging prefix to be the IP address for all of thread's lifetime. */
289  /* These stack-allocated variables should be valid for all the lifetime of
290  * the thread. */
291  char aligned_ipaddr[CF_MAX_IP_LEN + 2];
292  LoggingPrivContext log_ctx = {
293  .log_hook = LogHook,
294  .param = aligned_ipaddr
295  };
296 
297  strlcpy(aligned_ipaddr, conn->ipaddr, sizeof(aligned_ipaddr));
298  strlcat(aligned_ipaddr, "> ", sizeof(aligned_ipaddr));
299  /* Pad with enough spaces for IPv4 addresses to be aligned. Max chars are
300  * 15 for the address plus two for "> " == 17. */
301  size_t len;
302  for (len = strlen(aligned_ipaddr); len < 17; len++)
303  {
304  aligned_ipaddr[len] = ' ';
305  }
306  aligned_ipaddr[len] = '\0';
307 
308  LoggingPrivSetContext(&log_ctx);
309 
310  Log(LOG_LEVEL_INFO, "Accepting connection");
311 
312  /* We test if number of active threads is greater than max, if so we deny
313  connection, if it happened too many times within a short timeframe then we
314  kill ourself.TODO this test should be done *before* spawning the thread. */
317  {
318  if (TRIES > MAXTRIES)
319  {
320  /* This happens when no thread was freed while we had to drop 5
321  * (or maxconnections/3) consecutive connections, because none of
322  * the existing threads finished. */
324  "Server seems to be paralyzed. DOS attack? "
325  "Committing apoptosis...");
327  FatalError(conn->ctx, "Terminating");
328  }
329 
330  TRIES++;
332  "Too many threads (%d > %d), dropping connection! "
333  "Increase server maxconnections?",
335 
337  goto conndone;
338  }
339 
340  ACTIVE_THREADS++;
341  TRIES = 0;
343 
345 
346  /* 20 times the connect() timeout should be enough to avoid MD5
347  * computation timeouts on big files on old slow Solaris 8 machines. */
349  CONNTIMEOUT * 20 * 1000);
350 
352  {
353  /* Decide the protocol used. */
354  bool success = ServerTLSPeek(conn->conn_info);
355  if (!success)
356  {
357  goto dethread;
358  }
359  }
360 
361  ProtocolVersion protocol_version = ConnectionInfoProtocolVersion(conn->conn_info);
362  if (ProtocolIsTLS(protocol_version))
363  {
364  bool established = ServerTLSSessionEstablish(conn, NULL);
365  if (!established)
366  {
367  goto dethread;
368  }
369  }
370  else if (ProtocolIsClassic(protocol_version))
371  {
372  /* This connection is legacy protocol.
373  * We are not allowing it by default. */
375  {
377  "Connection is not using latest protocol, denying");
378  goto dethread;
379  }
380  }
381  else
382  {
383  UnexpectedError("HandleConnection: ProtocolVersion %d!",
385  goto dethread;
386  }
387 
388 
389  /* ========================= MAIN LOOPS ========================= */
390  if (ProtocolIsTLS(protocol_version))
391  {
392  /* New protocol does DNS reverse look up of the connected
393  * IP address, to check hostname access_rules. */
395  {
396  ret = getnameinfo((const struct sockaddr *) &conn->conn_info->ss,
397  conn->conn_info->ss_len,
398  conn->revdns, sizeof(conn->revdns),
399  NULL, 0, NI_NAMEREQD);
400  if (ret != 0)
401  {
403  "Reverse lookup failed (getnameinfo: %s)!",
404  gai_strerror(ret));
405  }
406  else
407  {
409  "Hostname (reverse looked up): %s",
410  conn->revdns);
411  }
412  }
413 
414  while (BusyWithNewProtocol(conn->ctx, conn))
415  {
416  }
417  }
418  else if (ProtocolIsClassic(protocol_version))
419  {
420  while (BusyWithClassicConnection(conn->ctx, conn))
421  {
422  }
423  }
424  else
425  {
426  assert(!"Bogus protocol version - but we checked that already !");
427  }
428  /* ============================================================ */
429 
430  Log(LOG_LEVEL_INFO, "Closing connection, terminating thread");
431 
432  dethread:
434  ACTIVE_THREADS--;
436 
437  conndone:
438  if (conn->conn_info->is_call_collect)
439  {
441  }
442  DeleteConn(conn);
443  return NULL;
444 }
445 
446 
447 /***************************************************************/
448 /* Toolkit/Class: conn */
449 /***************************************************************/
450 
452 {
453 #if 1
454  /* TODO: why do we do this ? We fail if getsockname() fails, but
455  * don't use the information it returned. Was the intent to use
456  * it to fill in conn->ipaddr ? */
457  struct sockaddr_storage addr;
458  socklen_t size = sizeof(addr);
459 
460  if (getsockname(ConnectionInfoSocket(info), (struct sockaddr *)&addr, &size) == -1)
461  {
463  "Could not obtain socket address. (getsockname: '%s')",
464  GetErrorStr());
465  return NULL;
466  }
467 #endif
468 
469  ServerConnectionState *conn = xcalloc(1, sizeof(*conn));
470  conn->ctx = ctx;
471  conn->conn_info = info;
472  conn->encryption_type = 'c';
473  /* Only public files (chmod o+r) accessible to non-root */
474  conn->uid = CF_UNKNOWN_OWNER; /* Careful, 0 is root! */
475  /* conn->maproot is false: only public files (chmod o+r) are accessible */
476 
478  "New connection (socket %d).",
479  ConnectionInfoSocket(info));
480  return conn;
481 }
482 
483 /**
484  * @note This function is thread-safe. Do NOT wrap it with mutex!
485  */
487 {
488  int sd = ConnectionInfoSocket(conn->conn_info);
489  if (sd != SOCKET_INVALID)
490  {
491  cf_closesocket(sd);
492  }
494 
495  if (conn->ipaddr[0] != '\0')
496  {
500  }
501 
502  *conn = (ServerConnectionState) {0};
503  free(conn->session_key);
504  free(conn);
505 }
void * xcalloc(size_t nmemb, size_t size)
Definition: alloc-mini.c:51
void FatalError(const EvalContext *ctx, char *s,...)
Definition: audit.c:94
#define ARG_UNUSED
Definition: cf-net.c:47
void CollectCallMarkProcessed()
#define SECONDS_PER_HOUR
Definition: cf3.defs.h:71
struct ServerConnectionState_ ServerConnectionState
Definition: cf3.defs.h:1731
#define CF_UNKNOWN_OWNER
Definition: cf3.defs.h:63
time_t CONNTIMEOUT
Definition: cf3globals.c:106
void free(void *)
#define CF_MAX_IP_LEN
Definition: cfnet.h:39
#define SOCKET_INVALID
Definition: cfnet.h:47
int cf_closesocket(int sd)
Definition: misc.c:31
void ConnectionInfoDestroy(ConnectionInfo **info)
Destroys a ConnectionInfo structure.
int ConnectionInfoSocket(const ConnectionInfo *info)
Connection socket.
ProtocolVersion ConnectionInfoProtocolVersion(const ConnectionInfo *info)
Protocol Version.
@ CONNECTIONINFO_STATUS_ESTABLISHED
#define CF_MAXVARSIZE
Definition: definitions.h:36
static void cleanup(void *generic_data)
Definition: fchmodat.c:56
const char * gai_strerror(int errcode)
Definition: getaddrinfo.c:340
int getnameinfo(const struct sockaddr *sa, socklen_t salen, char *node, socklen_t nodelen, char *service, socklen_t servicelen, int flags)
Definition: getaddrinfo.c:476
int errno
#define NULL
Definition: getopt1.c:56
pthread_mutex_t * cft_server_children
Definition: global_mutex.c:40
pthread_mutex_t * cft_count
Definition: global_mutex.c:38
bool IsMatchItemIn(const Item *list, const char *item)
Definition: item_lib.c:780
void DeleteItem(Item **liststart, Item *item)
Definition: item_lib.c:823
bool DeleteItemMatching(Item **list, const char *string)
Definition: item_lib.c:988
bool IsItemIn(const Item *list, const char *item)
Definition: item_lib.c:226
Item * PrependItem(Item **liststart, const char *itemstring, const char *classes)
Definition: item_lib.c:372
LogLevel
Definition: log.h:30
void LoggingPrivSetContext(LoggingPrivContext *pctx)
Attaches context to logging for current thread.
Definition: logging.c:93
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_DEBUG
Definition: logging.h:47
@ LOG_LEVEL_WARNING
Definition: logging.h:43
@ LOG_LEVEL_CRIT
Definition: logging.h:41
@ LOG_LEVEL_VERBOSE
Definition: logging.h:46
@ LOG_LEVEL_INFO
Definition: logging.h:45
void xsnprintf(char *str, size_t str_size, const char *format,...)
Definition: misc_lib.c:114
#define UnexpectedError(...)
Definition: misc_lib.h:38
#define ThreadUnlock(m)
Definition: mutex.h:33
#define ThreadLock(m)
Definition: mutex.h:32
int SetReceiveTimeout(int fd, unsigned long ms)
Definition: net.c:684
int socklen_t
Definition: platform.h:419
#define sockaddr_storage
Definition: platform.h:689
#define PRINTSIZE(what)
Definition: printsize.h:66
static bool ProtocolIsTLS(const ProtocolVersion p)
ProtocolVersion
static bool ProtocolIsClassic(const ProtocolVersion p)
int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize)
static void * HandleConnection(void *conn)
Definition: server.c:283
char CFRUNCOMMAND[1024]
Definition: server.c:81
int MAXTRIES
Definition: server.c:73
void ServerEntryPoint(EvalContext *ctx, const char *ipaddr, ConnectionInfo *info)
Definition: server.c:93
static void SpawnConnection(EvalContext *ctx, const char *ipaddr, ConnectionInfo *info)
Definition: server.c:197
static void DisableSendDelays(int sockfd)
Definition: server.c:259
int COLLECT_WINDOW
Definition: server.c:76
bool DENYBADCLOCKS
Definition: server.c:72
static char * LogHook(LoggingPrivContext *log_ctx, LogLevel level, const char *message)
Definition: server.c:273
int CFD_MAXPROCESSES
Definition: server.c:71
static void PurgeOldConnections(Item **list, time_t now)
Definition: server.c:165
ServerAccess SERVER_ACCESS
Definition: server.c:79
bool LOGENCRYPT
Definition: server.c:74
bool SERVER_LISTEN
Definition: server.c:77
static int TRIES
Definition: server.c:281
int COLLECT_INTERVAL
Definition: server.c:75
static void DeleteConn(ServerConnectionState *conn)
Definition: server.c:486
int ACTIVE_THREADS
Definition: server.c:69
static ServerConnectionState * NewConn(EvalContext *ctx, ConnectionInfo *info)
Definition: server.c:451
bool NEED_REVERSE_LOOKUP
bool BusyWithClassicConnection(EvalContext *ctx, ServerConnectionState *conn)
bool ServerTLSSessionEstablish(ServerConnectionState *conn, SSL_CTX *ssl_ctx)
Accept a TLS connection and authenticate and identify.
Definition: server_tls.c:530
bool BusyWithNewProtocol(EvalContext *ctx, ServerConnectionState *conn)
Definition: server_tls.c:640
bool ServerTLSPeek(ConnectionInfo *conn_info)
Set the connection type to CLASSIC or TLS.
Definition: server_tls.c:217
char * StringConcatenate(size_t count, const char *first,...)
Definition: string_lib.c:348
size_t strlcat(char *dst, const char *src, size_t siz)
Definition: strlcat.c:36
size_t strlcpy(char *dst, const char *src, size_t siz)
Definition: strlcpy.c:34
socklen_t ss_len
struct sockaddr ss
ConnectionStatus status
Definition: item_lib.h:33
Item * next
Definition: item_lib.h:38
LoggingPrivLogHook log_hook
Definition: logging_priv.h:38
Item * multiconnlist
Definition: server.h:65
Item * nonattackerlist
Definition: server.h:62
Item * connectionlist
Definition: server.h:59
Item * allowlegacyconnects
Definition: server.h:67
Item * attackerlist
Definition: server.h:63
EvalContext * ctx
Definition: server.h:124
char revdns[1025]
Definition: server.h:99
unsigned char * session_key
Definition: server.h:119
ConnectionInfo * conn_info
Definition: server.h:94