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_common.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 static const int CF_NOSIZE = -1;
27 
28 
29 #include <server_common.h>
30 
31 #include <item_lib.h> /* ItemList2CSV_bound */
32 #include <string_lib.h> /* ToLower,StrCatDelim */
33 #include <regex.h> /* StringMatchFull */
34 #include <crypto.h> /* EncryptString */
35 #include <files_names.h>
36 #include <files_interfaces.h>
37 #include <hash.h>
38 #include <file_lib.h>
39 #include <eval_context.h>
40 #include <dir.h>
41 #include <conversion.h>
42 #include <matching.h> /* IsRegexItemIn */
43 #include <pipes.h>
44 #include <classic.h> /* SendSocketStream */
45 #include <net.h> /* SendTransaction,ReceiveTransaction */
46 #include <openssl/err.h> /* ERR_get_error */
47 #include <protocol.h> /* ProtocolIsKnown() */
48 #include <tls_generic.h> /* TLSSend */
49 #include <rlist.h>
51 #include <connection_info.h>
52 #include <misc_lib.h> /* UnexpectedError */
53 #include <cf-windows-functions.h> /* NovaWin_UserNameToSid */
54 #include <mutex.h> /* ThreadLock */
55 #include <stat_cache.h> /* struct Stat */
56 #include "server_access.h"
57 
58 
59 /* NOTE: Always Log(LOG_LEVEL_INFO) before calling RefuseAccess(), so that
60  * some clue is printed in the cf-serverd logs. */
61 void RefuseAccess(ServerConnectionState *conn, char *errmesg)
62 {
64 
65  /* TODO remove logging, it's done elsewhere. */
66  Log(LOG_LEVEL_VERBOSE, "REFUSAL to user='%s' of request: %s",
67  NULL_OR_EMPTY(conn->username) ? "?" : conn->username,
68  errmesg);
69 }
70 
71 bool IsUserNameValid(const char *username)
72 {
73  /* Add whatever characters are considered invalid in username */
74  const char *invalid_username_characters = "\\/";
75 
76  if (strpbrk(username, invalid_username_characters) == NULL)
77  {
78  return true;
79  }
80 
81  return false;
82 }
83 
84 bool AllowedUser(char *user)
85 {
87  {
88  Log(LOG_LEVEL_DEBUG, "User %s granted connection privileges", user);
89  return true;
90  }
91 
92  Log(LOG_LEVEL_DEBUG, "User %s is not allowed on this server", user);
93  return false;
94 }
95 
97 {
98  Log(LOG_LEVEL_VERBOSE, "Scanning for all persistent classes");
99 
100  CF_DB *dbp;
101  CF_DBC *dbcp;
102 
103  if (!OpenDB(&dbp, dbid_state))
104  {
105  char *db_path = DBIdToPath(dbid_state);
106  Log(LOG_LEVEL_ERR, "Unable to open persistent classes database '%s'", db_path);
107  free(db_path);
108  return NULL;
109  }
110 
111  if (!NewDBCursor(dbp, &dbcp))
112  {
113  char *db_path = DBIdToPath(dbid_state);
114  Log(LOG_LEVEL_ERR, "Unable to get cursor for persistent classes database '%s'", db_path);
115  free(db_path);
116  CloseDB(dbp);
117  return NULL;
118  }
119 
120  const PersistentClassInfo *value;
121  int ksize, vsize;
122  char *key;
123  size_t count = 0;
124  time_t now = time(NULL);
125  Item *persistent_classes = NULL;
126  while (NextDB(dbcp, &key, &ksize, (void **)&value, &vsize))
127  {
128  if (now > value->expires)
129  {
131  "Persistent class %s expired, removing from database", key);
132  DBCursorDeleteEntry(dbcp);
133  }
134  else
135  {
136  count++;
137  PrependItem(&persistent_classes, key, NULL);
138  }
139  }
140 
141  DeleteDBCursor(dbcp);
142  CloseDB(dbp);
143 
145  {
146  char logbuf[CF_BUFSIZE];
147  ItemList2CSV_bound(persistent_classes, logbuf, sizeof(logbuf), ' ');
149  "Found %zu valid persistent classes in state database: %s",
150  count, logbuf);
151  }
152 
153  return persistent_classes;
154 }
155 
156 
158 {
159  char buffer[CF_BUFSIZE];
160 
161  snprintf(buffer, CF_BUFSIZE, "Hello %s (%s), nothing relevant to do here...\n\n", conn->hostname, conn->ipaddr);
162 
163  if (SendTransaction(conn->conn_info, buffer, 0, CF_DONE) == -1)
164  {
165  Log(LOG_LEVEL_ERR, "Unable to send transaction. (send: %s)", GetErrorStr());
166  }
167 }
168 
169 /* Used only in EXEC protocol command, to check if any of the received classes
170  * is defined in the server. */
172 {
173  char recvbuffer[CF_BUFSIZE];
174  Item *classlist = NULL, *ip;
175  int count = 0;
176 
177  while (true && (count < 10)) /* arbitrary check to avoid infinite loop, DoS attack */
178  {
179  count++;
180 
181  if (ReceiveTransaction(conn->conn_info, recvbuffer, NULL) == -1)
182  {
183  Log(LOG_LEVEL_VERBOSE, "Unable to read data from network. (ReceiveTransaction: %s)", GetErrorStr());
184  return false;
185  }
186 
187  if (strncmp(recvbuffer, CFD_TERMINATOR, strlen(CFD_TERMINATOR)) == 0)
188  {
189  Log(LOG_LEVEL_DEBUG, "Got CFD_TERMINATOR");
190  if (count == 1)
191  {
192  /* This is the common case, that cf-runagent had no
193  "-s class1,class2" argument. */
194  Log(LOG_LEVEL_DEBUG, "No classes were sent, assuming no restrictions...");
195  return true;
196  }
197 
198  break;
199  }
200 
201  Log(LOG_LEVEL_DEBUG, "Got class buffer: %s", recvbuffer);
202 
203  classlist = SplitStringAsItemList(recvbuffer, ' ');
204 
205  for (ip = classlist; ip != NULL; ip = ip->next)
206  {
207  Log(LOG_LEVEL_VERBOSE, "Checking whether class %s can be identified as me...", ip->name);
208 
209  if (IsDefinedClass(ctx, ip->name))
210  {
211  Log(LOG_LEVEL_DEBUG, "Class '%s' matched, accepting...", ip->name);
212  DeleteItemList(classlist);
213  return true;
214  }
215 
216  {
217  /* What the heck are we doing here? */
218  /* Hmmm so we iterate over all classes to see if the regex
219  * received (ip->name) matches (StringMatchFull) to any local
220  * class (expr)... SLOW! Change the spec! Don't accept
221  * regexes! How many will be affected if a specific class has
222  * to be set to run command, instead of matching a pattern?
223  * It's safer anyway... */
225  Class *cls = NULL;
226  while ((cls = ClassTableIteratorNext(iter)))
227  {
228  char *expr = ClassRefToString(cls->ns, cls->name);
229  /* FIXME: review this strcmp. Moved out from StringMatch */
230  bool match = (strcmp(ip->name, expr) == 0 ||
231  StringMatchFull(ip->name, expr));
232  free(expr);
233  if (match)
234  {
235  Log(LOG_LEVEL_DEBUG, "Class matched regular expression '%s', accepting...", ip->name);
236  DeleteItemList(classlist);
237  return true;
238  }
239  }
241  }
242 
243  if (strncmp(ip->name, CFD_TERMINATOR, strlen(CFD_TERMINATOR)) == 0)
244  {
245  Log(LOG_LEVEL_VERBOSE, "No classes matched, rejecting....");
246  ReplyNothing(conn);
247  DeleteItemList(classlist);
248  return false;
249  }
250  }
251  }
252 
253  ReplyNothing(conn);
254  Log(LOG_LEVEL_VERBOSE, "No classes matched, rejecting....");
255  DeleteItemList(classlist);
256  return false;
257 }
258 
259 /* TODO deprecate this function, only a simple SendTransaction(CFD_TERMINATOR)
260  * should be enough, without even error printing (it's already done in
261  * SendTransaction()). */
262 void Terminate(ConnectionInfo *connection)
263 {
264  /* We send a trailing NULL in this transaction packet. TODO WHY? */
265  if (SendTransaction(connection, CFD_TERMINATOR,
266  strlen(CFD_TERMINATOR) + 1, CF_DONE) == -1)
267  {
268  Log(LOG_LEVEL_VERBOSE, "Unable to reply with terminator. (send: %s)",
269  GetErrorStr());
270  }
271 }
272 
273 static bool TransferRights(
274  const ServerConnectionState *conn,
275  const char *filename,
276  const struct stat *sb)
277 {
278  Log(LOG_LEVEL_DEBUG, "Checking ownership of file: %s", filename);
279 
280  /* Don't do any check if connected user claims to be "root" or if
281  * "maproot" in access_rules contains the connecting IP address. */
282  if ((conn->uid == 0) || (conn->maproot))
283  {
284  Log(LOG_LEVEL_DEBUG, "Access granted because %s",
285  (conn->uid == 0) ? "remote user is root"
286  : "of maproot");
287  return true; /* access granted */
288  }
289 
290 #ifdef __MINGW32__
291 
292  SECURITY_DESCRIPTOR *secDesc;
293  SID *ownerSid;
294 
295  if (GetNamedSecurityInfo(
296  filename, SE_FILE_OBJECT, OWNER_SECURITY_INFORMATION,
297  (PSID *) &ownerSid, NULL, NULL, NULL, (void **) &secDesc)
298  != ERROR_SUCCESS)
299  {
301  "Could not retrieve owner of file '%s' "
302  "(GetNamedSecurityInfo: %s)",
303  filename, GetErrorStr());
304  return false;
305  }
306 
307  LocalFree(secDesc);
308 
309  if (!IsValidSid(conn->sid) ||
310  !EqualSid(ownerSid, conn->sid))
311  {
312  /* If "maproot" we've already granted access. */
313  assert(!conn->maproot);
314 
316  "Remote user '%s' is not the owner of the file, access denied, "
317  "consider maproot", conn->username);
318 
319  return false;
320  }
321 
323  "User '%s' is the owner of the file, access granted",
324  conn->username);
325 
326 #else /* UNIX systems - common path */
327 
328  if (sb->st_uid != conn->uid) /* does not own the file */
329  {
330  if (!(sb->st_mode & S_IROTH)) /* file not world readable */
331  {
333  "Remote user '%s' is not owner of the file, access denied, "
334  "consider maproot or making file world-readable",
335  conn->username);
336  return false;
337  }
338  else
339  {
341  "Remote user '%s' is not the owner of the file, "
342  "but file is world readable, access granted",
343  conn->username); /* access granted */
344  }
345  }
346  else
347  {
349  "User '%s' is the owner of the file, access granted",
350  conn->username); /* access granted */
351  }
352 
353  /* ADMIT ACCESS, to summarise the following condition is now true: */
354 
355  /* Remote user is root, where "user" is just a string in the protocol, he
356  * might claim whatever he wants but will be able to login only if the
357  * user-key.pub key is found, */
358  assert((conn->uid == 0) ||
359  /* OR remote IP has maproot in the file's access_rules, */
360  (conn->maproot == true) ||
361  /* OR file is owned by the same username the user claimed - useless or
362  * even dangerous outside NIS, KERBEROS or LDAP authenticated domains, */
363  (sb->st_uid == conn->uid) ||
364  /* OR file is readable by everyone */
365  (sb->st_mode & S_IROTH));
366 
367 #endif
368 
369  return true;
370 }
371 
372 static void AbortTransfer(ConnectionInfo *connection, char *filename)
373 {
374  Log(LOG_LEVEL_VERBOSE, "Aborting transfer of file due to source changes");
375 
376  char sendbuffer[CF_BUFSIZE];
377  snprintf(sendbuffer, CF_BUFSIZE, "%s%s: %s",
378  CF_CHANGEDSTR1, CF_CHANGEDSTR2, filename);
379 
380  if (SendTransaction(connection, sendbuffer, 0, CF_DONE) == -1)
381  {
382  Log(LOG_LEVEL_VERBOSE, "Send failed in GetFile. (send: %s)",
383  GetErrorStr());
384  }
385 }
386 
387 static void FailedTransfer(ConnectionInfo *connection)
388 {
389  Log(LOG_LEVEL_VERBOSE, "Transfer failure");
390 
391  char sendbuffer[CF_BUFSIZE];
392 
393  snprintf(sendbuffer, CF_BUFSIZE, "%s", CF_FAILEDSTR);
394 
395  if (SendTransaction(connection, sendbuffer, 0, CF_DONE) == -1)
396  {
397  Log(LOG_LEVEL_VERBOSE, "Send failed in GetFile. (send: %s)",
398  GetErrorStr());
399  }
400 }
401 
403 {
404  int fd;
405  off_t n_read, total = 0, sendlen = 0, count = 0;
406  char sendbuffer[CF_BUFSIZE + 256], filename[CF_BUFSIZE];
407  struct stat sb;
408  int blocksize = 2048;
409 
410  ConnectionInfo *conn_info = args->conn->conn_info;
411 
412  TranslatePath(filename, args->replyfile);
413 
414  stat(filename, &sb);
415 
416  Log(LOG_LEVEL_DEBUG, "CfGetFile('%s'), size = %jd",
417  filename, (intmax_t) sb.st_size);
418 
419 /* Now check to see if we have remote permission */
420 
421  if (!TransferRights(args->conn, filename, &sb))
422  {
423  Log(LOG_LEVEL_INFO, "REFUSE access to file: %s", filename);
424  RefuseAccess(args->conn, args->replyfile);
425  snprintf(sendbuffer, CF_BUFSIZE, "%s", CF_FAILEDSTR);
426 
427  const ProtocolVersion version = ConnectionInfoProtocolVersion(conn_info);
428  assert(ProtocolIsKnown(version));
429  if (ProtocolIsClassic(version))
430  {
431  SendSocketStream(ConnectionInfoSocket(conn_info), sendbuffer, args->buf_size);
432  }
433  else if (ProtocolIsTLS(version))
434  {
435  TLSSend(ConnectionInfoSSL(conn_info), sendbuffer, args->buf_size);
436  }
437  return;
438  }
439 
440 /* File transfer */
441 
442  if ((fd = safe_open(filename, O_RDONLY)) == -1)
443  {
444  Log(LOG_LEVEL_ERR, "Open error of file '%s'. (open: %s)",
445  filename, GetErrorStr());
446  snprintf(sendbuffer, CF_BUFSIZE, "%s", CF_FAILEDSTR);
447 
448  const ProtocolVersion version = ConnectionInfoProtocolVersion(conn_info);
449  assert(ProtocolIsKnown(version));
450  if (ProtocolIsClassic(version))
451  {
452  SendSocketStream(ConnectionInfoSocket(conn_info), sendbuffer, args->buf_size);
453  }
454  else if (ProtocolIsTLS(version))
455  {
456  TLSSend(ConnectionInfoSSL(conn_info), sendbuffer, args->buf_size);
457  }
458  }
459  else
460  {
461  int div = 3;
462 
463  if (sb.st_size > 10485760L) /* File larger than 10 MB, checks every 64kB */
464  {
465  div = 32;
466  }
467 
468  while (true)
469  {
470  memset(sendbuffer, 0, CF_BUFSIZE);
471 
472  Log(LOG_LEVEL_DEBUG, "Now reading from disk...");
473 
474  if ((n_read = read(fd, sendbuffer, blocksize)) == -1)
475  {
476  Log(LOG_LEVEL_ERR, "Read failed in GetFile. (read: %s)", GetErrorStr());
477  break;
478  }
479 
480  if (n_read == 0)
481  {
482  break;
483  }
484  else
485  {
486  off_t savedlen = sb.st_size;
487 
488  /* check the file is not changing at source */
489 
490  if (count++ % div == 0) /* Don't do this too often */
491  {
492  if (stat(filename, &sb))
493  {
494  Log(LOG_LEVEL_ERR, "Cannot stat file '%s'. (stat: %s)",
495  filename, GetErrorStr());
496  break;
497  }
498  }
499 
500  if (sb.st_size != savedlen)
501  {
502  snprintf(sendbuffer, CF_BUFSIZE, "%s%s: %s", CF_CHANGEDSTR1, CF_CHANGEDSTR2, filename);
503 
504  const ProtocolVersion version = ConnectionInfoProtocolVersion(conn_info);
505 
506  if (ProtocolIsClassic(version))
507  {
508  if (SendSocketStream(ConnectionInfoSocket(conn_info), sendbuffer, blocksize) == -1)
509  {
510  Log(LOG_LEVEL_VERBOSE, "Send failed in GetFile. (send: %s)", GetErrorStr());
511  }
512  }
513  else if (ProtocolIsTLS(version))
514  {
515  if (TLSSend(ConnectionInfoSSL(conn_info), sendbuffer, blocksize) == -1)
516  {
517  Log(LOG_LEVEL_VERBOSE, "Send failed in GetFile. (send: %s)", GetErrorStr());
518  }
519  }
520 
522  "Aborting transfer after %jd: file is changing rapidly at source.",
523  (intmax_t) total);
524  break;
525  }
526 
527  if ((savedlen - total) / blocksize > 0)
528  {
529  sendlen = blocksize;
530  }
531  else if (savedlen != 0)
532  {
533  sendlen = (savedlen - total);
534  }
535  }
536 
537  total += n_read;
538 
539  const ProtocolVersion version = ConnectionInfoProtocolVersion(conn_info);
540 
541  if (ProtocolIsClassic(version))
542  {
543  if (SendSocketStream(ConnectionInfoSocket(conn_info), sendbuffer, sendlen) == -1)
544  {
545  Log(LOG_LEVEL_VERBOSE, "Send failed in GetFile. (send: %s)", GetErrorStr());
546  break;
547  }
548  }
549  else if (ProtocolIsTLS(version))
550  {
551  if (TLSSend(ConnectionInfoSSL(conn_info), sendbuffer, sendlen) == -1)
552  {
553  Log(LOG_LEVEL_VERBOSE, "Send failed in GetFile. (send: %s)", GetErrorStr());
554  break;
555  }
556  }
557  }
558 
559  close(fd);
560  }
561 }
562 
564 /* Because the stream doesn't end for each file, we need to know the
565  exact number of bytes transmitted, which might change during
566  encryption, hence we need to handle this with transactions */
567 {
568  int fd, n_read, cipherlen = 0, finlen = 0;
569  off_t total = 0, count = 0;
570  char sendbuffer[CF_BUFSIZE + 256], out[CF_BUFSIZE], filename[CF_BUFSIZE];
571  unsigned char iv[32] =
572  { 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8 };
573  int blocksize = CF_BUFSIZE - 4 * CF_INBAND_OFFSET;
574  char *key, enctype;
575  struct stat sb;
576  ConnectionInfo *conn_info = args->conn->conn_info;
577 
578  key = args->conn->session_key;
579  enctype = args->conn->encryption_type;
580 
581  TranslatePath(filename, args->replyfile);
582 
583  stat(filename, &sb);
584 
585  Log(LOG_LEVEL_DEBUG, "CfEncryptGetFile('%s'), size = %jd",
586  filename, (intmax_t) sb.st_size);
587 
588 /* Now check to see if we have remote permission */
589 
590  if (!TransferRights(args->conn, filename, &sb))
591  {
592  Log(LOG_LEVEL_INFO, "REFUSE access to file: %s", filename);
593  RefuseAccess(args->conn, args->replyfile);
594  FailedTransfer(conn_info);
595  return;
596  }
597 
598  EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
599  if (ctx == NULL)
600  {
601  Log(LOG_LEVEL_ERR, "Failed to allocate cipher: %s",
602  TLSErrorString(ERR_get_error()));
603  return;
604  }
605 
606  if ((fd = safe_open(filename, O_RDONLY)) == -1)
607  {
608  Log(LOG_LEVEL_ERR, "Open error of file '%s'. (open: %s)", filename, GetErrorStr());
609  FailedTransfer(conn_info);
610  }
611  else
612  {
613  int div = 3;
614 
615  if (sb.st_size > 10485760L) /* File larger than 10 MB, checks every 64kB */
616  {
617  div = 32;
618  }
619 
620  while (true)
621  {
622  memset(sendbuffer, 0, CF_BUFSIZE);
623 
624  if ((n_read = read(fd, sendbuffer, blocksize)) == -1)
625  {
626  Log(LOG_LEVEL_ERR, "Read failed in EncryptGetFile. (read: %s)", GetErrorStr());
627  break;
628  }
629 
630  off_t savedlen = sb.st_size;
631 
632  if (count++ % div == 0) /* Don't do this too often */
633  {
634  Log(LOG_LEVEL_DEBUG, "Restatting '%s' - size %d", filename, n_read);
635  if (stat(filename, &sb))
636  {
637  Log(LOG_LEVEL_ERR, "Cannot stat file '%s' (stat: %s)",
638  filename, GetErrorStr());
639  break;
640  }
641  }
642 
643  if (sb.st_size != savedlen)
644  {
645  AbortTransfer(conn_info, filename);
646  break;
647  }
648 
649  total += n_read;
650 
651  if (n_read > 0)
652  {
653  EVP_EncryptInit_ex(ctx, CfengineCipher(enctype), NULL, key, iv);
654 
655  if (!EVP_EncryptUpdate(ctx, out, &cipherlen, sendbuffer, n_read))
656  {
657  FailedTransfer(conn_info);
658  EVP_CIPHER_CTX_free(ctx);
659  close(fd);
660  return;
661  }
662 
663  if (!EVP_EncryptFinal_ex(ctx, out + cipherlen, &finlen))
664  {
665  FailedTransfer(conn_info);
666  EVP_CIPHER_CTX_free(ctx);
667  close(fd);
668  return;
669  }
670  }
671 
672  if (total >= savedlen)
673  {
674  if (SendTransaction(conn_info, out, cipherlen + finlen, CF_DONE) == -1)
675  {
676  Log(LOG_LEVEL_VERBOSE, "Send failed in GetFile. (send: %s)", GetErrorStr());
677  EVP_CIPHER_CTX_free(ctx);
678  close(fd);
679  return;
680  }
681  break;
682  }
683  else
684  {
685  if (SendTransaction(conn_info, out, cipherlen + finlen, CF_MORE) == -1)
686  {
687  Log(LOG_LEVEL_VERBOSE, "Send failed in GetFile. (send: %s)", GetErrorStr());
688  close(fd);
689  EVP_CIPHER_CTX_free(ctx);
690  return;
691  }
692  }
693  }
694  }
695 
696  EVP_CIPHER_CTX_free(ctx);
697  close(fd);
698 }
699 
700 int StatFile(ServerConnectionState *conn, char *sendbuffer, char *ofilename)
701 /* Because we do not know the size or structure of remote datatypes,*/
702 /* the simplest way to transfer the data is to convert them into */
703 /* plain text and interpret them on the other side. */
704 {
705  Stat cfst;
706  struct stat statbuf, statlinkbuf;
707  char linkbuf[CF_BUFSIZE], filename[CF_BUFSIZE];
708  int islink = false;
709 
710  TranslatePath(filename, ofilename);
711 
712  memset(&cfst, 0, sizeof(Stat));
713 
714  if (strlen(ReadLastNode(filename)) > CF_MAXLINKSIZE)
715  {
716  snprintf(sendbuffer, CF_BUFSIZE, "BAD: Filename suspiciously long [%s]", filename);
717  Log(LOG_LEVEL_ERR, "%s", sendbuffer);
718  SendTransaction(conn->conn_info, sendbuffer, 0, CF_DONE);
719  return -1;
720  }
721 
722  if (lstat(filename, &statbuf) == -1)
723  {
724  snprintf(sendbuffer, CF_BUFSIZE, "BAD: unable to stat file %s", filename);
725  Log(LOG_LEVEL_VERBOSE, "%s. (lstat: %s)", sendbuffer, GetErrorStr());
726  SendTransaction(conn->conn_info, sendbuffer, 0, CF_DONE);
727  return -1;
728  }
729 
730  cfst.cf_readlink = NULL;
731  cfst.cf_lmode = 0;
732  cfst.cf_nlink = CF_NOSIZE;
733 
734  memset(linkbuf, 0, CF_BUFSIZE);
735 
736 #ifndef __MINGW32__ // windows doesn't support symbolic links
737  if (S_ISLNK(statbuf.st_mode))
738  {
739  islink = true;
740  cfst.cf_type = FILE_TYPE_LINK; /* pointless - overwritten */
741  cfst.cf_lmode = statbuf.st_mode & 07777;
742  cfst.cf_nlink = statbuf.st_nlink;
743 
744  if (readlink(filename, linkbuf, CF_BUFSIZE - 1) == -1)
745  {
746  strcpy(sendbuffer, "BAD: unable to read link");
747  Log(LOG_LEVEL_ERR, "%s. (readlink: %s)", sendbuffer, GetErrorStr());
748  SendTransaction(conn->conn_info, sendbuffer, 0, CF_DONE);
749  return -1;
750  }
751 
752  Log(LOG_LEVEL_DEBUG, "readlink '%s'", linkbuf);
753 
754  cfst.cf_readlink = linkbuf;
755  }
756 
757  if (islink && (stat(filename, &statlinkbuf) != -1)) /* linktype=copy used by agent */
758  {
759  Log(LOG_LEVEL_DEBUG, "Getting size of link deref '%s'", linkbuf);
760  statbuf.st_size = statlinkbuf.st_size;
761  statbuf.st_mode = statlinkbuf.st_mode;
762  statbuf.st_uid = statlinkbuf.st_uid;
763  statbuf.st_gid = statlinkbuf.st_gid;
764  statbuf.st_mtime = statlinkbuf.st_mtime;
765  statbuf.st_ctime = statlinkbuf.st_ctime;
766  }
767 
768 #endif /* !__MINGW32__ */
769 
770  if (S_ISDIR(statbuf.st_mode))
771  {
772  cfst.cf_type = FILE_TYPE_DIR;
773  }
774 
775  if (S_ISREG(statbuf.st_mode))
776  {
777  cfst.cf_type = FILE_TYPE_REGULAR;
778  }
779 
780  if (S_ISSOCK(statbuf.st_mode))
781  {
782  cfst.cf_type = FILE_TYPE_SOCK;
783  }
784 
785  if (S_ISCHR(statbuf.st_mode))
786  {
787  cfst.cf_type = FILE_TYPE_CHAR_;
788  }
789 
790  if (S_ISBLK(statbuf.st_mode))
791  {
792  cfst.cf_type = FILE_TYPE_BLOCK;
793  }
794 
795  if (S_ISFIFO(statbuf.st_mode))
796  {
797  cfst.cf_type = FILE_TYPE_FIFO;
798  }
799 
800  cfst.cf_mode = statbuf.st_mode & 07777;
801  cfst.cf_uid = statbuf.st_uid & 0xFFFFFFFF;
802  cfst.cf_gid = statbuf.st_gid & 0xFFFFFFFF;
803  cfst.cf_size = statbuf.st_size;
804  cfst.cf_atime = statbuf.st_atime;
805  cfst.cf_mtime = statbuf.st_mtime;
806  cfst.cf_ctime = statbuf.st_ctime;
807  cfst.cf_ino = statbuf.st_ino;
808  cfst.cf_dev = statbuf.st_dev;
809  cfst.cf_readlink = linkbuf;
810 
811  if (cfst.cf_nlink == CF_NOSIZE)
812  {
813  cfst.cf_nlink = statbuf.st_nlink;
814  }
815 
816  /* Is file sparse? */
817  if (statbuf.st_size > ST_NBYTES(statbuf))
818  {
819  cfst.cf_makeholes = 1; /* must have a hole to get checksum right */
820  }
821  else
822  {
823  cfst.cf_makeholes = 0;
824  }
825 
826  memset(sendbuffer, 0, CF_BUFSIZE);
827 
828  /* send as plain text */
829 
830  Log(LOG_LEVEL_DEBUG, "OK: type = %d, mode = %jo, lmode = %jo, "
831  "uid = %ju, gid = %ju, size = %jd, atime=%jd, mtime = %jd",
832  cfst.cf_type, (uintmax_t) cfst.cf_mode, (uintmax_t) cfst.cf_lmode,
833  (uintmax_t) cfst.cf_uid, (uintmax_t) cfst.cf_gid, (intmax_t) cfst.cf_size,
834  (intmax_t) cfst.cf_atime, (intmax_t) cfst.cf_mtime);
835 
836  snprintf(sendbuffer, CF_BUFSIZE,
837  "OK: %d %ju %ju %ju %ju %jd %jd %jd %jd %d %d %d %jd",
838  cfst.cf_type, (uintmax_t) cfst.cf_mode, (uintmax_t) cfst.cf_lmode,
839  (uintmax_t) cfst.cf_uid, (uintmax_t) cfst.cf_gid, (intmax_t) cfst.cf_size,
840  (intmax_t) cfst.cf_atime, (intmax_t) cfst.cf_mtime, (intmax_t) cfst.cf_ctime,
841  cfst.cf_makeholes, cfst.cf_ino, cfst.cf_nlink, (intmax_t) cfst.cf_dev);
842 
843  SendTransaction(conn->conn_info, sendbuffer, 0, CF_DONE);
844 
845  memset(sendbuffer, 0, CF_BUFSIZE);
846 
847  if (cfst.cf_readlink != NULL)
848  {
849  strcpy(sendbuffer, "OK:");
850  strcat(sendbuffer, cfst.cf_readlink);
851  }
852  else
853  {
854  strcpy(sendbuffer, "OK:");
855  }
856 
857  SendTransaction(conn->conn_info, sendbuffer, 0, CF_DONE);
858  return 0;
859 }
860 
861 bool CompareLocalHash(const char *filename, const char digest[EVP_MAX_MD_SIZE + 1],
862  char sendbuffer[CFD_FALSE_SIZE])
863 {
864  nt_static_assert(CFD_FALSE_SIZE == (strlen(CFD_FALSE) + 1));
865  nt_static_assert(strlen(CFD_FALSE) >= strlen(CFD_TRUE));
866  char translated_filename[CF_BUFSIZE] = { 0 };
867  TranslatePath(translated_filename, filename);
868 
869  unsigned char file_digest[EVP_MAX_MD_SIZE + 1] = { 0 };
870  /* TODO connection might timeout if this takes long! */
871  HashFile(translated_filename, file_digest, CF_DEFAULT_DIGEST, false);
872 
873  if (HashesMatch(digest, file_digest, CF_DEFAULT_DIGEST))
874  {
875  strcpy(sendbuffer, CFD_FALSE);
876  Log(LOG_LEVEL_DEBUG, "Hashes matched ok");
877  return true;
878  }
879  else
880  {
881  strcpy(sendbuffer, CFD_TRUE);
882  Log(LOG_LEVEL_DEBUG, "Hashes didn't match");
883  return false;
884  }
885 }
886 
887 void GetServerLiteral(EvalContext *ctx, ServerConnectionState *conn, char *sendbuffer, char *recvbuffer, int encrypted)
888 {
889  char handle[CF_BUFSIZE], out[CF_BUFSIZE];
890  int cipherlen;
891 
892  sscanf(recvbuffer, "VAR %255[^\n]", handle);
893 
894  if (ReturnLiteralData(ctx, handle, out))
895  {
896  memset(sendbuffer, 0, CF_BUFSIZE);
897  snprintf(sendbuffer, CF_BUFSIZE - 1, "%s", out);
898  }
899  else
900  {
901  memset(sendbuffer, 0, CF_BUFSIZE);
902  snprintf(sendbuffer, CF_BUFSIZE - 1, "BAD: Not found");
903  }
904 
905  if (encrypted)
906  {
907  cipherlen = EncryptString(out, sizeof(out),
908  sendbuffer, strlen(sendbuffer) + 1,
909  conn->encryption_type, conn->session_key);
910  SendTransaction(conn->conn_info, out, cipherlen, CF_DONE);
911  }
912  else
913  {
914  SendTransaction(conn->conn_info, sendbuffer, 0, CF_DONE);
915  }
916 }
917 
918 bool GetServerQuery(ServerConnectionState *conn, char *recvbuffer, int encrypt)
919 {
920  char query[CF_BUFSIZE];
921 
922  query[0] = '\0';
923  sscanf(recvbuffer, "QUERY %255[^\n]", query);
924 
925  if (strlen(query) == 0)
926  {
927  return false;
928  }
929 
930  return ReturnQueryData(conn, query, encrypt);
931 }
932 
933 void ReplyServerContext(ServerConnectionState *conn, int encrypted, Item *classes)
934 {
935  char sendbuffer[CF_BUFSIZE - CF_INBAND_OFFSET];
936 
937  size_t ret = ItemList2CSV_bound(classes,
938  sendbuffer, sizeof(sendbuffer), ',');
939  if (ret >= sizeof(sendbuffer))
940  {
941  Log(LOG_LEVEL_ERR, "Overflow: classes don't fit in send buffer");
942  }
943 
944  DeleteItemList(classes);
945 
946  if (encrypted)
947  {
948  char out[CF_BUFSIZE];
949  int cipherlen = EncryptString(out, sizeof(out),
950  sendbuffer, strlen(sendbuffer) + 1,
951  conn->encryption_type, conn->session_key);
952  SendTransaction(conn->conn_info, out, cipherlen, CF_DONE);
953  }
954  else
955  {
956  SendTransaction(conn->conn_info, sendbuffer, 0, CF_DONE);
957  }
958 }
959 
960 int CfOpenDirectory(ServerConnectionState *conn, char *sendbuffer, char *oldDirname)
961 {
962  Dir *dirh;
963  const struct dirent *dirp;
964  int offset;
965  char dirname[CF_BUFSIZE];
966 
967  TranslatePath(dirname, oldDirname);
968 
969  if (!IsAbsoluteFileName(dirname))
970  {
971  strcpy(sendbuffer, "BAD: request to access a non-absolute filename");
972  SendTransaction(conn->conn_info, sendbuffer, 0, CF_DONE);
973  return -1;
974  }
975 
976  if ((dirh = DirOpen(dirname)) == NULL)
977  {
978  Log(LOG_LEVEL_INFO, "Couldn't open directory '%s' (DirOpen:%s)",
979  dirname, GetErrorStr());
980  snprintf(sendbuffer, CF_BUFSIZE, "BAD: cfengine, couldn't open dir %s", dirname);
981  SendTransaction(conn->conn_info, sendbuffer, 0, CF_DONE);
982  return -1;
983  }
984 
985 /* Pack names for transmission */
986 
987  offset = 0;
988  for (dirp = DirRead(dirh); dirp != NULL; dirp = DirRead(dirh))
989  {
990  /* Always leave MAXLINKSIZE bytes for CFD_TERMINATOR. Why??? */
991  if (strlen(dirp->d_name) + 1 + offset >= CF_BUFSIZE - CF_MAXLINKSIZE)
992  {
993  /* Double '\0' indicates end of packet. */
994  sendbuffer[offset] = '\0';
995  SendTransaction(conn->conn_info, sendbuffer, offset + 1, CF_MORE);
996 
997  offset = 0; /* new packet */
998  }
999 
1000  /* TODO fix copying names greater than 256. */
1001  strlcpy(sendbuffer + offset, dirp->d_name, CF_MAXLINKSIZE);
1002  offset += strlen(dirp->d_name) + 1; /* +1 for '\0' */
1003  }
1004 
1005  strcpy(sendbuffer + offset, CFD_TERMINATOR);
1006  offset += strlen(CFD_TERMINATOR) + 1; /* +1 for '\0' */
1007  /* Double '\0' indicates end of packet. */
1008  sendbuffer[offset] = '\0';
1009  SendTransaction(conn->conn_info, sendbuffer, offset + 1, CF_DONE);
1010 
1011  DirClose(dirh);
1012  return 0;
1013 }
1014 
1015 /**************************************************************/
1016 
1017 int CfSecOpenDirectory(ServerConnectionState *conn, char *sendbuffer, char *dirname)
1018 {
1019  Dir *dirh;
1020  const struct dirent *dirp;
1021  int offset, cipherlen;
1022  char out[CF_BUFSIZE];
1023 
1024  if (!IsAbsoluteFileName(dirname))
1025  {
1026  strcpy(sendbuffer, "BAD: request to access a non-absolute filename");
1027  cipherlen = EncryptString(out, sizeof(out),
1028  sendbuffer, strlen(sendbuffer) + 1,
1029  conn->encryption_type, conn->session_key);
1030  SendTransaction(conn->conn_info, out, cipherlen, CF_DONE);
1031  return -1;
1032  }
1033 
1034  if ((dirh = DirOpen(dirname)) == NULL)
1035  {
1036  Log(LOG_LEVEL_VERBOSE, "Couldn't open dir %s", dirname);
1037  snprintf(sendbuffer, CF_BUFSIZE, "BAD: cfengine, couldn't open dir %s", dirname);
1038  cipherlen = EncryptString(out, sizeof(out),
1039  sendbuffer, strlen(sendbuffer) + 1,
1040  conn->encryption_type, conn->session_key);
1041  SendTransaction(conn->conn_info, out, cipherlen, CF_DONE);
1042  return -1;
1043  }
1044 
1045 /* Pack names for transmission */
1046 
1047  memset(sendbuffer, 0, CF_BUFSIZE);
1048 
1049  offset = 0;
1050 
1051  for (dirp = DirRead(dirh); dirp != NULL; dirp = DirRead(dirh))
1052  {
1053  if (strlen(dirp->d_name) + 1 + offset >= CF_BUFSIZE - CF_MAXLINKSIZE)
1054  {
1055  cipherlen = EncryptString(out, sizeof(out),
1056  sendbuffer, offset + 1,
1057  conn->encryption_type, conn->session_key);
1058  SendTransaction(conn->conn_info, out, cipherlen, CF_MORE);
1059  offset = 0;
1060  memset(sendbuffer, 0, CF_BUFSIZE);
1061  memset(out, 0, CF_BUFSIZE);
1062  }
1063 
1064  strlcpy(sendbuffer + offset, dirp->d_name, CF_MAXLINKSIZE);
1065  /* + zero byte separator */
1066  offset += strlen(dirp->d_name) + 1;
1067  }
1068 
1069  strcpy(sendbuffer + offset, CFD_TERMINATOR);
1070 
1071  cipherlen =
1072  EncryptString(out, sizeof(out),
1073  sendbuffer, offset + 2 + strlen(CFD_TERMINATOR),
1074  conn->encryption_type, conn->session_key);
1075  SendTransaction(conn->conn_info, out, cipherlen, CF_DONE);
1076  DirClose(dirh);
1077  return 0;
1078 }
1079 
1080 
1081 /********************* MISC UTILITY FUNCTIONS *************************/
1082 
1083 
1084 /**
1085  * Search and replace occurrences of #find1, #find2, #find3, with
1086  * #repl1, #repl2, #repl3 respectively.
1087  *
1088  * "$(connection.ip)" from "191.168.0.1"
1089  * "$(connection.hostname)" from "blah.cfengine.com",
1090  * "$(connection.key)" from "SHA=asdfghjkl"
1091  *
1092  * @return the output length of #buf, (size_t) -1 if overflow would occur,
1093  * or 0 if no replacement happened and #buf was not touched.
1094  *
1095  * @TODO change the function to more generic interface accepting arbitrary
1096  * find/replace pairs.
1097  */
1098 size_t ReplaceSpecialVariables(char *buf, size_t buf_size,
1099  const char *find1, const char *repl1,
1100  const char *find2, const char *repl2,
1101  const char *find3, const char *repl3)
1102 {
1103  size_t ret = 0;
1104 
1105  if ((find1 != NULL) && (find1[0] != '\0') &&
1106  (repl1 != NULL) && (repl1[0] != '\0'))
1107  {
1108  size_t ret2 = StringReplace(buf, buf_size, find1, repl1);
1109  ret = MAX(ret, ret2); /* size_t is unsigned, thus -1 wins */
1110  }
1111  if ((ret != (size_t) -1) &&
1112  (find2 != NULL) && (find2[0] != '\0') &&
1113  (repl2 != NULL) && (repl2[0] != '\0'))
1114  {
1115  size_t ret2 = StringReplace(buf, buf_size, find2, repl2);
1116  ret = MAX(ret, ret2);
1117  }
1118  if ((ret != (size_t) -1) &&
1119  (find3 != NULL) && (find3[0] != '\0') &&
1120  (repl3 != NULL) && (repl3[0] != '\0'))
1121  {
1122  size_t ret2 = StringReplace(buf, buf_size, find3, repl3);
1123  ret = MAX(ret, ret2);
1124  }
1125 
1126  /* Zero is returned only if all of the above were zero. */
1127  return ret;
1128 }
1129 
1130 
1131 /**
1132  * Remove trailing FILE_SEPARATOR, unless we're referring to root dir: '/' or 'a:\'
1133  */
1134 bool PathRemoveTrailingSlash(char *s, size_t s_len)
1135 {
1136  char *first_separator = strchr(s, FILE_SEPARATOR);
1137 
1138  if (first_separator != NULL &&
1139  s[s_len-1] == FILE_SEPARATOR &&
1140  &s[s_len-1] != first_separator)
1141  {
1142  s[s_len-1] = '\0';
1143  return true;
1144  }
1145 
1146  return false;
1147 }
1148 
1149 /**
1150  * Append a trailing FILE_SEPARATOR if it's not there.
1151  */
1152 bool PathAppendTrailingSlash(char *s, size_t s_len)
1153 {
1154  if (s_len > 0 && s[s_len-1] != FILE_SEPARATOR)
1155  {
1156  s[s_len] = FILE_SEPARATOR;
1157  s[s_len+1] = '\0';
1158  return true;
1159  }
1160 
1161  return false;
1162 }
1163 
1164 /* We use this instead of IsAbsoluteFileName() which also checks for
1165  * quotes. There is no meaning in receiving quoted strings over the
1166  * network. */
1167 static bool PathIsAbsolute(const char *s)
1168 {
1169  bool result = false;
1170 
1171 #if defined(__MINGW32__)
1172  if (isalpha(s[0]) && (s[1] == ':') && (s[2] == FILE_SEPARATOR))
1173  {
1174  result = true; /* A:\ */
1175  }
1176  else /* \\ */
1177  {
1178  result = (s[0] == FILE_SEPARATOR && s[1] == FILE_SEPARATOR);
1179  }
1180 #else
1181  if (s[0] == FILE_SEPARATOR) /* / */
1182  {
1183  result = true;
1184  }
1185 #endif
1186 
1187  return result;
1188 }
1189 
1190 /**
1191  * If #path is relative, expand the first part accorting to #shortcuts, doing
1192  * any replacements of special variables "$(connection.*)" on the way, with
1193  * the provided #ipaddr, #hostname, #key.
1194  *
1195  * @return the length of the new string or 0 if no replace took place. -1 in
1196  * case of overflow.
1197  */
1198 size_t ShortcutsExpand(char *path, size_t path_size,
1199  const StringMap *shortcuts,
1200  const char *ipaddr, const char *hostname,
1201  const char *key)
1202 {
1203  char dst[path_size];
1204  size_t path_len = strlen(path);
1205 
1206  if (path_len == 0)
1207  {
1208  UnexpectedError("ShortcutsExpand: 0 length string!");
1209  return (size_t) -1;
1210  }
1211 
1212  if (!PathIsAbsolute(path))
1213  {
1214  char *separ = strchr(path, FILE_SEPARATOR);
1215  size_t first_part_len;
1216  if (separ != NULL)
1217  {
1218  first_part_len = separ - path;
1219  assert(first_part_len < path_len);
1220  }
1221  else
1222  {
1223  first_part_len = path_len;
1224  }
1225  size_t second_part_len = path_len - first_part_len;
1226 
1227  /* '\0'-terminate first_part, do StringMapGet(), undo '\0'-term */
1228  char separ_char = path[first_part_len];
1229  path[first_part_len] = '\0';
1230  char *replacement = StringMapGet(shortcuts, path);
1231  path[first_part_len] = separ_char;
1232 
1233  /* Either the first_part ends with separator, or its all the string */
1234  assert(separ_char == FILE_SEPARATOR ||
1235  separ_char == '\0');
1236 
1237  if (replacement != NULL) /* we found a shortcut */
1238  {
1239  size_t replacement_len = strlen(replacement);
1240  if (replacement_len + 1 > path_size)
1241  {
1242  goto err_too_long;
1243  }
1244 
1245  /* Replacement path for shortcut was found, but it may contain
1246  * special variables such as $(connection.ip), that we also need
1247  * to expand. */
1248  /* TODO if StrAnyStr(replacement, "$(connection.ip)", "$(connection.hostname)", "$(connection.key)") */
1249  char replacement_expanded[path_size];
1250  memcpy(replacement_expanded, replacement, replacement_len + 1);
1251 
1252  size_t ret =
1253  ReplaceSpecialVariables(replacement_expanded, sizeof(replacement_expanded),
1254  "$(connection.ip)", ipaddr,
1255  "$(connection.hostname)", hostname,
1256  "$(connection.key)", key);
1257 
1258  size_t replacement_expanded_len;
1259  /* (ret == -1) is checked later. */
1260  if (ret == 0) /* No expansion took place */
1261  {
1262  replacement_expanded_len = replacement_len;
1263  }
1264  else
1265  {
1266  replacement_expanded_len = ret;
1267  }
1268 
1269  size_t dst_len = replacement_expanded_len + second_part_len;
1270  if (ret == (size_t) -1 || dst_len + 1 > path_size)
1271  {
1272  goto err_too_long;
1273  }
1274 
1275  /* Assemble final result. */
1276  memcpy(dst, replacement_expanded, replacement_expanded_len);
1277  /* Second part may be empty, then this only copies '\0'. */
1278  memcpy(&dst[replacement_expanded_len], &path[first_part_len],
1279  second_part_len + 1);
1280 
1282  "ShortcutsExpand: Path '%s' became: %s",
1283  path, dst);
1284 
1285  /* Copy back to path. */
1286  memcpy(path, dst, dst_len + 1);
1287  return dst_len;
1288  }
1289  }
1290 
1291  /* No expansion took place, either because path was absolute, or because
1292  * no shortcut was found. */
1293  return 0;
1294 
1295  err_too_long:
1296  Log(LOG_LEVEL_INFO, "Path too long after shortcut expansion!");
1297  return (size_t) -1;
1298 }
1299 
1300 /**
1301  * Canonicalize a path, ensure it is absolute, and resolve all symlinks.
1302  * In detail:
1303  *
1304  * 1. MinGW: Translate to windows-compatible: slashes to FILE_SEPARATOR
1305  * and uppercase to lowercase.
1306  * 2. Ensure the path is absolute.
1307  * 3. Resolve symlinks, resolve '.' and '..' and remove double '/'
1308  * WARNING this will currently fail if file does not exist,
1309  * returning -1 and setting errno==ENOENT!
1310  *
1311  * @note trailing slash is left as is if it's there.
1312  * @note #reqpath is written in place (if success was returned). It is always
1313  * an absolute path.
1314  * @note #reqpath is invalid to be of zero length.
1315  * @note #reqpath_size must be at least PATH_MAX.
1316  *
1317  * @return the length of #reqpath after preprocessing. In case of error
1318  * return (size_t) -1.
1319  */
1320 size_t PreprocessRequestPath(char *reqpath, size_t reqpath_size)
1321 {
1322  errno = 0; /* on return, errno might be set from realpath() */
1323  char dst[reqpath_size];
1324  size_t reqpath_len = strlen(reqpath);
1325 
1326  if (reqpath_len == 0)
1327  {
1328  UnexpectedError("PreprocessRequestPath: 0 length string!");
1329  return (size_t) -1;
1330  }
1331 
1332  /* Translate all slashes to backslashes on Windows so that all the rest
1333  * of work is done using FILE_SEPARATOR. THIS HAS TO BE FIRST. */
1334  #if defined(__MINGW32__)
1335  {
1336  char *p = reqpath;
1337  while ((p = strchr(p, '/')) != NULL)
1338  {
1339  *p = FILE_SEPARATOR;
1340  }
1341  /* Also convert everything to lowercase. */
1342  ToLowerStrInplace(reqpath);
1343  }
1344  #endif
1345 
1346  if (!PathIsAbsolute(reqpath))
1347  {
1348  Log(LOG_LEVEL_INFO, "Relative paths are not allowed: %s", reqpath);
1349  return (size_t) -1;
1350  }
1351 
1352  /* TODO replace realpath with Solaris' resolvepath(), in all
1353  * platforms. That one does not check for existence, just resolves
1354  * symlinks and canonicalises. Ideally we would want the following:
1355  *
1356  * PathResolve(dst, src, dst_size, basedir);
1357  *
1358  * - It prepends basedir if path relative (could be the shortcut)
1359  * - It compresses double '/', '..', '.'
1360  * - It follows each component of the path replacing symlinks
1361  * - errno = ENOENT if path component does not exist, but keeps
1362  * compressing path anyway.
1363  * - Leaves trailing slash as it was passed to it.
1364  * OR appends it depending on last component ISDIR.
1365  */
1366 
1367  assert(sizeof(dst) >= PATH_MAX); /* needed for realpath() */
1368  char *p = realpath(reqpath, dst);
1369  if (p == NULL)
1370  {
1371  /* TODO If path does not exist try to canonicalise only directory. INSECURE?*/
1372  /* if (errno == ENOENT) */
1373  /* { */
1374 
1375  /* } */
1376  struct stat statbuf;
1377  if ((lstat(reqpath, &statbuf) == 0) && S_ISLNK(statbuf.st_mode))
1378  {
1379  Log(LOG_LEVEL_VERBOSE, "Requested file is a dead symbolic link (filename: %s)", reqpath);
1380  strlcpy(dst, reqpath, CF_BUFSIZE);
1381  }
1382  else
1383  {
1385  "Failed to canonicalise filename '%s' (realpath: %s)",
1386  reqpath, GetErrorStr());
1387  return (size_t) -1;
1388  }
1389  }
1390 
1391  size_t dst_len = strlen(dst);
1392 
1393  /* Some realpath()s remove trailing '/' even for dirs! Put it back if
1394  * original request had it. */
1395  if (reqpath[reqpath_len - 1] == FILE_SEPARATOR &&
1396  dst[dst_len - 1] != FILE_SEPARATOR)
1397  {
1398  if (dst_len + 2 > sizeof(dst))
1399  {
1400  Log(LOG_LEVEL_INFO, "Error, path too long: %s", reqpath);
1401  return (size_t) -1;
1402  }
1403 
1404  PathAppendTrailingSlash(dst, dst_len);
1405  dst_len++;
1406  }
1407 
1408  memcpy(reqpath, dst, dst_len + 1);
1409  reqpath_len = dst_len;
1410 
1411  return reqpath_len;
1412 }
1413 
1414 
1415 /**
1416  * Set conn->uid (and conn->sid on Windows).
1417  */
1418 void SetConnIdentity(ServerConnectionState *conn, const char *username)
1419 {
1420  size_t username_len = strlen(username);
1421 
1422  conn->uid = CF_UNKNOWN_OWNER;
1423  conn->username[0] = '\0';
1424 
1425  if (username_len < sizeof(conn->username))
1426  {
1427  memcpy(conn->username, username, username_len + 1);
1428  }
1429 
1430  bool is_root = strcmp(conn->username, "root") == 0;
1431  if (is_root)
1432  {
1433  /* If the remote user identifies himself as root, even on Windows
1434  * cf-serverd must grant access to all files. uid==0 is checked later
1435  * in TranferRights() for that. */
1436  conn->uid = 0;
1437  }
1438 
1439 #ifdef __MINGW32__ /* NT uses security identifier instead of uid */
1440 
1441  if (!NovaWin_UserNameToSid(conn->username, (SID *) conn->sid,
1442  CF_MAXSIDSIZE, !is_root))
1443  {
1444  memset(conn->sid, 0, CF_MAXSIDSIZE); /* is invalid sid - discarded */
1445  }
1446 
1447 #else /* UNIX - common path */
1448 
1449  if (conn->uid == CF_UNKNOWN_OWNER) /* skip looking up UID for root */
1450  {
1451  static pthread_mutex_t pwnam_mtx = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP;
1452  struct passwd *pw = NULL;
1453 
1454  ThreadLock(&pwnam_mtx);
1455  /* TODO Redmine#7643: looking up the UID is expensive and should
1456  * not be needed, since today's agent machine VS hub most probably
1457  * do not share the accounts. */
1458  pw = getpwnam(conn->username);
1459  if (pw != NULL)
1460  {
1461  conn->uid = pw->pw_uid;
1462  }
1463  ThreadUnlock(&pwnam_mtx);
1464  }
1465 
1466 #endif
1467 }
1468 
1469 
1470 static bool CharsetAcceptable(const char *s, size_t s_len)
1471 {
1472  const char *ACCEPT =
1473  "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_:";
1474  size_t acceptable_chars = strspn(s, ACCEPT);
1475  if (s_len == 0)
1476  {
1477  s_len = strlen(s);
1478  }
1479 
1480  if (acceptable_chars < s_len)
1481  {
1483  "llegal character in column %zu of: %s",
1484  acceptable_chars, s);
1485  return false;
1486  }
1487 
1488  return true;
1489 }
1490 
1491 
1492 /**
1493  * @param #args_start is a comma separated list of words, which may be
1494  * prefixed with spaces and suffixed with spaces and other
1495  * words. Example: " asd,fgh,jk blah". In this example the
1496  * list has 3 words, and "blah" is not one of them.
1497  *
1498  * Both #args_start and #args_len are in-out parameters.
1499  * At the end of execution #args_start returns the real start of the list, and
1500  * #args_len the real length.
1501  */
1503  struct acl *acl,
1504  char **args_start, size_t *args_len)
1505 {
1506  char *s;
1507  size_t s_len, skip;
1508 
1509  assert(args_start != NULL);
1510  assert(args_len != NULL);
1511 
1512  /* Give the name s and s_len purely for ease of use. */
1513  s_len = *args_len;
1514  s = *args_start;
1515  /* Skip spaces in the beginning of argument list. */
1516  skip = strspn(s, " \t");
1517  s += skip;
1518 
1519  if (s_len == 0) /* if end was not given, find it */
1520  {
1521  s_len = strcspn(s, " \t");
1522  }
1523  else /* if end was given */
1524  {
1525  s_len = (skip <= s_len) ? (s_len - skip) : 0;
1526  }
1527 
1528  /* Admit, unless any token fails to be authorised. */
1529  bool admit = true;
1530  if (s_len > 0)
1531  {
1532  const char tmp_c = s[s_len];
1533  s[s_len] = '\0';
1534 
1535  /* Iterate over comma-separated list. */
1536 
1537  char *token = &s[0];
1538  while (token < &s[s_len] && admit)
1539  {
1540  char *token_end = strchrnul(token, ',');
1541 
1542  const char tmp_sep = *token_end;
1543  *token_end = '\0';
1544 
1545  if (!CharsetAcceptable(token, 0) ||
1546  !acl_CheckRegex(acl, token,
1547  conn->ipaddr, conn->revdns,
1549  conn->username))
1550  {
1551  Log(LOG_LEVEL_INFO, "Access denied to: %s", token);
1552  admit = false; /* EARLY RETURN */
1553  }
1554 
1555  *token_end = tmp_sep;
1556  token = token_end + 1;
1557  }
1558 
1559  s[s_len] = tmp_c;
1560  }
1561 
1562  *args_start = s;
1563  *args_len = s_len;
1564  return admit;
1565 }
1566 
1567 
1568 /**
1569  * @return #true if the connection should remain open for next requests, or
1570  * #false if the server should actively close it - for example when
1571  * protocol errors have occurred.
1572  */
1573 bool DoExec2(const EvalContext *ctx,
1574  ServerConnectionState *conn,
1575  char *exec_args,
1576  char *sendbuf, size_t sendbuf_size)
1577 {
1578  assert(conn != NULL);
1579 
1580  /* STEP 0: Verify cfruncommand was successfully configured. */
1582  {
1583  Log(LOG_LEVEL_INFO, "EXEC denied due to empty cfruncommand");
1584  RefuseAccess(conn, "EXEC");
1585  return false;
1586  }
1587 
1588  /* STEP 1: Resolve and check permissions of CFRUNCOMMAND's arg0. IT is
1589  * done now and not at configuration time, as the file stat may
1590  * have changed since then. */
1591  {
1592  char arg0[PATH_MAX];
1593  if (CommandArg0_bound(arg0, CFRUNCOMMAND, sizeof(arg0)) == (size_t) -1 ||
1594  PreprocessRequestPath(arg0, sizeof(arg0)) == (size_t) -1)
1595  {
1596  Log(LOG_LEVEL_INFO, "EXEC failed, invalid cfruncommand arg0");
1597  RefuseAccess(conn, "EXEC");
1598  return false;
1599  }
1600 
1601  /* Check body server access_rules, whether arg0 is authorized. */
1602 
1603  /* TODO EXEC should not just use paths_acl access control, but
1604  * specific "exec_path" ACL. Then different command execution could be
1605  * allowed per host, and the host could even set argv[0] in his EXEC
1606  * request, rather than only the arguments. */
1607 
1608  if (acl_CheckPath(paths_acl, arg0,
1609  conn->ipaddr, conn->revdns,
1611  == false)
1612  {
1613  Log(LOG_LEVEL_INFO, "EXEC denied due to ACL for file: %s", arg0);
1614  RefuseAccess(conn, "EXEC");
1615  return false;
1616  }
1617  }
1618 
1619  /* STEP 2: Check body server control "allowusers" */
1620  if (!AllowedUser(conn->username))
1621  {
1622  Log(LOG_LEVEL_INFO, "EXEC denied due to not allowed user: %s",
1623  conn->username);
1624  RefuseAccess(conn, "EXEC");
1625  return false;
1626  }
1627 
1628  /* STEP 3: This matches cf-runagent -s class1,class2 against classes
1629  * set during cf-serverd's policy evaluation. */
1630 
1631  if (!MatchClasses(ctx, conn))
1632  {
1633  snprintf(sendbuf, sendbuf_size,
1634  "EXEC denied due to failed class match (check cf-serverd verbose output)");
1635  Log(LOG_LEVEL_INFO, "%s", sendbuf);
1636  SendTransaction(conn->conn_info, sendbuf, 0, CF_DONE);
1637  return true;
1638  }
1639 
1640 
1641  /* STEP 4: Parse and authorise the EXEC arguments, which will be used as
1642  * arguments to CFRUNCOMMAND. Currently we only accept
1643  * [ -D classlist ] and [ -b bundlesequence ] arguments. */
1644 
1645  char cmdbuf[CF_BUFSIZE] = "";
1646  size_t cmdbuf_len = 0;
1647 
1648  assert(sizeof(CFRUNCOMMAND) <= sizeof(cmdbuf));
1649 
1650  StrCat(cmdbuf, sizeof(cmdbuf), &cmdbuf_len, CFRUNCOMMAND, 0);
1651 
1652  exec_args += strspn(exec_args, " \t"); /* skip spaces */
1653  while (exec_args[0] != '\0')
1654  {
1655  if (strncmp(exec_args, "-D", 2) == 0)
1656  {
1657  exec_args += 2;
1658 
1659  char *classlist = exec_args;
1660  size_t classlist_len = 0;
1661  bool allow = AuthorizeDelimitedArgs(conn, roles_acl,
1662  &classlist, &classlist_len);
1663  if (!allow)
1664  {
1665  snprintf(sendbuf, sendbuf_size,
1666  "EXEC denied role activation (check cf-serverd verbose output)");
1667  Log(LOG_LEVEL_INFO, "%s", sendbuf);
1668  SendTransaction(conn->conn_info, sendbuf, 0, CF_DONE);
1669  return true;
1670  }
1671 
1672  if (classlist_len > 0)
1673  {
1674  /* Append "-D classlist" to cfruncommand. */
1675  StrCat(cmdbuf, sizeof(cmdbuf), &cmdbuf_len,
1676  " -D ", 0);
1677  StrCat(cmdbuf, sizeof(cmdbuf), &cmdbuf_len,
1678  classlist, classlist_len);
1679  }
1680 
1681  exec_args = classlist + classlist_len;
1682  }
1683  else if (strncmp(exec_args, "-b", 2) == 0)
1684  {
1685  exec_args += 2;
1686 
1687  char *bundlesequence = exec_args;
1688  size_t bundlesequence_len = 0;
1689 
1690  bool allow = AuthorizeDelimitedArgs(conn, bundles_acl,
1691  &bundlesequence,
1692  &bundlesequence_len);
1693  if (!allow)
1694  {
1695  snprintf(sendbuf, sendbuf_size,
1696  "EXEC denied bundle activation (check cf-serverd verbose output)");
1697  Log(LOG_LEVEL_INFO, "%s", sendbuf);
1698  SendTransaction(conn->conn_info, sendbuf, 0, CF_DONE);
1699  return true;
1700  }
1701 
1702  if (bundlesequence_len > 0)
1703  {
1704  /* Append "--bundlesequence bundlesequence" to cfruncommand. */
1705  StrCat(cmdbuf, sizeof(cmdbuf), &cmdbuf_len,
1706  " --bundlesequence ", 0);
1707  StrCat(cmdbuf, sizeof(cmdbuf), &cmdbuf_len,
1708  bundlesequence, bundlesequence_len);
1709  }
1710 
1711  exec_args = bundlesequence + bundlesequence_len;
1712  }
1713  else /* disallowed parameter */
1714  {
1715  snprintf(sendbuf, sendbuf_size,
1716  "EXEC denied: invalid arguments: %s",
1717  exec_args);
1718  Log(LOG_LEVEL_INFO, "%s", sendbuf);
1719  SendTransaction(conn->conn_info, sendbuf, 0, CF_DONE);
1720  return true;
1721  }
1722 
1723  exec_args += strspn(exec_args, " \t"); /* skip spaces */
1724  }
1725 
1726  if (cmdbuf_len >= sizeof(cmdbuf))
1727  {
1728  snprintf(sendbuf, sendbuf_size,
1729  "EXEC denied: too long (%zu B) command: %s",
1730  cmdbuf_len, cmdbuf);
1731  Log(LOG_LEVEL_INFO, "%s", sendbuf);
1732  SendTransaction(conn->conn_info, sendbuf, 0, CF_DONE);
1733  return false;
1734  }
1735 
1736  /* STEP 5: RUN CFRUNCOMMAND. */
1737 
1738  snprintf(sendbuf, sendbuf_size,
1739  "cf-serverd executing cfruncommand: %s\n",
1740  cmdbuf);
1741  SendTransaction(conn->conn_info, sendbuf, 0, CF_DONE);
1742  Log(LOG_LEVEL_INFO, "%s", sendbuf);
1743 
1744  FILE *pp = cf_popen(cmdbuf, "r", true);
1745  if (pp == NULL)
1746  {
1747  snprintf(sendbuf, sendbuf_size,
1748  "Unable to run '%s' (pipe: %s)",
1749  cmdbuf, GetErrorStr());
1750  Log(LOG_LEVEL_INFO, "%s", sendbuf);
1751  SendTransaction(conn->conn_info, sendbuf, 0, CF_DONE);
1752  return false;
1753  }
1754 
1755  size_t line_size = CF_BUFSIZE;
1756  char *line = xmalloc(line_size);
1757  while (true)
1758  {
1759  ssize_t res = CfReadLine(&line, &line_size, pp);
1760  if (res == -1)
1761  {
1762  if (!feof(pp))
1763  {
1764  /* Error reading, discard all unconsumed input before
1765  * aborting - linux-specific! */
1766  fflush(pp);
1767  }
1768  break;
1769  }
1770 
1771  /* NOTICE: we can't SendTransaction() overlong strings, and we need to
1772  * prepend and append to the string. */
1773  size_t line_len = strlen(line);
1774  if (line_len >= sendbuf_size - 5)
1775  {
1776  line[sendbuf_size - 5] = '\0';
1777  }
1778 
1779  /* Prefixing output with "> " and postfixing with '\n' is new
1780  * behaviour as of 3.7.0. Prefixing happens to avoid zero-length
1781  * transaction packet. */
1782  /* Old cf-runagent versions do not append a newline, so we must do
1783  * it here. New ones do though, so TODO deprecate. */
1784  xsnprintf(sendbuf, sendbuf_size, "> %s\n", line);
1785  if (SendTransaction(conn->conn_info, sendbuf, 0, CF_DONE) == -1)
1786  {
1788  "Sending failed, aborting EXEC (send: %s)",
1789  GetErrorStr());
1790  break;
1791  }
1792  }
1793  free(line);
1794  int exit_code = cf_pclose(pp);
1795  if (exit_code >= 0)
1796  {
1797  xsnprintf(sendbuf, sendbuf_size, "(exit code: %d)\n", exit_code);
1798  if (SendTransaction(conn->conn_info, sendbuf, 0, CF_DONE) == -1)
1799  {
1800  Log(LOG_LEVEL_INFO, "Failed to send exit code from EXEC agent run");
1801  }
1802  }
1803 
1804  return true;
1805 }
void * xmalloc(size_t size)
Definition: alloc-mini.c:46
bool ReturnQueryData(ServerConnectionState *conn, char *menu, int encrypt)
int ReturnLiteralData(EvalContext *ctx, char *handle, char *ret)
#define CF_CHANGEDSTR1
Definition: cf3.defs.h:136
#define CFD_TERMINATOR
Definition: cf3.defs.h:102
#define CF_CHANGEDSTR2
Definition: cf3.defs.h:137
#define CFD_FALSE_SIZE
Definition: cf3.defs.h:105
#define CFD_TRUE
Definition: cf3.defs.h:103
#define CF_MAXLINKSIZE
Definition: cf3.defs.h:56
#define CF_FAILEDSTR
Definition: cf3.defs.h:135
#define CFD_FALSE
Definition: cf3.defs.h:104
#define CF_UNKNOWN_OWNER
Definition: cf3.defs.h:63
HashMethod CF_DEFAULT_DIGEST
Definition: cf3globals.c:88
void free(void *)
#define CF_DONE
Definition: cfnet.h:45
#define CF_MORE
Definition: cfnet.h:46
#define CF_INBAND_OFFSET
Definition: cfnet.h:51
char * ClassRefToString(const char *ns, const char *name)
Definition: class.c:319
void ClassTableIteratorDestroy(ClassTableIterator *iter)
Definition: class.c:286
Class * ClassTableIteratorNext(ClassTableIterator *iter)
Definition: class.c:248
int SendSocketStream(int sd, const char buffer[4096], int tosend)
Send #tosend bytes from buffer to sd.
Definition: classic.c:135
#define nt_static_assert(x)
SSL * ConnectionInfoSSL(const ConnectionInfo *info)
SSL structure.
int ConnectionInfoSocket(const ConnectionInfo *info)
Connection socket.
ProtocolVersion ConnectionInfoProtocolVersion(const ConnectionInfo *info)
Protocol Version.
size_t CommandArg0_bound(char *dst, const char *src, size_t dst_size)
Definition: conversion.c:848
int EncryptString(char *out, size_t out_size, const char *in, int plainlen, char type, unsigned char *key)
Definition: crypto.c:593
bool NextDB(DBCursor *cursor, char **key, int *ksize, void **value, int *vsize)
Definition: dbm_api.c:601
bool DBCursorDeleteEntry(DBCursor *cursor)
Definition: dbm_api.c:607
bool OpenDB(DBHandle **dbp, dbid id)
Definition: dbm_api.c:441
char * DBIdToPath(dbid id)
Definition: dbm_api.c:171
bool DeleteDBCursor(DBCursor *cursor)
Definition: dbm_api.c:617
void CloseDB(DBHandle *handle)
Definition: dbm_api.c:472
bool NewDBCursor(DBHandle *handle, DBCursor **cursor)
Definition: dbm_api.c:588
@ dbid_state
Definition: dbm_api.h:44
#define CF_BUFSIZE
Definition: definitions.h:50
#define CF_MAXSIDSIZE
Definition: definitions.h:37
const struct dirent * DirRead(Dir *dir)
Definition: unix_dir.c:92
void DirClose(Dir *dir)
Definition: unix_dir.c:118
Dir * DirOpen(const char *dirname)
Definition: unix_dir.c:41
const EVP_CIPHER * CfengineCipher(char type)
void TranslatePath(char *new, const char *old)
ClassTableIterator * EvalContextClassTableIteratorNewGlobal(const EvalContext *ctx, const char *ns, bool is_hard, bool is_soft)
static bool IsDefinedClass(const EvalContext *ctx, const char *context)
Definition: eval_context.h:213
ssize_t CfReadLine(char **buff, size_t *size, FILE *fp)
Works exactly like posix 'getline', EXCEPT it does not include carriage return at the end.
Definition: file_lib.c:1476
bool IsAbsoluteFileName(const char *f)
Definition: file_lib.c:333
int safe_open(const char *pathname, int flags)
Definition: file_lib.c:516
#define FILE_SEPARATOR
Definition: file_lib.h:102
const char * ReadLastNode(const char *str)
Definition: files_names.c:539
int errno
#define NULL
Definition: getopt1.c:56
bool HashesMatch(const unsigned char digest1[EVP_MAX_MD_SIZE+1], const unsigned char digest2[EVP_MAX_MD_SIZE+1], HashMethod type)
Definition: hash.c:594
void HashFile(const char *const filename, unsigned char digest[EVP_MAX_MD_SIZE+1], HashMethod type, bool text_mode)
Definition: hash.c:443
size_t ItemList2CSV_bound(const Item *list, char *buf, size_t buf_size, char separator)
Definition: item_lib.c:680
Item * SplitStringAsItemList(const char *string, char sep)
Definition: item_lib.c:613
void DeleteItemList(Item *item)
Definition: item_lib.c:808
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
const char * KeyPrintableHash(const Key *key)
Printable hash of the key.
Definition: key.c:84
LogLevel LogGetGlobalLevel(void)
Definition: logging.c:581
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_VERBOSE
Definition: logging.h:46
@ LOG_LEVEL_INFO
Definition: logging.h:45
char * StringMapGet(const StringMap *map, const char *key)
Definition: map.c:359
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 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
int cf_pclose(FILE *pp)
Definition: pipes_unix.c:812
FILE * cf_popen(const char *command, const char *type, bool capture_stderr)
Definition: pipes_unix.c:332
#define S_IROTH
Definition: platform.h:950
#define ST_NBYTES(statbuf)
Definition: platform.h:856
int lstat(const char *file_name, struct stat *buf)
#define S_ISDIR(m)
Definition: platform.h:916
#define S_ISBLK(m)
Definition: platform.h:928
#define S_ISSOCK(m)
Definition: platform.h:931
#define S_ISCHR(m)
Definition: platform.h:925
#define S_ISFIFO(m)
Definition: platform.h:922
#define S_ISLNK(m)
Definition: platform.h:919
#define dirent
Definition: platform.h:160
#define S_ISREG(m)
Definition: platform.h:913
#define PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP
Definition: platform.h:427
#define PATH_MAX
Definition: platform.h:176
static bool ProtocolIsKnown(const ProtocolVersion p)
static bool ProtocolIsTLS(const ProtocolVersion p)
ProtocolVersion
static bool ProtocolIsClassic(const ProtocolVersion p)
bool StringMatchFull(const char *regex, const char *str)
Definition: regex.c:106
char CFRUNCOMMAND[1024]
Definition: server.c:81
ServerAccess SERVER_ACCESS
Definition: server.c:79
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)
struct acl * paths_acl
Definition: server_access.c:39
struct acl * roles_acl
Definition: server_access.c:45
struct acl * bundles_acl
Definition: server_access.c:44
static const int CF_NOSIZE
Definition: server_common.c:26
static void FailedTransfer(ConnectionInfo *connection)
Item * ListPersistentClasses()
Definition: server_common.c:96
int StatFile(ServerConnectionState *conn, char *sendbuffer, char *ofilename)
static bool CharsetAcceptable(const char *s, size_t s_len)
static void ReplyNothing(ServerConnectionState *conn)
static bool PathIsAbsolute(const char *s)
bool CompareLocalHash(const char *filename, const char digest[EVP_MAX_MD_SIZE+1], char sendbuffer[sizeof("CFD_FALSE")])
void CfGetFile(ServerFileGetState *args)
bool MatchClasses(const EvalContext *ctx, ServerConnectionState *conn)
void SetConnIdentity(ServerConnectionState *conn, const char *username)
bool PathAppendTrailingSlash(char *s, size_t s_len)
size_t ReplaceSpecialVariables(char *buf, size_t buf_size, const char *find1, const char *repl1, const char *find2, const char *repl2, const char *find3, const char *repl3)
bool DoExec2(const EvalContext *ctx, ServerConnectionState *conn, char *exec_args, char *sendbuf, size_t sendbuf_size)
static void AbortTransfer(ConnectionInfo *connection, char *filename)
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)
bool AllowedUser(char *user)
Definition: server_common.c:84
void CfEncryptGetFile(ServerFileGetState *args)
int CfSecOpenDirectory(ServerConnectionState *conn, char *sendbuffer, char *dirname)
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)
static bool TransferRights(const ServerConnectionState *conn, const char *filename, const struct stat *sb)
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
static bool AuthorizeDelimitedArgs(const ServerConnectionState *conn, struct acl *acl, char **args_start, size_t *args_len)
#define MAX(x, y)
Definition: snprintf.c:500
@ FILE_TYPE_SOCK
Definition: stat_cache.h:39
@ FILE_TYPE_FIFO
Definition: stat_cache.h:36
@ FILE_TYPE_LINK
Definition: stat_cache.h:34
@ FILE_TYPE_CHAR_
Definition: stat_cache.h:38
@ FILE_TYPE_REGULAR
Definition: stat_cache.h:33
@ FILE_TYPE_BLOCK
Definition: stat_cache.h:37
@ FILE_TYPE_DIR
Definition: stat_cache.h:35
char * strchrnul(const char *s, int c)
Definition: strchrnul.c:9
void StrCat(char *dst, size_t dst_size, size_t *dst_len, const char *src, size_t src_len)
Definition: string_lib.c:1494
ssize_t StringReplace(char *buf, size_t buf_size, const char *find, const char *replace)
Definition: string_lib.c:868
void ToLowerStrInplace(char *str)
Definition: string_lib.c:162
#define NULL_OR_EMPTY(str)
Definition: string_lib.h:43
size_t strlcpy(char *dst, const char *src, size_t siz)
Definition: strlcpy.c:34
Definition: class.h:31
char * ns
Definition: class.h:32
char * name
Definition: class.h:33
Definition: unix_dir.c:33
Definition: item_lib.h:33
unsigned int expires
Definition: db_structs.h:143
Item * allowuserlist
Definition: server.h:64
char username[1024]
Definition: server.h:107
char revdns[1025]
Definition: server.h:99
char hostname[1024]
Definition: server.h:113
unsigned char * session_key
Definition: server.h:119
ConnectionInfo * conn_info
Definition: server.h:94
ServerConnectionState * conn
Definition: server.h:129
char * replyfile
Definition: server.h:133
time_t cf_atime
Definition: stat_cache.h:53
mode_t cf_lmode
Definition: stat_cache.h:48
off_t cf_size
Definition: stat_cache.h:52
gid_t cf_gid
Definition: stat_cache.h:51
time_t cf_mtime
Definition: stat_cache.h:54
time_t cf_ctime
Definition: stat_cache.h:55
dev_t cf_dev
Definition: stat_cache.h:61
int cf_ino
Definition: stat_cache.h:60
int cf_nlink
Definition: stat_cache.h:59
char * cf_readlink
Definition: stat_cache.h:57
char cf_makeholes
Definition: stat_cache.h:56
mode_t cf_mode
Definition: stat_cache.h:49
uid_t cf_uid
Definition: stat_cache.h:50
FileType cf_type
Definition: stat_cache.h:47
Definition: map.h:212
const char * TLSErrorString(intmax_t errcode)
Definition: tls_generic.c:87
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