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)  

bootstrap.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 <bootstrap.h>
26 
27 #include <policy_server.h>
28 #include <eval_context.h>
29 #include <files_names.h>
30 #include <scope.h>
31 #include <files_interfaces.h>
32 #include <exec_tools.h>
33 #include <generic_agent.h> // PrintVersionBanner
34 #include <audit.h>
35 #include <logging.h>
36 #include <string_lib.h>
37 #include <files_lib.h>
38 #include <known_dirs.h>
39 #include <addr_lib.h>
40 #include <communication.h>
41 #include <client_code.h>
42 #include <assert.h>
43 #include <crypto.h>
44 #include <openssl/rand.h>
45 #include <encode.h>
46 
47 /*
48 Bootstrapping is a tricky sequence of fragile events. We need to map shakey/IP
49 and identify policy hub IP in a special order to bootstrap the license and agents.
50 
51 During commercial bootstrap:
52 
53  - InitGA (generic-agent) loads the public key
54  - The verifylicense function sets the policy hub but fails to verify license yet
55  as there is no key/IP binding
56  - Policy server gets set in workdir/state/am_policy_hub
57  - The agents gets run and start this all over again, but this time
58  the am_policy_hub is defined and caches the key/IP binding
59  - Now the license has a binding, resolves the policy hub's key and succeeds
60 
61 */
62 
63 #if defined(__CYGWIN__) || defined(__ANDROID__)
64 
65 bool BootstrapAllowed(void)
66 {
67  return true;
68 }
69 
70 #elif !defined(__MINGW32__)
71 
72 bool BootstrapAllowed(void)
73 {
74  return IsPrivileged();
75 }
76 
77 #endif
78 
79 
80 /**
81  * @brief Sets both internal C variables as well as policy sys variables.
82  *
83  * Called at bootstrap and after reading policy_server.dat.
84  * Changes sys.policy_hub and sys.policy_hub_port.
85  * NULL is an acceptable value for new_policy_server. Could happen when an
86  * already bootstrapped server re-parses its policies, and the
87  * policy_server.dat file has been removed. Then this function will be called
88  * with NULL as new_policy_server, and cf-serverd will keep running even
89  * without a policy server set.
90  *
91  * @param ctx EvalContext is used to set related variables
92  * @param new_policy_server can be 'host:port', same as policy_server.dat
93  */
94 void EvalContextSetPolicyServer(EvalContext *ctx, const char *new_policy_server)
95 {
96  // Remove variables if undefined policy server:
97  if ( NULL_OR_EMPTY(new_policy_server) )
98  {
100  "policy_hub" );
102  "policy_hub_port" );
103  return;
104  }
105 
106  PolicyServerSet(new_policy_server);
107  const char *ip = PolicyServerGetIP();
108 
109  // Set the sys.policy_hub variable:
110  if ( ip != NULL )
111  {
113  "policy_hub", ip,
115  "source=bootstrap" );
116  }
117  else
118  {
120  "policy_hub" );
121  }
122 
123  // Set the sys.policy_hub_port variable:
124  if (PolicyServerGetPort() != NULL)
125  {
127  "policy_hub_port", PolicyServerGetPort(),
129  "source=bootstrap" );
130  }
131  else // Default value (CFENGINE_PORT_STR = "5308") is set
132  {
134  "policy_hub_port",
137  "source=bootstrap" );
138  }
139 }
140 
141 //*******************************************************************
142 // POLICY SERVER FILE FUNCTIONS:
143 //*******************************************************************
144 
145 /**
146  * @brief Reads the policy_server.dat and sets the internal variables.
147  * @param[in] ctx EvalContext used by EvalContextSetPolicyServer()
148  * @param[in] workdir the directory of policy_server.dat usually GetWorkDir()
149  */
150  void EvalContextSetPolicyServerFromFile(EvalContext *ctx, const char *workdir)
151  {
152  char *contents = PolicyServerReadFile(workdir);
153  EvalContextSetPolicyServer(ctx, contents);
154  free(contents);
155  }
156 
157 
158 
159 //*******************************************************************
160 // POLICY HUB FUNCTIONS:
161 //*******************************************************************
162 
163 /**
164  * @brief Updates sys.last_policy_update variable from $(sys.masterdir)/cf_promises_validated
165  * @param ctx EvalContext to put variable into
166  */
168 {
169  // Get the timestamp on policy update
170  struct stat sb;
171  {
172  char cf_promises_validated_filename[CF_MAXVARSIZE];
173  snprintf(cf_promises_validated_filename, CF_MAXVARSIZE, "%s/cf_promises_validated", GetMasterDir());
174  MapName(cf_promises_validated_filename);
175 
176  if ((stat(cf_promises_validated_filename, &sb)) != 0)
177  {
178  return;
179  }
180  }
181 
182  char timebuf[26] = { 0 };
183  cf_strtimestamp_local(sb.st_mtime, timebuf);
184 
185  EvalContextVariablePutSpecial(ctx, SPECIAL_SCOPE_SYS, "last_policy_update", timebuf, CF_DATA_TYPE_STRING, "source=agent");
186 }
187 
188 /**
189  * @return True if the file STATEDIR/am_policy_hub exists
190  */
191  bool GetAmPolicyHub(void)
192  {
193  char path[CF_BUFSIZE] = { 0 };
194  snprintf(path, sizeof(path), "%s/am_policy_hub", GetStateDir());
195  MapName(path);
196 
197  struct stat sb;
198  return stat(path, &sb) == 0;
199  }
200 
201 /**
202  * @brief Set the STATEDIR/am_policy_hub marker file.
203  * @param am_policy_hub If true, create marker file. If false, delete it.
204  * @return True if successful
205  */
206 static char *AmPolicyHubFilename(void)
207 {
208  return StringFormat("%s%cam_policy_hub", GetStateDir(), FILE_SEPARATOR);
209 }
210 
211 bool WriteAmPolicyHubFile(bool am_policy_hub)
212 {
213  char *filename = AmPolicyHubFilename();
214  if (am_policy_hub)
215  {
216  if (!GetAmPolicyHub())
217  {
218  if (creat(filename, 0600) == -1)
219  {
220  Log(LOG_LEVEL_ERR, "Error writing marker file '%s'", filename);
221  free(filename);
222  return false;
223  }
224  }
225  }
226  else
227  {
228  if (GetAmPolicyHub())
229  {
230  if (unlink(filename) != 0)
231  {
232  Log(LOG_LEVEL_ERR, "Error removing marker file '%s'", filename);
233  free(filename);
234  return false;
235  }
236  }
237  }
238  free(filename);
239  return true;
240 }
241 
242 
243 //*******************************************************************
244 // FAILSAFE FUNCTIONS:
245 //*******************************************************************
246 
247 /**
248  * @brief Write the builtin failsafe policy to the default location
249  * @return True if successful
250  */
251 bool WriteBuiltinFailsafePolicy(const char *inputdir)
252 {
253  char failsafe_path[CF_BUFSIZE];
254  snprintf(failsafe_path, CF_BUFSIZE - 1, "%s/failsafe.cf", inputdir);
255  MapName(failsafe_path);
256 
257  return WriteBuiltinFailsafePolicyToPath(failsafe_path);
258 }
259 
260 /**
261  * @brief Exposed for testing. Use WriteBuiltinFailsafePolicy.
262  */
263 bool WriteBuiltinFailsafePolicyToPath(const char *filename)
264 {
265  // The bootstrap.inc file is generated by "make bootstrap-inc"
266  const char *bootstrap_content =
267 #include "bootstrap.inc"
268 ;
269 
270  Log(LOG_LEVEL_INFO, "Writing built-in failsafe policy to '%s'", filename);
271 
272  FILE *fout = safe_fopen(filename, "w");
273  if (!fout)
274  {
275  Log(LOG_LEVEL_ERR, "Unable to write failsafe to '%s' (fopen: %s)", filename, GetErrorStr());
276  return false;
277  }
278 
279  fputs(bootstrap_content, fout);
280  fclose(fout);
281 
282  return true;
283 }
284 
285 
286 //*******************************************************************
287 // POLICY FILE FUNCTIONS:
288 //*******************************************************************
289 
290 /**
291  * @brief Removes all files in $(sys.inputdir)
292  * @param inputdir
293  * @return True if successful
294  */
295 bool RemoveAllExistingPolicyInInputs(const char *inputs_path)
296 {
297  Log(LOG_LEVEL_INFO, "Removing all files in '%s'", inputs_path);
298 
299  struct stat sb;
300  if (stat(inputs_path, &sb) == -1)
301  {
302  if (errno == ENOENT)
303  {
304  return true;
305  }
306  else
307  {
308  Log(LOG_LEVEL_ERR, "Could not stat inputs directory at '%s'. (stat: %s)", inputs_path, GetErrorStr());
309  return false;
310  }
311  }
312 
313  if (!S_ISDIR(sb.st_mode))
314  {
315  Log(LOG_LEVEL_ERR, "Inputs path exists at '%s', but it is not a directory", inputs_path);
316  return false;
317  }
318 
319  return DeleteDirectoryTree(inputs_path);
320 }
321 
322 /**
323  * @return True if the file $(sys.masterdir)/promises.cf exists
324  */
325 bool MasterfileExists(const char *masterdir)
326 {
327  char filename[CF_BUFSIZE] = { 0 };
328  snprintf(filename, sizeof(filename), "%s/promises.cf", masterdir);
329  MapName(filename);
330 
331  struct stat sb;
332  if (stat(filename, &sb) == -1)
333  {
334  if (errno == ENOENT)
335  {
336  return false;
337  }
338  else
339  {
340  Log(LOG_LEVEL_ERR, "Could not stat file '%s'. (stat: %s)", filename, GetErrorStr());
341  return false;
342  }
343  }
344 
345  if (!S_ISREG(sb.st_mode))
346  {
347  Log(LOG_LEVEL_ERR, "Path exists at '%s', but it is not a regular file", filename);
348  return false;
349  }
350 
351  return true;
352 }
353 
354 static char *BootstrapIDFilename(const char *workdir)
355 {
356  assert(workdir != NULL);
357  return StringFormat("%s%cbootstrap_id.dat", workdir, FILE_SEPARATOR);
358 }
359 
360 char *CreateBootstrapIDFile(const char *workdir)
361 {
362  assert(workdir != NULL);
363  char *filename = BootstrapIDFilename(workdir);
364 
365  FILE *file = safe_fopen_create_perms(filename, "w", CF_PERMS_DEFAULT);
366  if (file == NULL)
367  {
368  Log(LOG_LEVEL_ERR, "Unable to write bootstrap id file '%s' (fopen: %s)", filename, GetErrorStr());
369  free(filename);
370  return NULL;
371  }
373  #define RANDOM_BYTES 240 / 8 // 240 avoids padding (divisible by 6)
374  #define BASE_64_LENGTH_NO_PADDING (4 * (RANDOM_BYTES / 3))
375  unsigned char buf[RANDOM_BYTES];
376  RAND_bytes(buf, RANDOM_BYTES);
377  char *b64_id = StringEncodeBase64(buf, RANDOM_BYTES);
378  fprintf(file, "%s\n", b64_id);
379  fclose(file);
380 
381  free(filename);
382  return b64_id;
383 }
384 
385 char *ReadBootstrapIDFile(const char *workdir)
386 {
387  assert(workdir != NULL);
388 
389  char *const path = BootstrapIDFilename(workdir);
390  Writer *writer = FileRead(path, BASE_64_LENGTH_NO_PADDING + 1, NULL);
391  if (writer == NULL)
392  {
393  // Not having a bootstrap id file is considered normal
395  "Could not read bootstrap ID from file: '%s'",
396  path);
397  free(path);
398  return NULL;
399  }
400  char *data = StringWriterClose(writer);
401 
402  size_t data_length = strlen(data);
403  assert(data_length == BASE_64_LENGTH_NO_PADDING + 1);
404  assert(data[data_length - 1] == '\n');
405  if (data_length != BASE_64_LENGTH_NO_PADDING + 1)
406  {
407  Log(LOG_LEVEL_ERR, "'%s' contains invalid data: '%s'", path, data);
408  free(path);
409  free(data);
410  return NULL;
411  }
412 
413  data[data_length - 1] = '\0';
414 
416  "Successfully read bootstrap ID '%s' from file '%s'",
417  data,
418  path);
419  free(path);
420  return data;
421 }
422 
423 void EvalContextSetBootstrapID(EvalContext *ctx, char *bootstrap_id)
424 {
426  ctx,
428  "bootstrap_id",
429  bootstrap_id,
431  "source=bootstrap");
432 }
void UpdateLastPolicyUpdateTime(EvalContext *ctx)
Updates sys.last_policy_update variable from $(sys.masterdir)/cf_promises_validated.
Definition: bootstrap.c:167
void EvalContextSetPolicyServer(EvalContext *ctx, const char *new_policy_server)
Sets both internal C variables as well as policy sys variables.
Definition: bootstrap.c:94
static char * AmPolicyHubFilename(void)
Set the STATEDIR/am_policy_hub marker file.
Definition: bootstrap.c:206
#define BASE_64_LENGTH_NO_PADDING
void EvalContextSetPolicyServerFromFile(EvalContext *ctx, const char *workdir)
Reads the policy_server.dat and sets the internal variables.
Definition: bootstrap.c:150
char * CreateBootstrapIDFile(const char *workdir)
Definition: bootstrap.c:360
bool BootstrapAllowed(void)
Definition: bootstrap.c:72
bool WriteBuiltinFailsafePolicyToPath(const char *filename)
Exposed for testing. Use WriteBuiltinFailsafePolicy.
Definition: bootstrap.c:263
bool MasterfileExists(const char *masterdir)
Definition: bootstrap.c:325
static char * BootstrapIDFilename(const char *workdir)
Definition: bootstrap.c:354
bool WriteBuiltinFailsafePolicy(const char *inputdir)
Write the builtin failsafe policy to the default location.
Definition: bootstrap.c:251
bool RemoveAllExistingPolicyInInputs(const char *inputs_path)
Removes all files in $(sys.inputdir)
Definition: bootstrap.c:295
char * ReadBootstrapIDFile(const char *workdir)
Definition: bootstrap.c:385
bool WriteAmPolicyHubFile(bool am_policy_hub)
Definition: bootstrap.c:211
#define RANDOM_BYTES
void EvalContextSetBootstrapID(EvalContext *ctx, char *bootstrap_id)
Definition: bootstrap.c:423
bool GetAmPolicyHub(void)
Definition: bootstrap.c:191
@ CF_DATA_TYPE_STRING
Definition: cf3.defs.h:369
void free(void *)
char CFENGINE_PORT_STR[16]
Definition: client_code.c:78
void CryptoInitialize()
Definition: crypto.c:71
#define CF_PERMS_DEFAULT
Definition: definitions.h:58
#define CF_BUFSIZE
Definition: definitions.h:50
#define CF_MAXVARSIZE
Definition: definitions.h:36
char * StringEncodeBase64(const char *str, size_t len)
Definition: encode.c:36
bool EvalContextVariableRemoveSpecial(const EvalContext *ctx, SpecialScope scope, const char *lval)
bool EvalContextVariablePutSpecial(EvalContext *ctx, SpecialScope scope, const char *lval, const void *value, DataType type, const char *tags)
bool DeleteDirectoryTree(const char *path)
Deletes directory path recursively. Symlinks are not followed. Note that this function only deletes t...
Definition: file_lib.c:1304
FILE * safe_fopen_create_perms(const char *const path, const char *const mode, const mode_t create_perms)
Definition: file_lib.c:832
Writer * FileRead(const char *filename, size_t max_size, bool *truncated)
Definition: file_lib.c:63
FILE * safe_fopen(const char *const path, const char *const mode)
Definition: file_lib.c:812
char * MapName(char *s)
Definition: file_lib.c:441
#define FILE_SEPARATOR
Definition: file_lib.h:102
int errno
#define NULL
Definition: getopt1.c:56
const char * GetStateDir(void)
Definition: known_dirs.c:186
const char * GetMasterDir(void)
Definition: known_dirs.c:184
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
bool IsPrivileged()
Definition: patches.c:105
#define S_ISDIR(m)
Definition: platform.h:916
#define S_ISREG(m)
Definition: platform.h:913
char * PolicyServerReadFile(const char *workdir)
Reads the policy_server.dat file.
void PolicyServerSet(const char *new_policy_server)
Sets both internal C variables as well as policy sys variables.
Definition: policy_server.c:77
const char * PolicyServerGetPort()
Gets the port part of the policy server.
const char * PolicyServerGetIP()
Gets the IP address of policy server, does lookup if necessary.
Access to Policy Server IP Address, hostname and port number.
char * cf_strtimestamp_local(const time_t time, char *buf)
Definition: patches.c:128
@ SPECIAL_SCOPE_SYS
Definition: scope.h:38
char * StringFormat(const char *fmt,...)
Format string like sprintf and return formatted string allocated on heap as a return value.
Definition: string_lib.c:51
#define NULL_OR_EMPTY(str)
Definition: string_lib.h:43
Definition: writer.c:45
char * StringWriterClose(Writer *writer)
Definition: writer.c:262