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)  

cf-key-functions.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 <platform.h>
26 #include <cf-key-functions.h>
27 
28 #include <openssl/bn.h> /* BN_*, BIGNUM */
29 #include <openssl/rand.h> /* RAND_* */
30 #include <libcrypto-compat.h>
31 
32 #include <lastseen.h>
33 #include <dir.h>
34 #include <scope.h>
35 #include <files_copy.h>
36 #include <files_interfaces.h>
37 #include <hash.h>
38 #include <keyring.h>
39 #include <communication.h>
40 #include <eval_context.h>
41 #include <crypto.h>
42 #include <file_lib.h>
43 #include <known_dirs.h>
44 
45 
46 /*****************************************************************************/
47 
48 /** Print digest of the specified public key file.
49  Return 0 on success and 1 on error. */
50 int PrintDigest(const char *pubkey)
51 {
52  char *digeststr = LoadPubkeyDigest(pubkey);
53 
54  if (digeststr == NULL)
55  {
56  return 1; /* ERROR exitcode */
57  }
58 
59  fprintf(stdout, "%s\n", digeststr);
60  free(digeststr);
61  return 0; /* OK exitcode */
62 }
63 
64 /**
65  * Split a "key" argument of the form "[[user@]address:]filename" into
66  * components (public) key file name, IP address, and (remote) user
67  * name. Pointers to the corresponding segments of the #keyarg
68  * string will be written into the three output arguments #filename,
69  * #ipaddr, and #username. (Hence, the three output string have
70  * the same lifetime/scope as the #keyarg string.)
71  *
72  * The only required component is the file name. If IP address is
73  * missing, NULL is written into the #ipaddr pointer. If the
74  * username is missing, #username will point to the constant string
75  * "root".
76  *
77  * @NOTE the #keyarg argument is modified by this function!
78  */
79 void ParseKeyArg(char *keyarg, char **filename, char **ipaddr, char **username)
80 {
81  char *s;
82 
83  /* set defaults */
84  *ipaddr = NULL;
85  *username = "root";
86 
87  /* use rightmost colon so we can cope with IPv6 addresses */
88  s = strrchr(keyarg, ':');
89  if (s == NULL)
90  {
91  /* no colon, entire argument is a filename */
92  *filename = keyarg;
93  return;
94  }
95 
96  *s = '\0'; /* split string */
97  *filename = s + 1; /* filename starts at 1st character after ':' */
98 
99  s = strchr(keyarg, '@');
100  if (s == NULL)
101  {
102  /* no username given, use default */
103  *ipaddr = keyarg;
104  return;
105  }
106 
107  *s = '\0';
108  *ipaddr = s + 1;
109  *username = keyarg;
110 
111  /* special case: if we got user@:/path/to/file
112  then reset ipaddr to NULL instead of empty string */
113  if (**ipaddr == '\0')
114  {
115  *ipaddr = NULL;
116  }
117 
118  return;
119 }
120 
121 extern bool cf_key_interrupted;
122 
123 #define HOST_FMT_TRUNCATE "%-10.10s %-40.40s %-25.25s %-26.26s %-s\n"
124 #define HOST_FMT_NO_TRUNCATE "%s\t%s\t%s\t%s\t%s\n"
125 
126 typedef struct _HostPrintState
127 {
128  int count;
129  bool truncate;
131 
132 static bool ShowHost(
133  const char *const hostkey,
134  const char *const address,
135  bool incoming,
136  const KeyHostSeen *const quality,
137  void *const ctx)
138 {
139  HostPrintState *const state = ctx;
140  char hostname[NI_MAXHOST];
141  if (LOOKUP_HOSTS)
142  {
143  int ret = IPString2Hostname(hostname, address, sizeof(hostname));
144  if (ret == -1)
145  {
146  strcpy(hostname, "-");
147  }
148  }
149  else
150  {
151  strlcpy(hostname, address, sizeof(hostname));
152  }
153  ++(state->count);
154 
155  bool truncate = state->truncate;
156  char timebuf[26];
157  printf(truncate ? HOST_FMT_TRUNCATE : HOST_FMT_NO_TRUNCATE,
158  incoming ? "Incoming" : "Outgoing",
159  address, hostname,
160  cf_strtimestamp_local(quality->lastseen, timebuf), hostkey);
161 
162  return !cf_key_interrupted;
163 }
164 
165 void ShowLastSeenHosts(bool truncate)
166 {
167  HostPrintState state = { 0 };
168  state.count = 0;
169  state.truncate = truncate;
170 
171  printf(
173  "Direction",
174  "IP",
175  "Name",
176  "Last connection",
177  "Key");
178 
180  {
181  Log(LOG_LEVEL_ERR, "Unable to show lastseen database");
182  return;
183  }
184 
185  printf("Total Entries: %d\n", state.count);
186 }
187 
188 /**
189  * @brief removes all traces of entry 'input' from lastseen and filesystem
190  *
191  * @param[in] key digest (SHA/MD5 format) or free host name string
192  * @param[in] must_be_coherent. false : delete if lastseen is incoherent,
193  * true : don't if lastseen is incoherent
194  * @retval 0 if entry was deleted, >0 otherwise
195  */
196 int RemoveKeys(const char *input, bool must_be_coherent)
197 {
198  int res = 0;
199  char equivalent[CF_BUFSIZE];
200  equivalent[0] = '\0';
201 
202  res = RemoveKeysFromLastSeen(input, must_be_coherent, equivalent, sizeof(equivalent));
203  if (res!=0)
204  {
205  return res;
206  }
207 
208  Log(LOG_LEVEL_INFO, "Removed corresponding entries from lastseen database.");
209 
210  int removed_input = RemovePublicKey(input);
211  int removed_equivalent = RemovePublicKey(equivalent);
212 
213  if ((removed_input == -1) || (removed_equivalent == -1))
214  {
215  Log(LOG_LEVEL_ERR, "Last seen database: unable to remove keys for the entry '%s'", input);
216  return 255;
217  }
218  else if (removed_input + removed_equivalent == 0)
219  {
220  Log(LOG_LEVEL_ERR, "No key file(s) for entry '%s' were found on the filesystem", input);
221  return 1;
222  }
223  else
224  {
225  Log(LOG_LEVEL_INFO, "Removed %d corresponding key file(s) from filesystem.",
226  removed_input + removed_equivalent);
227  return 0;
228  }
229 
230  return -1;
231 }
232 
233 static bool KeepKeyPromisesRSA(RSA *pair, const char *public_key_file, const char *private_key_file)
234 {
235  FILE *fp = safe_fopen_create_perms(private_key_file, "w", 0600);
236  if (fp == NULL)
237  {
239  "Error while writing private key file '%s' (fopen: %s)",
240  private_key_file, GetErrorStr());
241  return false;
242  }
243 
244  Log(LOG_LEVEL_VERBOSE, "Writing private key to '%s'", private_key_file);
245 
246  int res = PEM_write_RSAPrivateKey(fp, pair, NULL, NULL, 0, NULL, NULL);
247  fclose(fp);
248 
249  if (res == 0)
250  {
252  "Couldn't write private key. (PEM_write_RSAPrivateKey: %s)",
254  return false;
255  }
256 
257  fp = safe_fopen_create_perms(public_key_file, "w", 0600);
258  if (fp == NULL)
259  {
261  "Error while writing public key file '%s' (fopen: %s)",
262  public_key_file, GetErrorStr());
263  return false;
264  }
265 
266  Log(LOG_LEVEL_VERBOSE, "Writing public key to file '%s'", public_key_file);
267 
268  if (!PEM_write_RSAPublicKey(fp, pair))
269  {
271  "Unable to write public key. (PEM_write_RSAPublicKey: %s)",
273  return false;
274  }
275 
276  fclose(fp);
277 
278  char vbuff[CF_BUFSIZE];
279  snprintf(vbuff, CF_BUFSIZE, "%s%crandseed", GetWorkDir(), FILE_SEPARATOR);
280  Log(LOG_LEVEL_VERBOSE, "Using '%s' for randseed", vbuff);
281 
282  if (RAND_write_file(vbuff) != 1024)
283  {
284  Log(LOG_LEVEL_ERR, "Unable to write randseed");
285  unlink(vbuff); /* randseed isn't safe to use */
286  return false;
287  }
288 
289  if (chmod(vbuff, 0600) != 0)
290  {
292  "Unable to set permissions on '%s' (chmod: %s)",
293  vbuff, GetErrorStr());
294  return false;
295  }
296 
297  return true;
298 }
299 
300 bool KeepKeyPromises(const char *public_key_file, const char *private_key_file, const int key_size)
301 {
302  struct stat statbuf;
303 
304  if (stat(public_key_file, &statbuf) != -1)
305  {
306  Log(LOG_LEVEL_ERR, "A key file already exists at %s", public_key_file);
307  return false;
308  }
309 
310  if (stat(private_key_file, &statbuf) != -1)
311  {
312  Log(LOG_LEVEL_ERR, "A key file already exists at %s", private_key_file);
313  return false;
314  }
315 
316  Log(LOG_LEVEL_INFO, "Making a key pair for CFEngine, please wait, this could take a minute...");
317 
318 #ifdef OPENSSL_NO_DEPRECATED
319  RSA *pair = RSA_new();
320  BIGNUM *rsa_bignum = BN_new();
321  if (pair != NULL && rsa_bignum != NULL)
322  {
323  BN_set_word(rsa_bignum, RSA_F4);
324  int res = RSA_generate_key_ex(pair, key_size, rsa_bignum, NULL);
325  if (res == 0)
326  {
327  DESTROY_AND_NULL(RSA_free, pair); // pair = NULL
328  }
329  }
330  else
331  {
332  DESTROY_AND_NULL(RSA_free, pair); // pair = NULL
333  }
334 
335  BN_clear_free(rsa_bignum);
336 
337 #else
338  RSA *pair = RSA_generate_key(key_size, 65537, NULL, NULL);
339 
340 #endif
341  if (pair == NULL)
342  {
343  Log(LOG_LEVEL_ERR, "Unable to generate cryptographic key (RSA_generate_key: %s)",
345  return false;
346  }
347  bool ret = KeepKeyPromisesRSA(pair, public_key_file, private_key_file);
348  RSA_free(pair);
349  return ret;
350 }
351 
352 
354 {
355  Log(LOG_LEVEL_ERR, "License installation only applies to CFEngine Enterprise");
356 
357  return false;
358 }
359 
360 int ForceKeyRemoval(const char *hash)
361 {
362 /**
363  Removal of a key hash is made of two passes :
364  Pass #1 (read-only)
365  -> fetches the IP addresses directly linked to the hash key
366  Pass #2 (made of deletes)
367  -> remove all the IP addresses in the previous list
368  -> remove the physical key.pub from the filesystem
369 
370  WARNING: Please backup your lastseen database before calling this
371  function in the case where a 1-to-1 relatioship between
372  the IP and a single keyhash does not exist
373 **/
374  CF_DB *dbp;
375  CF_DBC *dbcp;
376  char *key;
377  void *value;
378  int ksize, vsize;
379 
380  Seq *hostips = SeqNew(100, free);
381  if (OpenDB(&dbp, dbid_lastseen))
382  {
383  if (NewDBCursor(dbp, &dbcp))
384  {
385  while (NextDB(dbcp, &key, &ksize, &value, &vsize))
386  {
387  if ((key[0] != 'a') || (value == NULL))
388  {
389  continue;
390  }
391  if (!strncmp(hash, value, strlen(hash)))
392  {
393  SeqAppend(hostips, xstrdup(key + 1));
394  }
395  }
396  DeleteDBCursor(dbcp);
397  }
398  CloseDB(dbp);
399  }
400  if (OpenDB(&dbp, dbid_lastseen))
401  {
402  char tmp[CF_BUFSIZE];
403  snprintf(tmp, CF_BUFSIZE, "k%s", hash);
404  char vtmp[CF_BUFSIZE];
405  if (!ReadDB(dbp, tmp, &vtmp, sizeof(vtmp)))
406  {
407  Log(LOG_LEVEL_ERR, "Failed to read the main hash key entry '%s'. Will continue to purge other entries related to it.", hash);
408  }
409  else
410  {
411  SeqAppend(hostips, xstrdup(vtmp + 1));
412  }
413  snprintf(tmp, CF_BUFSIZE, "k%s", hash);
414  DeleteDB(dbp, tmp);
415  snprintf(tmp, CF_BUFSIZE, "qi%s", hash);
416  DeleteDB(dbp, tmp);
417  snprintf(tmp, CF_BUFSIZE, "qo%s", hash);
418  DeleteDB(dbp, tmp);
419  RemovePublicKey(hash);
420  for (int i = 0; i < SeqLength(hostips); ++i)
421  {
422  const char *myip = SeqAt(hostips, i);
423  snprintf(tmp, CF_BUFSIZE, "a%s", myip);
424  DeleteDB(dbp, tmp);
425  }
426  CloseDB(dbp);
427  }
428  SeqDestroy(hostips);
429  return 0;
430 }
431 
432 int ForceIpAddressRemoval(const char *ip)
433 {
434 /**
435  Removal of an ip is made of two passes :
436  Pass #1 (read-only)
437  -> fetches the key hashes directly linked to the ip address
438  Pass #2 (made of deletes)
439  -> remove all the key hashes in the previous list
440  -> remove the physical key hashes .pub files from the filesystem
441 
442  WARNING: Please backup your lastseen database before calling this
443  function in the case where a 1-to-1 relatioship between
444  the IP and a single keyhash does not exist
445 **/
446  CF_DB *dbp;
447  CF_DBC *dbcp;
448  char *key;
449  void *value;
450  int ksize, vsize;
451 
452  Seq *hostkeys = SeqNew(100, free);
453  if (OpenDB(&dbp, dbid_lastseen))
454  {
455  if (NewDBCursor(dbp, &dbcp))
456  {
457  while (NextDB(dbcp, &key, &ksize, &value, &vsize))
458  {
459  if ((key[0] != 'k') || (value == NULL))
460  {
461  continue;
462  }
463  if (!strncmp(ip, value, strlen(ip)))
464  {
465  SeqAppend(hostkeys, xstrdup(key + 1));
466  }
467  }
468  DeleteDBCursor(dbcp);
469  }
470  CloseDB(dbp);
471  }
472  if (OpenDB(&dbp, dbid_lastseen))
473  {
474  char tmp[CF_BUFSIZE];
475  snprintf(tmp, CF_BUFSIZE, "a%s", ip);
476  char vtmp[CF_BUFSIZE];
477  if (!ReadDB(dbp, tmp, &vtmp, sizeof(vtmp)))
478  {
479  Log(LOG_LEVEL_ERR, "Failed to read the main ip address entry '%s'. Will continue to purge other entries related to it.", ip);
480  }
481  else
482  {
483  SeqAppend(hostkeys, xstrdup(vtmp + 1));
484  }
485  snprintf(tmp, CF_BUFSIZE, "a%s", ip);
486  DeleteDB(dbp, tmp);
487  for (int i = 0; i < SeqLength(hostkeys); ++i)
488  {
489  const char *myk = SeqAt(hostkeys, i);
490  snprintf(tmp, CF_BUFSIZE, "k%s", myk);
491  DeleteDB(dbp, tmp);
492  snprintf(tmp, CF_BUFSIZE, "qi%s", myk);
493  DeleteDB(dbp, tmp);
494  snprintf(tmp, CF_BUFSIZE, "qo%s", myk);
495  DeleteDB(dbp, tmp);
496  RemovePublicKey(myk);
497  }
498  CloseDB(dbp);
499  }
500  SeqDestroy(hostkeys);
501  return 0;
502 }
char * xstrdup(const char *str)
Definition: alloc-mini.c:56
#define DESTROY_AND_NULL(destroy, ptr)
Definition: alloc.h:42
int ForceKeyRemoval(const char *hash)
bool cf_key_interrupted
Definition: cf-key.c:121
static bool KeepKeyPromisesRSA(RSA *pair, const char *public_key_file, const char *private_key_file)
int PrintDigest(const char *pubkey)
#define HOST_FMT_TRUNCATE
void ShowLastSeenHosts(bool truncate)
void ParseKeyArg(char *keyarg, char **filename, char **ipaddr, char **username)
static bool ShowHost(const char *const hostkey, const char *const address, bool incoming, const KeyHostSeen *const quality, void *const ctx)
struct _HostPrintState HostPrintState
#define HOST_FMT_NO_TRUNCATE
bool LicenseInstall(char *path_source)
int ForceIpAddressRemoval(const char *ip)
bool KeepKeyPromises(const char *public_key_file, const char *private_key_file, const int key_size)
int RemoveKeys(const char *input, bool must_be_coherent)
removes all traces of entry 'input' from lastseen and filesystem
bool LOOKUP_HOSTS
Definition: cf-key.c:57
#define ARG_UNUSED
Definition: cf-net.c:47
static int input(void)
Definition: cf3lex.c:2154
void free(void *)
int IPString2Hostname(char *dst, const char *ipaddr, size_t dst_size)
Reverse DNS lookup of ipaddr, store the address as string into dst of size dst_size.
const char * CryptoLastErrorString()
Definition: crypto.c:65
char * LoadPubkeyDigest(const char *filename)
Definition: crypto.c:522
bool NextDB(DBCursor *cursor, char **key, int *ksize, void **value, int *vsize)
Definition: dbm_api.c:601
bool DeleteDB(DBHandle *handle, const char *key)
Definition: dbm_api.c:583
bool OpenDB(DBHandle **dbp, dbid id)
Definition: dbm_api.c:441
bool ReadDB(DBHandle *handle, const char *key, void *dest, int destSz)
Definition: dbm_api.c:556
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_lastseen
Definition: dbm_api.h:45
#define CF_BUFSIZE
Definition: definitions.h:50
#define ENTERPRISE_FUNC_1ARG_DEFINE_STUB(__ret, __func, __t1, __p1)
FILE * safe_fopen_create_perms(const char *const path, const char *const mode, const mode_t create_perms)
Definition: file_lib.c:832
#define FILE_SEPARATOR
Definition: file_lib.h:102
#define NULL
Definition: getopt1.c:56
int RemovePublicKey(const char *id)
Definition: keyring.c:53
const char * GetWorkDir(void)
Definition: known_dirs.c:114
int RemoveKeysFromLastSeen(const char *input, bool must_be_coherent, char *equivalent, size_t equivalent_size)
removes all traces of entry 'input' from lastseen DB
Definition: lastseen.c:694
bool ScanLastSeenQuality(LastSeenQualityCallback callback, void *ctx)
Definition: lastseen.c:576
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_VERBOSE
Definition: logging.h:46
@ LOG_LEVEL_INFO
Definition: logging.h:45
int chmod(const char *path, mode_t mode)
#define NI_MAXHOST
Definition: platform.h:962
state
Definition: rlist.c:708
char * cf_strtimestamp_local(const time_t time, char *buf)
Definition: patches.c:128
size_t SeqLength(const Seq *seq)
Length of the sequence.
Definition: sequence.c:354
Seq * SeqNew(size_t initialCapacity, void(ItemDestroy)(void *item))
Definition: sequence.c:31
void SeqDestroy(Seq *seq)
Destroy an existing Sequence.
Definition: sequence.c:60
void SeqAppend(Seq *seq, void *item)
Append a new item to the Sequence.
Definition: sequence.c:104
static void * SeqAt(const Seq *seq, int i)
Definition: sequence.h:57
size_t strlcpy(char *dst, const char *src, size_t siz)
Definition: strlcpy.c:34
time_t lastseen
Definition: db_structs.h:20
Sequence data-structure.
Definition: sequence.h:50