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)  

client_protocol.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 <client_protocol.h>
26 
27 #include <openssl/bn.h> /* BN_* */
28 #include <openssl/err.h> /* ERR_get_error */
29 #include <libcrypto-compat.h>
30 
31 #include <communication.h>
32 #include <net.h>
33 
34 /* libutils */
35 #include <logging.h> /* Log */
36 
37 /* TODO remove all includes from libpromises. */
38 extern char VIPADDRESS[CF_MAX_IP_LEN];
39 extern char VDOMAIN[];
40 extern char VFQNAME[];
41 #include <unix.h> /* GetCurrentUsername */
42 #include <lastseen.h> /* LastSaw */
43 #include <crypto.h> /* PublicKeyFile */
44 #include <hash.h> /* HashString,HashesMatch,HashPubKey*/
45 #include <known_dirs.h>
46 #include <connection_info.h>
47 #include <tls_generic.h> /* TLSErrorString */
48 
49 
50 /*********************************************************************/
51 
52 static bool SKIPIDENTIFY = false; /* GLOBAL_P */
53 
54 /*********************************************************************/
55 
56 void SetSkipIdentify(bool enabled)
57 {
58  SKIPIDENTIFY = enabled;
59 }
60 
61 /*********************************************************************/
62 
63 bool IdentifyAgent(ConnectionInfo *conn_info)
64 {
65  assert(conn_info != NULL);
66 
67  char uname[CF_BUFSIZE], sendbuff[CF_BUFSIZE];
68  char dnsname[CF_MAXVARSIZE], localip[CF_MAX_IP_LEN];
69  int ret;
70 
71  if ((!SKIPIDENTIFY) && (strcmp(VDOMAIN, CF_START_DOMAIN) == 0))
72  {
73  Log(LOG_LEVEL_ERR, "Undefined domain name");
74  return false;
75  }
76 
77  if (!SKIPIDENTIFY)
78  {
79  /* First we need to find out the IP address and DNS name of the socket
80  we are sending from. This is not necessarily the same as VFQNAME if
81  the machine has a different uname from its IP name (!) This can
82  happen on poorly set up machines or on hosts with multiple
83  interfaces, with different names on each interface ... */
84  struct sockaddr_storage myaddr = {0};
85  socklen_t myaddr_len = sizeof(myaddr);
86 
87  if (getsockname(conn_info->sd, (struct sockaddr *) &myaddr, &myaddr_len) == -1)
88  {
89  Log(LOG_LEVEL_ERR, "Couldn't get socket address. (getsockname: %s)", GetErrorStr());
90  return false;
91  }
92 
93  /* No lookup, just convert the bound address to string. */
94  ret = getnameinfo((struct sockaddr *) &myaddr, myaddr_len,
95  localip, sizeof(localip),
96  NULL, 0, NI_NUMERICHOST);
97  if (ret != 0)
98  {
100  "During agent identification. (getnameinfo: %s)",
101  gai_strerror(ret));
102  return false;
103  }
104 
105  /* dnsname: Reverse lookup of the bound IP address. */
106  ret = getnameinfo((struct sockaddr *) &myaddr, myaddr_len,
107  dnsname, sizeof(dnsname), NULL, 0, 0);
108  if (ret != 0)
109  {
110  /* getnameinfo doesn't fail on resolution failure, it just prints
111  * the IP, so here something else is wrong. */
113  "During agent identification for '%s'. (getnameinfo: %s)",
114  localip, gai_strerror(ret));
115  return false;
116  }
117 
118  /* Append a hostname if getnameinfo() does not return FQDN. Might only
119  * happen if the host has no domainname, in which case VDOMAIN might
120  * also be empty! In addition, missing PTR will give numerical result,
121  * so use it either it's IPv4 or IPv6. Finally don't append the
122  * VDOMAIN if "localhost" is resolved name, it means we're connecting
123  * via loopback. */
124  if ((strlen(VDOMAIN) > 0) &&
125  !IsIPV6Address(dnsname) && (strchr(dnsname, '.') == NULL) &&
126  strcmp(dnsname, "localhost") != 0)
127  {
128  strcat(dnsname, ".");
129  strlcat(dnsname, VDOMAIN, sizeof(dnsname));
130  }
131 
132  /* Seems to be a bug in some resolvers that adds garbage, when it just
133  * returns the input. */
134  if (strncmp(dnsname, localip, strlen(localip)) == 0
135  && dnsname[strlen(localip)] != '\0')
136  {
137  dnsname[strlen(localip)] = '\0';
139  "getnameinfo() seems to append garbage to unresolvable IPs, bug mitigated by CFEngine but please report your platform!");
140  }
141  }
142  else
143  {
144  assert(sizeof(localip) >= sizeof(VIPADDRESS));
145  strcpy(localip, VIPADDRESS);
146 
148  "skipidentify was promised, so we are trusting and simply announcing the identity as '%s' for this host",
149  strlen(VFQNAME) > 0 ? VFQNAME : "skipident");
150  if (strlen(VFQNAME) > 0)
151  {
152  strcpy(dnsname, VFQNAME);
153  }
154  else
155  {
156  strcpy(dnsname, "skipident");
157  }
158  }
159 
160 /* client always identifies as root on windows */
161 #ifdef __MINGW32__
162  snprintf(uname, sizeof(uname), "%s", "root");
163 #else
164  GetCurrentUserName(uname, sizeof(uname));
165 #endif
166 
167  snprintf(sendbuff, sizeof(sendbuff), "CAUTH %s %s %s %d",
168  localip, dnsname, uname, 0);
169 
170  if (SendTransaction(conn_info, sendbuff, 0, CF_DONE) == -1)
171  {
173  "During identify agent, could not send auth response. (SendTransaction: %s)", GetErrorStr());
174  return false;
175  }
176 
177  return true;
178 }
179 
180 /*********************************************************************/
181 
182 static bool SetSessionKey(AgentConnection *conn)
183 {
184  BIGNUM *bp;
185  int session_size = CfSessionKeySize(conn->encryption_type);
186 
187  bp = BN_new();
188 
189  if (bp == NULL)
190  {
191  Log(LOG_LEVEL_ERR, "Could not allocate session key");
192  return false;
193  }
194 
195  // session_size is in bytes
196  if (!BN_rand(bp, session_size * 8, -1, 0))
197  {
198  Log(LOG_LEVEL_ERR, "Can't generate cryptographic key");
199  BN_clear_free(bp);
200  return false;
201  }
202 
203  conn->session_key = xmalloc(BN_num_bytes(bp));
204  BN_bn2bin(bp, conn->session_key);
205 
206  BN_clear_free(bp);
207  return true;
208 }
209 
210 bool AuthenticateAgent(AgentConnection *conn, bool trust_key)
211 {
212  char sendbuffer[CF_EXPANDSIZE], in[CF_BUFSIZE], *out, *decrypted_cchall;
213  BIGNUM *nonce_challenge, *bn = NULL;
214  unsigned char digest[EVP_MAX_MD_SIZE + 1];
215  int encrypted_len, nonce_len = 0, len, session_size;
216  bool need_to_implicitly_trust_server;
217  char enterprise_field = 'c';
218 
219  if (PRIVKEY == NULL || PUBKEY == NULL)
220  {
221  Log(LOG_LEVEL_ERR, "No public/private key pair is loaded,"
222  " please create one using cf-key");
223  return false;
224  }
225 
226  enterprise_field = CfEnterpriseOptions();
227  session_size = CfSessionKeySize(enterprise_field);
228 
229 /* Generate a random challenge to authenticate the server */
230 
231  nonce_challenge = BN_new();
232  if (nonce_challenge == NULL)
233  {
234  Log(LOG_LEVEL_ERR, "Cannot allocate BIGNUM structure for server challenge");
235  return false;
236  }
237 
238  BN_rand(nonce_challenge, CF_NONCELEN, 0, 0);
239  nonce_len = BN_bn2mpi(nonce_challenge, in);
240 
241  if (FIPS_MODE)
242  {
243  HashString(in, nonce_len, digest, CF_DEFAULT_DIGEST);
244  }
245  else
246  {
247  HashString(in, nonce_len, digest, HASH_METHOD_MD5);
248  }
249 
250 /* We assume that the server bound to the remote socket is the official one i.e. = root's */
251 
252  /* Ask the server to send us the public key if we don't have it. */
253  RSA *server_pubkey = HavePublicKeyByIP(conn->username, conn->remoteip);
254  if (server_pubkey)
255  {
256  need_to_implicitly_trust_server = false;
257  encrypted_len = RSA_size(server_pubkey);
258  }
259  else
260  {
261  need_to_implicitly_trust_server = true;
262  encrypted_len = nonce_len;
263  }
264 
265 // Server pubkey is what we want to has as a unique ID
266 
267  snprintf(sendbuffer, sizeof(sendbuffer), "SAUTH %c %d %d %c",
268  need_to_implicitly_trust_server ? 'n': 'y',
269  encrypted_len, nonce_len, enterprise_field);
270 
271  out = xmalloc(encrypted_len);
272 
273  if (server_pubkey != NULL)
274  {
275  if (RSA_public_encrypt(nonce_len, in, out, server_pubkey, RSA_PKCS1_PADDING) <= 0)
276  {
278  "Public encryption failed. (RSA_public_encrypt: %s)",
280  free(out);
281  RSA_free(server_pubkey);
282  return false;
283  }
284 
285  memcpy(sendbuffer + CF_RSA_PROTO_OFFSET, out, encrypted_len);
286  }
287  else
288  {
289  memcpy(sendbuffer + CF_RSA_PROTO_OFFSET, in, nonce_len);
290  }
291 
292 /* proposition C1 - Send challenge / nonce */
293 
294  SendTransaction(conn->conn_info, sendbuffer, CF_RSA_PROTO_OFFSET + encrypted_len, CF_DONE);
295 
296  BN_free(bn);
297  BN_free(nonce_challenge);
298  free(out);
299 
300 /*Send the public key - we don't know if server has it */
301 /* proposition C2 */
302 
303  const BIGNUM *pubkey_n, *pubkey_e;
304  RSA_get0_key(PUBKEY, &pubkey_n, &pubkey_e, NULL);
305 
306  memset(sendbuffer, 0, CF_EXPANDSIZE);
307  len = BN_bn2mpi(pubkey_n, sendbuffer);
308  SendTransaction(conn->conn_info, sendbuffer, len, CF_DONE); /* No need to encrypt the public key ... */
309 
310 /* proposition C3 */
311  memset(sendbuffer, 0, CF_EXPANDSIZE);
312  len = BN_bn2mpi(pubkey_e, sendbuffer);
313  SendTransaction(conn->conn_info, sendbuffer, len, CF_DONE);
314 
315 /* check reply about public key - server can break conn_info here */
316 
317 /* proposition S1 */
318  memset(in, 0, CF_BUFSIZE);
319 
320  if (ReceiveTransaction(conn->conn_info, in, NULL) == -1)
321  {
322  Log(LOG_LEVEL_ERR, "Protocol transaction broken off (1). (ReceiveTransaction: %s)", GetErrorStr());
323  RSA_free(server_pubkey);
324  return false;
325  }
326 
327  if (BadProtoReply(in))
328  {
329  Log(LOG_LEVEL_ERR, "Bad protocol reply: %s", in);
330  RSA_free(server_pubkey);
331  return false;
332  }
333 
334 /* Get challenge response - should be CF_DEFAULT_DIGEST of challenge */
335 
336 /* proposition S2 */
337  memset(in, 0, CF_BUFSIZE);
338 
339  if (ReceiveTransaction(conn->conn_info, in, NULL) == -1)
340  {
341  Log(LOG_LEVEL_ERR, "Protocol transaction broken off (2). (ReceiveTransaction: %s)", GetErrorStr());
342  RSA_free(server_pubkey);
343  return false;
344  }
345 
346  /* Check if challenge reply was correct */
347  if ((HashesMatch(digest, in, CF_DEFAULT_DIGEST)) ||
348  (HashesMatch(digest, in, HASH_METHOD_MD5))) // Legacy
349  {
350  if (need_to_implicitly_trust_server == false)
351  {
352  /* The IP was found in lastseen. */
354  ".....................[.h.a.i.l.].................................");
356  "Strong authentication of server '%s' connection confirmed",
357  conn->this_server);
358  }
359  else /* IP was not found in lastseen */
360  {
361  if (trust_key)
362  {
364  "Trusting server identity, promise to accept key from '%s' = '%s'",
365  conn->this_server, conn->remoteip);
366  }
367  else
368  {
370  "Not authorized to trust public key of server '%s' (trustkey = false)",
371  conn->this_server);
372  RSA_free(server_pubkey);
373  return false;
374  }
375  }
376  }
377  else
378  {
379  Log(LOG_LEVEL_ERR, "Challenge response from server '%s/%s' was incorrect", conn->this_server,
380  conn->remoteip);
381  RSA_free(server_pubkey);
382  return false;
383  }
384 
385 /* Receive counter challenge from server */
386 
387 /* proposition S3 */
388  memset(in, 0, CF_BUFSIZE);
389  encrypted_len = ReceiveTransaction(conn->conn_info, in, NULL);
390 
391  if (encrypted_len == -1)
392  {
393  Log(LOG_LEVEL_ERR, "Protocol transaction sent illegal cipher length");
394  RSA_free(server_pubkey);
395  return false;
396  }
397 
398  decrypted_cchall = xmalloc(encrypted_len);
399 
400  if (RSA_private_decrypt(encrypted_len, in, decrypted_cchall, PRIVKEY, RSA_PKCS1_PADDING) <= 0)
401  {
403  "Private decrypt failed, abandoning. (RSA_private_decrypt: %s)",
405  RSA_free(server_pubkey);
406  return false;
407  }
408 
409 /* proposition C4 */
410  if (FIPS_MODE)
411  {
412  HashString(decrypted_cchall, nonce_len, digest, CF_DEFAULT_DIGEST);
413  }
414  else
415  {
416  HashString(decrypted_cchall, nonce_len, digest, HASH_METHOD_MD5);
417  }
418 
419  if (FIPS_MODE)
420  {
422  }
423  else
424  {
425  SendTransaction(conn->conn_info, digest, CF_MD5_LEN, CF_DONE);
426  }
427 
428  free(decrypted_cchall);
429 
430 /* If we don't have the server's public key, it will be sent */
431 
432  if (server_pubkey == NULL)
433  {
434  RSA *newkey = RSA_new();
435 
436  Log(LOG_LEVEL_VERBOSE, "Collecting public key from server!");
437 
438  /* proposition S4 - conditional */
439  if ((len = ReceiveTransaction(conn->conn_info, in, NULL)) == -1)
440  {
441  Log(LOG_LEVEL_ERR, "Protocol error in RSA authentation from IP '%s'", conn->this_server);
442  return false;
443  }
444 
445  BIGNUM *newkey_n, *newkey_e;
446  if ((newkey_n = BN_mpi2bn(in, len, NULL)) == NULL)
447  {
449  "Private key decrypt failed. (BN_mpi2bn: %s)",
451  RSA_free(newkey);
452  return false;
453  }
454 
455  /* proposition S5 - conditional */
456 
457  if ((len = ReceiveTransaction(conn->conn_info, in, NULL)) == -1)
458  {
459  Log(LOG_LEVEL_INFO, "Protocol error in RSA authentation from IP '%s'",
460  conn->this_server);
461  BN_clear_free(newkey_n);
462  RSA_free(newkey);
463  return false;
464  }
465 
466  if ((newkey_e = BN_mpi2bn(in, len, NULL)) == NULL)
467  {
469  "Public key decrypt failed. (BN_mpi2bn: %s)",
471  BN_clear_free(newkey_n);
472  RSA_free(newkey);
473  return false;
474  }
475 
476  if (RSA_set0_key(newkey, newkey_n, newkey_e, NULL) != 1)
477  {
478  Log(LOG_LEVEL_ERR, "Failed to set RSA key: %s",
479  TLSErrorString(ERR_get_error()));
480  BN_clear_free(newkey_e);
481  BN_clear_free(newkey_n);
482  RSA_free(newkey);
483  return false;
484  }
485 
486  server_pubkey = RSAPublicKey_dup(newkey);
487  RSA_free(newkey);
488  }
489  assert(server_pubkey != NULL);
490 
491 /* proposition C5 */
492 
493  if (!SetSessionKey(conn))
494  {
495  Log(LOG_LEVEL_ERR, "Unable to set session key");
496  return false;
497  }
498 
499  if (conn->session_key == NULL)
500  {
501  Log(LOG_LEVEL_ERR, "A random session key could not be established");
502  RSA_free(server_pubkey);
503  return false;
504  }
505 
506  encrypted_len = RSA_size(server_pubkey);
507 
508  out = xmalloc(encrypted_len);
509 
510  if (RSA_public_encrypt(session_size, conn->session_key, out, server_pubkey, RSA_PKCS1_PADDING) <= 0)
511  {
513  "Public encryption failed. (RSA_public_encrypt: %s)",
515  free(out);
516  RSA_free(server_pubkey);
517  return false;
518  }
519 
520  SendTransaction(conn->conn_info, out, encrypted_len, CF_DONE);
521 
522  Key *key = KeyNew(server_pubkey, CF_DEFAULT_DIGEST);
523  conn->conn_info->remote_key = key;
524 
525  Log(LOG_LEVEL_VERBOSE, "Public key identity of host '%s' is: %s",
527 
528  SavePublicKey(conn->username, KeyPrintableHash(conn->conn_info->remote_key), server_pubkey);
529 
530  unsigned int length = 0;
532 
533  free(out);
534 
535  return true;
536 }
537 
538 
539 /*********************************************************************/
540 
541 bool BadProtoReply(char *buf)
542 {
543  return (strncmp(buf, "BAD: ", 5) == 0);
544 }
545 
546 /*********************************************************************/
547 
548 bool OKProtoReply(char *buf)
549 {
550  return (strncmp(buf, "OK:", 3) == 0);
551 }
552 
553 /*********************************************************************/
554 
555 bool FailedProtoReply(char *buf)
556 {
557  return (strncmp(buf, CF_FAILEDSTR, strlen(CF_FAILEDSTR)) == 0);
558 }
void * xmalloc(size_t size)
Definition: alloc-mini.c:46
#define CF_NONCELEN
Definition: cf3.defs.h:55
#define CF_FAILEDSTR
Definition: cf3.defs.h:135
#define CF_START_DOMAIN
Definition: cf3.defs.h:139
HashMethod CF_DEFAULT_DIGEST
Definition: cf3globals.c:88
bool FIPS_MODE
Definition: cf3globals.c:36
int CF_DEFAULT_DIGEST_LEN
Definition: cf3globals.c:89
void free(void *)
#define CF_RSA_PROTO_OFFSET
Definition: cfnet.h:49
#define CF_DONE
Definition: cfnet.h:45
#define CF_MAX_IP_LEN
Definition: cfnet.h:39
void SetSkipIdentify(bool enabled)
char VDOMAIN[]
Definition: cf3globals.c:60
char VFQNAME[]
Definition: cf3globals.c:58
bool FailedProtoReply(char *buf)
bool AuthenticateAgent(AgentConnection *conn, bool trust_key)
static bool SetSessionKey(AgentConnection *conn)
char VIPADDRESS[64]
Definition: cf3globals.c:81
bool OKProtoReply(char *buf)
static bool SKIPIDENTIFY
bool BadProtoReply(char *buf)
bool IdentifyAgent(ConnectionInfo *conn_info)
bool IsIPV6Address(char *name)
Definition: communication.c:70
const char * CryptoLastErrorString()
Definition: crypto.c:65
RSA * HavePublicKeyByIP(const char *username, const char *ipaddress)
Search for a key given an IP address, by getting the key hash value from lastseen db.
Definition: crypto.c:323
bool SavePublicKey(const char *user, const char *digest, const RSA *key)
Definition: crypto.c:429
#define CF_BUFSIZE
Definition: definitions.h:50
#define CF_EXPANDSIZE
Definition: definitions.h:51
#define CF_MAXVARSIZE
Definition: definitions.h:36
char CfEnterpriseOptions()
int CfSessionKeySize(char type)
const char * gai_strerror(int errcode)
Definition: getaddrinfo.c:340
int getnameinfo(const struct sockaddr *sa, socklen_t salen, char *node, socklen_t nodelen, char *service, socklen_t servicelen, int flags)
Definition: getaddrinfo.c:476
#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 HashString(const char *const buffer, const int len, unsigned char digest[EVP_MAX_MD_SIZE+1], HashMethod type)
Definition: hash.c:478
@ HASH_METHOD_MD5
Definition: hash_method.h:37
@ CF_MD5_LEN
Definition: hash_method.h:50
const char * KeyPrintableHash(const Key *key)
Printable hash of the key.
Definition: key.c:84
Key * KeyNew(RSA *rsa, HashMethod method)
Creates a new Key structure.
Definition: key.c:37
const unsigned char * KeyBinaryHash(const Key *key, unsigned int *length)
Binary hash of the key.
Definition: key.c:75
void LastSaw(const char *ipaddress, const char *digest, LastSeenRole role)
Definition: lastseen.c:90
@ LAST_SEEN_ROLE_CONNECT
Definition: lastseen.h:38
void RSA_get0_key(const RSA *r, const BIGNUM **n, const BIGNUM **e, const BIGNUM **d)
int RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d)
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_WARNING
Definition: logging.h:43
@ LOG_LEVEL_VERBOSE
Definition: logging.h:46
@ LOG_LEVEL_INFO
Definition: logging.h:45
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 socklen_t
Definition: platform.h:419
int uname(struct utsname *buf)
#define sockaddr_storage
Definition: platform.h:689
size_t strlcat(char *dst, const char *src, size_t siz)
Definition: strlcat.c:36
ConnectionInfo * conn_info
Definition: cfnet.h:98
unsigned char * session_key
Definition: cfnet.h:104
char username[128]
Definition: cfnet.h:100
char * this_server
Definition: cfnet.h:112
char encryption_type
Definition: cfnet.h:105
char remoteip[64]
Definition: cfnet.h:103
Definition: key.c:32
RSA * PRIVKEY
Definition: cf3globals.c:72
RSA * PUBKEY
Definition: tls_client.c:44
const char * TLSErrorString(intmax_t errcode)
Definition: tls_generic.c:87
bool GetCurrentUserName(char *userName, int userNameLen)
Definition: unix.c:107