void CheckRemoteVersion() { char sendbuffer[CF_BUFSIZE]; char recvbuffer[CF_BUFSIZE]; int tosend; Debug("CheckRemoteVersion\n"); snprintf(sendbuffer,CF_BUFSIZE,"VERSION"); tosend = strlen(sendbuffer); if (SendTransaction(CONN->sd,sendbuffer,tosend,CF_DONE) == -1) { snprintf(OUTPUT,CF_BUFSIZE*2,"Transmission failed while checking version"); CfLog(cfinform,OUTPUT,"send"); return; } if (ReceiveTransaction(CONN->sd,recvbuffer,NULL) == -1) { snprintf(OUTPUT,CF_BUFSIZE*2,"Reply failed while checking version"); CfLog(cfinform,OUTPUT,"send"); CONN->protoversion = 0; return; } if (BadProtoReply(recvbuffer)) { CONN->protoversion = 0; return; } else { CONN->protoversion = 1; return; } }
int AuthenticateAgent(AgentConnection *conn, bool trust_key) { char sendbuffer[CF_EXPANDSIZE], in[CF_BUFSIZE], *out, *decrypted_cchall; BIGNUM *nonce_challenge, *bn = NULL; unsigned char digest[EVP_MAX_MD_SIZE]; int encrypted_len, nonce_len = 0, len, session_size; bool need_to_implicitly_trust_server; char enterprise_field = 'c'; RSA *server_pubkey = NULL; if ((PUBKEY == NULL) || (PRIVKEY == NULL)) { /* Try once more to load the keys, maybe the system is converging. */ LoadSecretKeys(); if ((PUBKEY == NULL) || (PRIVKEY == NULL)) { char *pubkeyfile = PublicKeyFile(GetWorkDir()); Log(LOG_LEVEL_ERR, "No public/private key pair found at: %s", pubkeyfile); free(pubkeyfile); return false; } } enterprise_field = CfEnterpriseOptions(); session_size = CfSessionKeySize(enterprise_field); /* Generate a random challenge to authenticate the server */ nonce_challenge = BN_new(); if (nonce_challenge == NULL) { Log(LOG_LEVEL_ERR, "Cannot allocate BIGNUM structure for server challenge"); return false; } BN_rand(nonce_challenge, CF_NONCELEN, 0, 0); nonce_len = BN_bn2mpi(nonce_challenge, in); if (FIPS_MODE) { HashString(in, nonce_len, digest, CF_DEFAULT_DIGEST); } else { HashString(in, nonce_len, digest, HASH_METHOD_MD5); } /* We assume that the server bound to the remote socket is the official one i.e. = root's */ /* Ask the server to send us the public key if we don't have it. */ if ((server_pubkey = HavePublicKeyByIP(conn->username, conn->remoteip))) { need_to_implicitly_trust_server = false; encrypted_len = RSA_size(server_pubkey); } else { need_to_implicitly_trust_server = true; encrypted_len = nonce_len; } // Server pubkey is what we want to has as a unique ID snprintf(sendbuffer, sizeof(sendbuffer), "SAUTH %c %d %d %c", need_to_implicitly_trust_server ? 'n': 'y', encrypted_len, nonce_len, enterprise_field); out = xmalloc(encrypted_len); if (server_pubkey != NULL) { if (RSA_public_encrypt(nonce_len, in, out, server_pubkey, RSA_PKCS1_PADDING) <= 0) { Log(LOG_LEVEL_ERR, "Public encryption failed. (RSA_public_encrypt: %s)", CryptoLastErrorString()); free(out); RSA_free(server_pubkey); return false; } memcpy(sendbuffer + CF_RSA_PROTO_OFFSET, out, encrypted_len); } else { memcpy(sendbuffer + CF_RSA_PROTO_OFFSET, in, nonce_len); } /* proposition C1 - Send challenge / nonce */ SendTransaction(conn->conn_info, sendbuffer, CF_RSA_PROTO_OFFSET + encrypted_len, CF_DONE); BN_free(bn); BN_free(nonce_challenge); free(out); /*Send the public key - we don't know if server has it */ /* proposition C2 */ memset(sendbuffer, 0, CF_EXPANDSIZE); len = BN_bn2mpi(PUBKEY->n, sendbuffer); SendTransaction(conn->conn_info, sendbuffer, len, CF_DONE); /* No need to encrypt the public key ... */ /* proposition C3 */ memset(sendbuffer, 0, CF_EXPANDSIZE); len = BN_bn2mpi(PUBKEY->e, sendbuffer); SendTransaction(conn->conn_info, sendbuffer, len, CF_DONE); /* check reply about public key - server can break conn_info here */ /* proposition S1 */ memset(in, 0, CF_BUFSIZE); if (ReceiveTransaction(conn->conn_info, in, NULL) == -1) { Log(LOG_LEVEL_ERR, "Protocol transaction broken off (1). (ReceiveTransaction: %s)", GetErrorStr()); RSA_free(server_pubkey); return false; } if (BadProtoReply(in)) { Log(LOG_LEVEL_ERR, "Bad protocol reply: %s", in); RSA_free(server_pubkey); return false; } /* Get challenge response - should be CF_DEFAULT_DIGEST of challenge */ /* proposition S2 */ memset(in, 0, CF_BUFSIZE); if (ReceiveTransaction(conn->conn_info, in, NULL) == -1) { Log(LOG_LEVEL_ERR, "Protocol transaction broken off (2). (ReceiveTransaction: %s)", GetErrorStr()); RSA_free(server_pubkey); return false; } /* Check if challenge reply was correct */ if ((HashesMatch(digest, in, CF_DEFAULT_DIGEST)) || (HashesMatch(digest, in, HASH_METHOD_MD5))) // Legacy { if (need_to_implicitly_trust_server == false) { /* The IP was found in lastseen. */ Log(LOG_LEVEL_VERBOSE, ".....................[.h.a.i.l.]................................."); Log(LOG_LEVEL_VERBOSE, "Strong authentication of server '%s' connection confirmed", conn->this_server); } else /* IP was not found in lastseen */ { if (trust_key) { Log(LOG_LEVEL_VERBOSE, "Trusting server identity, promise to accept key from '%s' = '%s'", conn->this_server, conn->remoteip); } else { Log(LOG_LEVEL_ERR, "Not authorized to trust public key of server '%s' (trustkey = false)", conn->this_server); RSA_free(server_pubkey); return false; } } } else { Log(LOG_LEVEL_ERR, "Challenge response from server '%s/%s' was incorrect", conn->this_server, conn->remoteip); RSA_free(server_pubkey); return false; } /* Receive counter challenge from server */ /* proposition S3 */ memset(in, 0, CF_BUFSIZE); encrypted_len = ReceiveTransaction(conn->conn_info, in, NULL); if (encrypted_len <= 0) { Log(LOG_LEVEL_ERR, "Protocol transaction sent illegal cipher length"); RSA_free(server_pubkey); return false; } decrypted_cchall = xmalloc(encrypted_len); if (RSA_private_decrypt(encrypted_len, in, decrypted_cchall, PRIVKEY, RSA_PKCS1_PADDING) <= 0) { Log(LOG_LEVEL_ERR, "Private decrypt failed, abandoning. (RSA_private_decrypt: %s)", CryptoLastErrorString()); RSA_free(server_pubkey); return false; } /* proposition C4 */ if (FIPS_MODE) { HashString(decrypted_cchall, nonce_len, digest, CF_DEFAULT_DIGEST); } else { HashString(decrypted_cchall, nonce_len, digest, HASH_METHOD_MD5); } if (FIPS_MODE) { SendTransaction(conn->conn_info, digest, CF_DEFAULT_DIGEST_LEN, CF_DONE); } else { SendTransaction(conn->conn_info, digest, CF_MD5_LEN, CF_DONE); } free(decrypted_cchall); /* If we don't have the server's public key, it will be sent */ if (server_pubkey == NULL) { RSA *newkey = RSA_new(); Log(LOG_LEVEL_VERBOSE, "Collecting public key from server!"); /* proposition S4 - conditional */ if ((len = ReceiveTransaction(conn->conn_info, in, NULL)) <= 0) { Log(LOG_LEVEL_ERR, "Protocol error in RSA authentation from IP '%s'", conn->this_server); return false; } if ((newkey->n = BN_mpi2bn(in, len, NULL)) == NULL) { Log(LOG_LEVEL_ERR, "Private key decrypt failed. (BN_mpi2bn: %s)", CryptoLastErrorString()); RSA_free(newkey); return false; } /* proposition S5 - conditional */ if ((len = ReceiveTransaction(conn->conn_info, in, NULL)) <= 0) { Log(LOG_LEVEL_INFO, "Protocol error in RSA authentation from IP '%s'", conn->this_server); RSA_free(newkey); return false; } if ((newkey->e = BN_mpi2bn(in, len, NULL)) == NULL) { Log(LOG_LEVEL_ERR, "Public key decrypt failed. (BN_mpi2bn: %s)", CryptoLastErrorString()); RSA_free(newkey); return false; } server_pubkey = RSAPublicKey_dup(newkey); RSA_free(newkey); } assert(server_pubkey != NULL); /* proposition C5 */ if (!SetSessionKey(conn)) { Log(LOG_LEVEL_ERR, "Unable to set session key"); return false; } if (conn->session_key == NULL) { Log(LOG_LEVEL_ERR, "A random session key could not be established"); RSA_free(server_pubkey); return false; } encrypted_len = RSA_size(server_pubkey); out = xmalloc(encrypted_len); if (RSA_public_encrypt(session_size, conn->session_key, out, server_pubkey, RSA_PKCS1_PADDING) <= 0) { Log(LOG_LEVEL_ERR, "Public encryption failed. (RSA_public_encrypt: %s)", CryptoLastErrorString()); free(out); RSA_free(server_pubkey); return false; } SendTransaction(conn->conn_info, out, encrypted_len, CF_DONE); Key *key = KeyNew(server_pubkey, CF_DEFAULT_DIGEST); conn->conn_info->remote_key = key; Log(LOG_LEVEL_VERBOSE, "Public key identity of host '%s' is: %s", conn->remoteip, KeyPrintableHash(conn->conn_info->remote_key)); SavePublicKey(conn->username, KeyPrintableHash(conn->conn_info->remote_key), server_pubkey); unsigned int length = 0; LastSaw(conn->remoteip, KeyBinaryHash(conn->conn_info->remote_key, &length), LAST_SEEN_ROLE_CONNECT); free(out); return true; }
/** * @param #stattype should be either "link" or "file". If a link, this reads * readlink and sends it back in the same packet. It then * caches the value for each copy command. * */ int cf_remote_stat(AgentConnection *conn, bool encrypt, const char *file, struct stat *statbuf, const char *stattype) { assert(strcmp(stattype, "file") == 0 || strcmp(stattype, "link") == 0); /* We encrypt only for CLASSIC protocol. The TLS protocol is always over * encrypted layer, so it does not support encrypted (S*) commands. */ encrypt = encrypt && conn->conn_info->protocol == CF_PROTOCOL_CLASSIC; if (strlen(file) > CF_BUFSIZE - 30) { Log(LOG_LEVEL_ERR, "Filename too long"); return -1; } int ret = StatFromCache(conn, file, statbuf, stattype); if (ret == 0 || ret == -1) /* found or error */ { return ret; } /* Not found in cache */ char recvbuffer[CF_BUFSIZE]; memset(recvbuffer, 0, CF_BUFSIZE); time_t tloc = time(NULL); if (tloc == (time_t) -1) { Log(LOG_LEVEL_ERR, "Couldn't read system clock (time: %s)", GetErrorStr()); tloc = 0; } char sendbuffer[CF_BUFSIZE]; int tosend; sendbuffer[0] = '\0'; if (encrypt) { if (conn->session_key == NULL) { Log(LOG_LEVEL_ERR, "Cannot do encrypted copy without keys (use cf-key)"); return -1; } char in[CF_BUFSIZE], out[CF_BUFSIZE]; snprintf(in, CF_BUFSIZE - 1, "SYNCH %jd STAT %s", (intmax_t) tloc, file); int cipherlen = EncryptString(out, sizeof(out), in, strlen(in) + 1, conn->encryption_type, conn->session_key); tosend = cipherlen + CF_PROTO_OFFSET; if(tosend > sizeof(sendbuffer)) { ProgrammingError("cf_remote_stat: tosend (%d) > sendbuffer (%ld)", tosend, sizeof(sendbuffer)); } snprintf(sendbuffer, CF_BUFSIZE - 1, "SSYNCH %d", cipherlen); memcpy(sendbuffer + CF_PROTO_OFFSET, out, cipherlen); } else { snprintf(sendbuffer, CF_BUFSIZE, "SYNCH %jd STAT %s", (intmax_t) tloc, file); tosend = strlen(sendbuffer); } if (SendTransaction(conn->conn_info, sendbuffer, tosend, CF_DONE) == -1) { Log(LOG_LEVEL_INFO, "Transmission failed/refused talking to %.255s:%.255s. (stat: %s)", conn->this_server, file, GetErrorStr()); return -1; } if (ReceiveTransaction(conn->conn_info, recvbuffer, NULL) == -1) { /* TODO mark connection in the cache as closed. */ return -1; } if (strstr(recvbuffer, "unsynchronized")) { Log(LOG_LEVEL_ERR, "Clocks differ too much to do copy by date (security), server reported: %s", recvbuffer + strlen("BAD: ")); return -1; } if (BadProtoReply(recvbuffer)) { Log(LOG_LEVEL_VERBOSE, "Server returned error: %s", recvbuffer + strlen("BAD: ")); errno = EPERM; return -1; } if (OKProtoReply(recvbuffer)) { Stat cfst; // use intmax_t here to provide enough space for large values coming over the protocol intmax_t d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12 = 0, d13 = 0; ret = sscanf(recvbuffer, "OK: " "%1" PRIdMAX // 01 cfst.cf_type " %5" PRIdMAX // 02 cfst.cf_mode " %14" PRIdMAX // 03 cfst.cf_lmode " %14" PRIdMAX // 04 cfst.cf_uid " %14" PRIdMAX // 05 cfst.cf_gid " %18" PRIdMAX // 06 cfst.cf_size " %14" PRIdMAX // 07 cfst.cf_atime " %14" PRIdMAX // 08 cfst.cf_mtime " %14" PRIdMAX // 09 cfst.cf_ctime " %1" PRIdMAX // 10 cfst.cf_makeholes " %14" PRIdMAX // 11 cfst.cf_ino " %14" PRIdMAX // 12 cfst.cf_nlink " %18" PRIdMAX, // 13 cfst.cf_dev &d1, &d2, &d3, &d4, &d5, &d6, &d7, &d8, &d9, &d10, &d11, &d12, &d13); if (ret < 13) { Log(LOG_LEVEL_ERR, "Cannot read SYNCH reply from '%s', only %d/13 items parsed", conn->remoteip, ret ); return -1; } cfst.cf_type = (FileType) d1; cfst.cf_mode = (mode_t) d2; cfst.cf_lmode = (mode_t) d3; cfst.cf_uid = (uid_t) d4; cfst.cf_gid = (gid_t) d5; cfst.cf_size = (off_t) d6; cfst.cf_atime = (time_t) d7; cfst.cf_mtime = (time_t) d8; cfst.cf_ctime = (time_t) d9; cfst.cf_makeholes = (char) d10; cfst.cf_ino = d11; cfst.cf_nlink = d12; cfst.cf_dev = (dev_t)d13; /* Use %?d here to avoid memory overflow attacks */ memset(recvbuffer, 0, CF_BUFSIZE); if (ReceiveTransaction(conn->conn_info, recvbuffer, NULL) == -1) { /* TODO mark connection in the cache as closed. */ return -1; } if (strlen(recvbuffer) > 3) { cfst.cf_readlink = xstrdup(recvbuffer + 3); } else { cfst.cf_readlink = NULL; } switch (cfst.cf_type) { case FILE_TYPE_REGULAR: cfst.cf_mode |= (mode_t) S_IFREG; break; case FILE_TYPE_DIR: cfst.cf_mode |= (mode_t) S_IFDIR; break; case FILE_TYPE_CHAR_: cfst.cf_mode |= (mode_t) S_IFCHR; break; case FILE_TYPE_FIFO: cfst.cf_mode |= (mode_t) S_IFIFO; break; case FILE_TYPE_SOCK: cfst.cf_mode |= (mode_t) S_IFSOCK; break; case FILE_TYPE_BLOCK: cfst.cf_mode |= (mode_t) S_IFBLK; break; case FILE_TYPE_LINK: cfst.cf_mode |= (mode_t) S_IFLNK; break; } cfst.cf_filename = xstrdup(file); cfst.cf_server = xstrdup(conn->this_server); cfst.cf_failed = false; if (cfst.cf_lmode != 0) { cfst.cf_lmode |= (mode_t) S_IFLNK; } NewStatCache(&cfst, conn); if ((cfst.cf_lmode != 0) && (strcmp(stattype, "link") == 0)) { statbuf->st_mode = cfst.cf_lmode; } else { statbuf->st_mode = cfst.cf_mode; } statbuf->st_uid = cfst.cf_uid; statbuf->st_gid = cfst.cf_gid; statbuf->st_size = cfst.cf_size; statbuf->st_mtime = cfst.cf_mtime; statbuf->st_ctime = cfst.cf_ctime; statbuf->st_atime = cfst.cf_atime; statbuf->st_ino = cfst.cf_ino; statbuf->st_dev = cfst.cf_dev; statbuf->st_nlink = cfst.cf_nlink; return 0; } Log(LOG_LEVEL_ERR, "Transmission refused or failed statting '%s', got '%s'", file, recvbuffer); errno = EPERM; return -1; }
int KeyAuthentication(struct Image *ip) { char sendbuffer[CF_EXPANDSIZE],in[CF_BUFSIZE],*out,*decrypted_cchall; BIGNUM *nonce_challenge, *bn = NULL; unsigned long err; unsigned char digest[EVP_MAX_MD_SIZE]; int encrypted_len,nonce_len = 0,len; char cant_trust_server, keyname[CF_BUFSIZE]; RSA *server_pubkey = NULL; if (COMPATIBILITY_MODE) { return true; } if (PUBKEY == NULL || PRIVKEY == NULL) { CfLog(cferror,"No public/private key pair found\n",""); return false; } /* Generate a random challenge to authenticate the server */ nonce_challenge = BN_new(); BN_rand(nonce_challenge,CF_NONCELEN,0,0); nonce_len = BN_bn2mpi(nonce_challenge,in); ChecksumString(in,nonce_len,digest,'m'); /* We assume that the server bound to the remote socket is the official one i.e. = root's */ if (OptionIs(CONTEXTID,"HostnameKeys",true)) { snprintf(keyname,CF_BUFSIZE,"root-%s",ip->server); Debug("KeyAuthentication(with hostname key %s)\n",keyname); } else { snprintf(keyname,CF_BUFSIZE,"root-%s",CONN->remoteip); Debug("KeyAuthentication(with IP keyname %s)\n",keyname); } if (server_pubkey = HavePublicKey(keyname)) { cant_trust_server = 'y'; /* encrypted_len = BN_num_bytes(server_pubkey->n);*/ encrypted_len = RSA_size(server_pubkey); } else { cant_trust_server = 'n'; /* have to trust server, since we can't verify id */ encrypted_len = nonce_len; } snprintf(sendbuffer,CF_BUFSIZE,"SAUTH %c %d %d",cant_trust_server,encrypted_len,nonce_len); if ((out = malloc(encrypted_len)) == NULL) { FatalError("memory failure"); } if (server_pubkey != NULL) { if (RSA_public_encrypt(nonce_len,in,out,server_pubkey,RSA_PKCS1_PADDING) <= 0) { err = ERR_get_error(); snprintf(OUTPUT,CF_BUFSIZE,"Public encryption failed = %s\n",ERR_reason_error_string(err)); CfLog(cferror,OUTPUT,""); free(out); return false; } memcpy(sendbuffer+CF_RSA_PROTO_OFFSET,out,encrypted_len); } else { memcpy(sendbuffer+CF_RSA_PROTO_OFFSET,in,nonce_len); } /* proposition C1 - Send challenge / nonce */ SendTransaction(CONN->sd,sendbuffer,CF_RSA_PROTO_OFFSET+encrypted_len,CF_DONE); BN_free(bn); BN_free(nonce_challenge); free(out); if (DEBUG||D2) { RSA_print_fp(stdout,PUBKEY,0); } /*Send the public key - we don't know if server has it */ /* proposition C2 */ memset(sendbuffer,0,CF_EXPANDSIZE); len = BN_bn2mpi(PUBKEY->n,sendbuffer); SendTransaction(CONN->sd,sendbuffer,len,CF_DONE); /* No need to encrypt the public key ... */ /* proposition C3 */ memset(sendbuffer,0,CF_EXPANDSIZE); len = BN_bn2mpi(PUBKEY->e,sendbuffer); SendTransaction(CONN->sd,sendbuffer,len,CF_DONE); /* check reply about public key - server can break connection here */ /* proposition S1 */ memset(in,0,CF_BUFSIZE); if (ReceiveTransaction(CONN->sd,in,NULL) == -1) { CfLog(cferror,"Protocol transaction broken off",NULL); return false; } if (BadProtoReply(in)) { CfLog(cferror,in,""); return false; } /* Get challenge response - should be md5 of challenge */ /* proposition S2 */ memset(in,0,CF_BUFSIZE); if (ReceiveTransaction(CONN->sd,in,NULL) == -1) { CfLog(cferror,"Protocol transaction broken off",NULL); return false; } if (!ChecksumsMatch(digest,in,'m')) { snprintf(OUTPUT,CF_BUFSIZE,"Challenge response from server %s/%s was incorrect!",ip->server,CONN->remoteip); CfLog(cferror,OUTPUT,""); return false; } else { char server[CF_EXPANDSIZE]; ExpandVarstring(ip->server,server,NULL); if (cant_trust_server == 'y') /* challenge reply was correct */ { Verbose("\n...............................................................\n"); snprintf(OUTPUT,CF_BUFSIZE,"Strong authentication of server=%s connection confirmed\n",server); CfLog(cfverbose,OUTPUT,""); } else { if (ip->trustkey == 'y') { snprintf(OUTPUT,CF_BUFSIZE,"Trusting server identity and willing to accept key from %s=%s",server,CONN->remoteip); CfLog(cferror,OUTPUT,""); } else { snprintf(OUTPUT,CF_BUFSIZE,"Not authorized to trust the server=%s's public key (trustkey=false)\n",server); CfLog(cferror,OUTPUT,""); return false; } } } /* Receive counter challenge from server */ Debug("Receive counter challenge from server\n"); /* proposition S3 */ memset(in,0,CF_BUFSIZE); encrypted_len = ReceiveTransaction(CONN->sd,in,NULL); if (encrypted_len < 0) { CfLog(cferror,"Protocol transaction sent illegal cipher length",NULL); return false; } if ((decrypted_cchall = malloc(encrypted_len)) == NULL) { FatalError("memory failure"); } if (RSA_private_decrypt(encrypted_len,in,decrypted_cchall,PRIVKEY,RSA_PKCS1_PADDING) <= 0) { err = ERR_get_error(); snprintf(OUTPUT,CF_BUFSIZE,"Private decrypt failed = %s, abandoning\n",ERR_reason_error_string(err)); CfLog(cferror,OUTPUT,""); return false; } /* proposition C4 */ ChecksumString(decrypted_cchall,nonce_len,digest,'m'); Debug("Replying to counter challenge with md5\n"); SendTransaction(CONN->sd,digest,16,CF_DONE); free(decrypted_cchall); /* If we don't have the server's public key, it will be sent */ if (server_pubkey == NULL) { RSA *newkey = RSA_new(); Debug("Collecting public key from server!\n"); /* proposition S4 - conditional */ if ((len = ReceiveTransaction(CONN->sd,in,NULL)) <= 0) { CfLog(cferror,"Protocol error in RSA authentation from IP %s\n",ip->server); return false; } if ((newkey->n = BN_mpi2bn(in,len,NULL)) == NULL) { err = ERR_get_error(); snprintf(OUTPUT,CF_BUFSIZE,"Private decrypt failed = %s\n",ERR_reason_error_string(err)); CfLog(cferror,OUTPUT,""); RSA_free(newkey); return false; } /* proposition S5 - conditional */ if ((len=ReceiveTransaction(CONN->sd,in,NULL)) == 0) { CfLog(cfinform,"Protocol error in RSA authentation from IP %s\n",ip->server); RSA_free(newkey); return false; } if ((newkey->e = BN_mpi2bn(in,len,NULL)) == NULL) { err = ERR_get_error(); snprintf(OUTPUT,CF_BUFSIZE,"Private decrypt failed = %s\n",ERR_reason_error_string(err)); CfLog(cferror,OUTPUT,""); RSA_free(newkey); return false; } SavePublicKey(keyname,newkey); server_pubkey = RSAPublicKey_dup(newkey); RSA_free(newkey); } /* proposition C5 */ GenerateRandomSessionKey(); DebugBinOut(CONN->session_key,CF_BLOWFISHSIZE); if (CONN->session_key == NULL) { CfLog(cferror,"A random session key could not be established",""); return false; } else { Debug("Generated session key\n"); DebugBinOut(CONN->session_key,CF_BLOWFISHSIZE); } /* blowfishmpisize = BN_bn2mpi((BIGNUM *)CONN->session_key,in); */ DebugBinOut(CONN->session_key,CF_BLOWFISHSIZE); encrypted_len = RSA_size(server_pubkey); Debug("Encrypt %d to %d\n",CF_BLOWFISHSIZE,encrypted_len); if ((out = malloc(encrypted_len)) == NULL) { FatalError("memory failure"); } if (RSA_public_encrypt(CF_BLOWFISHSIZE,CONN->session_key,out,server_pubkey,RSA_PKCS1_PADDING) <= 0) { err = ERR_get_error(); snprintf(OUTPUT,CF_BUFSIZE,"Public encryption failed = %s\n",ERR_reason_error_string(err)); CfLog(cferror,OUTPUT,""); free(out); return false; } Debug("Encryption succeeded\n"); SendTransaction(CONN->sd,out,encrypted_len,CF_DONE); DebugBinOut(out,encrypted_len); if (server_pubkey != NULL) { RSA_free(server_pubkey); } free(out); return true; }
Item *RemoteDirList(const char *dirname, bool encrypt, AgentConnection *conn) { char sendbuffer[CF_BUFSIZE]; char recvbuffer[CF_BUFSIZE]; char in[CF_BUFSIZE]; char out[CF_BUFSIZE]; int n, cipherlen = 0, tosend; char *sp; Item *files = NULL; Item *ret = NULL; if (strlen(dirname) > CF_BUFSIZE - 20) { Log(LOG_LEVEL_ERR, "Directory name too long"); return NULL; } if (encrypt) { if (conn->session_key == NULL) { Log(LOG_LEVEL_ERR, "Cannot do encrypted copy without keys (use cf-key)"); return NULL; } snprintf(in, CF_BUFSIZE, "OPENDIR %s", dirname); cipherlen = EncryptString(conn->encryption_type, in, out, conn->session_key, strlen(in) + 1); snprintf(sendbuffer, CF_BUFSIZE - 1, "SOPENDIR %d", cipherlen); memcpy(sendbuffer + CF_PROTO_OFFSET, out, cipherlen); tosend = cipherlen + CF_PROTO_OFFSET; } else { snprintf(sendbuffer, CF_BUFSIZE, "OPENDIR %s", dirname); tosend = strlen(sendbuffer); } if (SendTransaction(conn->sd, sendbuffer, tosend, CF_DONE) == -1) { return NULL; } while (true) { if ((n = ReceiveTransaction(conn->sd, recvbuffer, NULL)) == -1) { return NULL; } if (n == 0) { break; } if (encrypt) { memcpy(in, recvbuffer, n); DecryptString(conn->encryption_type, in, recvbuffer, conn->session_key, n); } if (FailedProtoReply(recvbuffer)) { Log(LOG_LEVEL_INFO, "Network access to '%s:%s' denied", conn->this_server, dirname); return NULL; } if (BadProtoReply(recvbuffer)) { Log(LOG_LEVEL_INFO, "%s", recvbuffer + 4); return NULL; } for (sp = recvbuffer; *sp != '\0'; sp++) { Item *ip; if (strncmp(sp, CFD_TERMINATOR, strlen(CFD_TERMINATOR)) == 0) /* End transmission */ { return ret; } ip = xcalloc(1, sizeof(Item)); ip->name = (char *) AllocateDirentForFilename(sp); if (files == NULL) /* First element */ { ret = ip; files = ip; } else { files->next = ip; files = ip; } while (*sp != '\0') { sp++; } } } return ret; }
int cf_remote_stat(char *file, struct stat *buf, char *stattype, bool encrypt, AgentConnection *conn) /* If a link, this reads readlink and sends it back in the same package. It then caches the value for each copy command */ { char sendbuffer[CF_BUFSIZE]; char recvbuffer[CF_BUFSIZE]; char in[CF_BUFSIZE], out[CF_BUFSIZE]; int ret, tosend, cipherlen; time_t tloc; memset(recvbuffer, 0, CF_BUFSIZE); if (strlen(file) > CF_BUFSIZE - 30) { Log(LOG_LEVEL_ERR, "Filename too long"); return -1; } ret = CacheStat(file, buf, stattype, conn); if (ret != 0) { return ret; } if ((tloc = time((time_t *) NULL)) == -1) { Log(LOG_LEVEL_ERR, "Couldn't read system clock"); } sendbuffer[0] = '\0'; if (encrypt) { if (conn->session_key == NULL) { Log(LOG_LEVEL_ERR, "Cannot do encrypted copy without keys (use cf-key)"); return -1; } snprintf(in, CF_BUFSIZE - 1, "SYNCH %jd STAT %s", (intmax_t) tloc, file); cipherlen = EncryptString(conn->encryption_type, in, out, conn->session_key, strlen(in) + 1); snprintf(sendbuffer, CF_BUFSIZE - 1, "SSYNCH %d", cipherlen); memcpy(sendbuffer + CF_PROTO_OFFSET, out, cipherlen); tosend = cipherlen + CF_PROTO_OFFSET; } else { snprintf(sendbuffer, CF_BUFSIZE, "SYNCH %jd STAT %s", (intmax_t) tloc, file); tosend = strlen(sendbuffer); } if (SendTransaction(conn->sd, sendbuffer, tosend, CF_DONE) == -1) { Log(LOG_LEVEL_INFO, "Transmission failed/refused talking to %.255s:%.255s. (stat: %s)", conn->this_server, file, GetErrorStr()); return -1; } if (ReceiveTransaction(conn->sd, recvbuffer, NULL) == -1) { return -1; } if (strstr(recvbuffer, "unsynchronized")) { Log(LOG_LEVEL_ERR, "Clocks differ too much to do copy by date (security) '%s'", recvbuffer + 4); return -1; } if (BadProtoReply(recvbuffer)) { Log(LOG_LEVEL_VERBOSE, "Server returned error '%s'", recvbuffer + 4); errno = EPERM; return -1; } if (OKProtoReply(recvbuffer)) { Stat cfst; // use intmax_t here to provide enough space for large values coming over the protocol intmax_t d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12 = 0, d13 = 0; ret = sscanf(recvbuffer, "OK: " "%1" PRIdMAX // 01 cfst.cf_type " %5" PRIdMAX // 02 cfst.cf_mode " %14" PRIdMAX // 03 cfst.cf_lmode " %14" PRIdMAX // 04 cfst.cf_uid " %14" PRIdMAX // 05 cfst.cf_gid " %18" PRIdMAX // 06 cfst.cf_size " %14" PRIdMAX // 07 cfst.cf_atime " %14" PRIdMAX // 08 cfst.cf_mtime " %14" PRIdMAX // 09 cfst.cf_ctime " %1" PRIdMAX // 10 cfst.cf_makeholes " %14" PRIdMAX // 11 cfst.cf_ino " %14" PRIdMAX // 12 cfst.cf_nlink " %18" PRIdMAX, // 13 cfst.cf_dev &d1, &d2, &d3, &d4, &d5, &d6, &d7, &d8, &d9, &d10, &d11, &d12, &d13); if (ret < 13) { Log(LOG_LEVEL_ERR, "Cannot read SYNCH reply from '%s', only %d/13 items parsed", conn->remoteip, ret ); return -1; } cfst.cf_type = (FileType) d1; cfst.cf_mode = (mode_t) d2; cfst.cf_lmode = (mode_t) d3; cfst.cf_uid = (uid_t) d4; cfst.cf_gid = (gid_t) d5; cfst.cf_size = (off_t) d6; cfst.cf_atime = (time_t) d7; cfst.cf_mtime = (time_t) d8; cfst.cf_ctime = (time_t) d9; cfst.cf_makeholes = (char) d10; cfst.cf_ino = d11; cfst.cf_nlink = d12; cfst.cf_dev = (dev_t)d13; /* Use %?d here to avoid memory overflow attacks */ memset(recvbuffer, 0, CF_BUFSIZE); if (ReceiveTransaction(conn->sd, recvbuffer, NULL) == -1) { return -1; } if (strlen(recvbuffer) > 3) { cfst.cf_readlink = xstrdup(recvbuffer + 3); } else { cfst.cf_readlink = NULL; } switch (cfst.cf_type) { case FILE_TYPE_REGULAR: cfst.cf_mode |= (mode_t) S_IFREG; break; case FILE_TYPE_DIR: cfst.cf_mode |= (mode_t) S_IFDIR; break; case FILE_TYPE_CHAR_: cfst.cf_mode |= (mode_t) S_IFCHR; break; case FILE_TYPE_FIFO: cfst.cf_mode |= (mode_t) S_IFIFO; break; case FILE_TYPE_SOCK: cfst.cf_mode |= (mode_t) S_IFSOCK; break; case FILE_TYPE_BLOCK: cfst.cf_mode |= (mode_t) S_IFBLK; break; case FILE_TYPE_LINK: cfst.cf_mode |= (mode_t) S_IFLNK; break; } cfst.cf_filename = xstrdup(file); cfst.cf_server = xstrdup(conn->this_server); cfst.cf_failed = false; if (cfst.cf_lmode != 0) { cfst.cf_lmode |= (mode_t) S_IFLNK; } NewClientCache(&cfst, conn); if ((cfst.cf_lmode != 0) && (strcmp(stattype, "link") == 0)) { buf->st_mode = cfst.cf_lmode; } else { buf->st_mode = cfst.cf_mode; } buf->st_uid = cfst.cf_uid; buf->st_gid = cfst.cf_gid; buf->st_size = cfst.cf_size; buf->st_mtime = cfst.cf_mtime; buf->st_ctime = cfst.cf_ctime; buf->st_atime = cfst.cf_atime; buf->st_ino = cfst.cf_ino; buf->st_dev = cfst.cf_dev; buf->st_nlink = cfst.cf_nlink; return 0; } Log(LOG_LEVEL_ERR, "Transmission refused or failed statting '%s', got '%s'", file, recvbuffer); errno = EPERM; return -1; }
int AuthenticateAgent(AgentConnection *conn, bool trust_key) { char sendbuffer[CF_EXPANDSIZE], in[CF_BUFSIZE], *out, *decrypted_cchall; BIGNUM *nonce_challenge, *bn = NULL; unsigned long err; unsigned char digest[EVP_MAX_MD_SIZE]; int encrypted_len, nonce_len = 0, len, session_size; bool implicitly_trust_server; char enterprise_field = 'c'; RSA *server_pubkey = NULL; if ((PUBKEY == NULL) || (PRIVKEY == NULL)) { Log(LOG_LEVEL_ERR, "No public/private key pair found at %s", PublicKeyFile(GetWorkDir())); return false; } enterprise_field = CfEnterpriseOptions(); session_size = CfSessionKeySize(enterprise_field); /* Generate a random challenge to authenticate the server */ nonce_challenge = BN_new(); if (nonce_challenge == NULL) { Log(LOG_LEVEL_ERR, "Cannot allocate BIGNUM structure for server challenge"); return false; } BN_rand(nonce_challenge, CF_NONCELEN, 0, 0); nonce_len = BN_bn2mpi(nonce_challenge, in); if (FIPS_MODE) { HashString(in, nonce_len, digest, CF_DEFAULT_DIGEST); } else { HashString(in, nonce_len, digest, HASH_METHOD_MD5); } /* We assume that the server bound to the remote socket is the official one i.e. = root's */ if ((server_pubkey = HavePublicKeyByIP(conn->username, conn->remoteip))) { implicitly_trust_server = false; encrypted_len = RSA_size(server_pubkey); } else { implicitly_trust_server = true; encrypted_len = nonce_len; } // Server pubkey is what we want to has as a unique ID snprintf(sendbuffer, sizeof(sendbuffer), "SAUTH %c %d %d %c", implicitly_trust_server ? 'n': 'y', encrypted_len, nonce_len, enterprise_field); out = xmalloc(encrypted_len); if (server_pubkey != NULL) { if (RSA_public_encrypt(nonce_len, in, out, server_pubkey, RSA_PKCS1_PADDING) <= 0) { err = ERR_get_error(); Log(LOG_LEVEL_ERR, "Public encryption failed = %s", ERR_reason_error_string(err)); free(out); RSA_free(server_pubkey); return false; } memcpy(sendbuffer + CF_RSA_PROTO_OFFSET, out, encrypted_len); } else { memcpy(sendbuffer + CF_RSA_PROTO_OFFSET, in, nonce_len); } /* proposition C1 - Send challenge / nonce */ SendTransaction(conn->sd, sendbuffer, CF_RSA_PROTO_OFFSET + encrypted_len, CF_DONE); BN_free(bn); BN_free(nonce_challenge); free(out); if (DEBUG) { RSA_print_fp(stdout, PUBKEY, 0); } /*Send the public key - we don't know if server has it */ /* proposition C2 */ memset(sendbuffer, 0, CF_EXPANDSIZE); len = BN_bn2mpi(PUBKEY->n, sendbuffer); SendTransaction(conn->sd, sendbuffer, len, CF_DONE); /* No need to encrypt the public key ... */ /* proposition C3 */ memset(sendbuffer, 0, CF_EXPANDSIZE); len = BN_bn2mpi(PUBKEY->e, sendbuffer); SendTransaction(conn->sd, sendbuffer, len, CF_DONE); /* check reply about public key - server can break connection here */ /* proposition S1 */ memset(in, 0, CF_BUFSIZE); if (ReceiveTransaction(conn->sd, in, NULL) == -1) { Log(LOG_LEVEL_ERR, "Protocol transaction broken off (1): %s", GetErrorStr()); RSA_free(server_pubkey); return false; } if (BadProtoReply(in)) { Log(LOG_LEVEL_ERR, "%s", in); RSA_free(server_pubkey); return false; } /* Get challenge response - should be CF_DEFAULT_DIGEST of challenge */ /* proposition S2 */ memset(in, 0, CF_BUFSIZE); if (ReceiveTransaction(conn->sd, in, NULL) == -1) { Log(LOG_LEVEL_ERR, "Protocol transaction broken off (2): %s", GetErrorStr()); RSA_free(server_pubkey); return false; } if ((HashesMatch(digest, in, CF_DEFAULT_DIGEST)) || (HashesMatch(digest, in, HASH_METHOD_MD5))) // Legacy { if (implicitly_trust_server == false) /* challenge reply was correct */ { Log(LOG_LEVEL_VERBOSE, ".....................[.h.a.i.l.]................................."); Log(LOG_LEVEL_VERBOSE, "Strong authentication of server=%s connection confirmed", conn->this_server); } else { if (trust_key) { Log(LOG_LEVEL_VERBOSE, " -> Trusting server identity, promise to accept key from %s=%s", conn->this_server, conn->remoteip); } else { Log(LOG_LEVEL_ERR, " !! Not authorized to trust the server=%s's public key (trustkey=false)", conn->this_server); RSA_free(server_pubkey); return false; } } } else { Log(LOG_LEVEL_ERR, "Challenge response from server %s/%s was incorrect!", conn->this_server, conn->remoteip); RSA_free(server_pubkey); return false; } /* Receive counter challenge from server */ /* proposition S3 */ memset(in, 0, CF_BUFSIZE); encrypted_len = ReceiveTransaction(conn->sd, in, NULL); if (encrypted_len <= 0) { Log(LOG_LEVEL_ERR, "Protocol transaction sent illegal cipher length"); RSA_free(server_pubkey); return false; } decrypted_cchall = xmalloc(encrypted_len); if (RSA_private_decrypt(encrypted_len, in, decrypted_cchall, PRIVKEY, RSA_PKCS1_PADDING) <= 0) { err = ERR_get_error(); Log(LOG_LEVEL_ERR, "Private decrypt failed = %s, abandoning", ERR_reason_error_string(err)); RSA_free(server_pubkey); return false; } /* proposition C4 */ if (FIPS_MODE) { HashString(decrypted_cchall, nonce_len, digest, CF_DEFAULT_DIGEST); } else { HashString(decrypted_cchall, nonce_len, digest, HASH_METHOD_MD5); } if (FIPS_MODE) { SendTransaction(conn->sd, digest, CF_DEFAULT_DIGEST_LEN, CF_DONE); } else { SendTransaction(conn->sd, digest, CF_MD5_LEN, CF_DONE); } free(decrypted_cchall); /* If we don't have the server's public key, it will be sent */ if (server_pubkey == NULL) { RSA *newkey = RSA_new(); Log(LOG_LEVEL_VERBOSE, " -> Collecting public key from server!"); /* proposition S4 - conditional */ if ((len = ReceiveTransaction(conn->sd, in, NULL)) <= 0) { Log(LOG_LEVEL_ERR, "Protocol error in RSA authentation from IP %s", conn->this_server); return false; } if ((newkey->n = BN_mpi2bn(in, len, NULL)) == NULL) { err = ERR_get_error(); Log(LOG_LEVEL_ERR, "Private key decrypt failed = %s", ERR_reason_error_string(err)); RSA_free(newkey); return false; } /* proposition S5 - conditional */ if ((len = ReceiveTransaction(conn->sd, in, NULL)) <= 0) { Log(LOG_LEVEL_INFO, "Protocol error in RSA authentation from IP %s", conn->this_server); RSA_free(newkey); return false; } if ((newkey->e = BN_mpi2bn(in, len, NULL)) == NULL) { err = ERR_get_error(); Log(LOG_LEVEL_ERR, "Public key decrypt failed = %s", ERR_reason_error_string(err)); RSA_free(newkey); return false; } server_pubkey = RSAPublicKey_dup(newkey); RSA_free(newkey); } /* proposition C5 */ if (!SetSessionKey(conn)) { Log(LOG_LEVEL_ERR, "Unable to set session key"); return false; } if (conn->session_key == NULL) { Log(LOG_LEVEL_ERR, "A random session key could not be established"); RSA_free(server_pubkey); return false; } encrypted_len = RSA_size(server_pubkey); out = xmalloc(encrypted_len); if (RSA_public_encrypt(session_size, conn->session_key, out, server_pubkey, RSA_PKCS1_PADDING) <= 0) { err = ERR_get_error(); Log(LOG_LEVEL_ERR, "Public encryption failed = %s", ERR_reason_error_string(err)); free(out); RSA_free(server_pubkey); return false; } SendTransaction(conn->sd, out, encrypted_len, CF_DONE); if (server_pubkey != NULL) { char buffer[EVP_MAX_MD_SIZE * 4]; HashPubKey(server_pubkey, conn->digest, CF_DEFAULT_DIGEST); Log(LOG_LEVEL_VERBOSE, " -> Public key identity of host \"%s\" is \"%s\"", conn->remoteip, HashPrintSafe(CF_DEFAULT_DIGEST, conn->digest, buffer)); SavePublicKey(conn->username, conn->remoteip, buffer, server_pubkey); // FIXME: username is local LastSaw(conn->remoteip, conn->digest, LAST_SEEN_ROLE_CONNECT); } free(out); RSA_free(server_pubkey); return true; }
int AuthenticateAgent(AgentConnection *conn, Attributes attr, Promise *pp) { char sendbuffer[CF_EXPANDSIZE], in[CF_BUFSIZE], *out, *decrypted_cchall; BIGNUM *nonce_challenge, *bn = NULL; unsigned long err; unsigned char digest[EVP_MAX_MD_SIZE]; int encrypted_len, nonce_len = 0, len, session_size; char dont_implicitly_trust_server, enterprise_field = 'c'; RSA *server_pubkey = NULL; if (PUBKEY == NULL || PRIVKEY == NULL) { CfOut(cf_error, "", "No public/private key pair found\n"); return false; } enterprise_field = CfEnterpriseOptions(); session_size = CfSessionKeySize(enterprise_field); /* Generate a random challenge to authenticate the server */ nonce_challenge = BN_new(); BN_rand(nonce_challenge, CF_NONCELEN, 0, 0); nonce_len = BN_bn2mpi(nonce_challenge, in); if (FIPS_MODE) { HashString(in, nonce_len, digest, CF_DEFAULT_DIGEST); } else { HashString(in, nonce_len, digest, cf_md5); } /* We assume that the server bound to the remote socket is the official one i.e. = root's */ if ((server_pubkey = HavePublicKeyByIP(conn->username, conn->remoteip))) { dont_implicitly_trust_server = 'y'; encrypted_len = RSA_size(server_pubkey); } else { dont_implicitly_trust_server = 'n'; /* have to trust server, since we can't verify id */ encrypted_len = nonce_len; } // Server pubkey is what we want to has as a unique ID snprintf(sendbuffer, sizeof(sendbuffer), "SAUTH %c %d %d %c", dont_implicitly_trust_server, encrypted_len, nonce_len, enterprise_field); out = xmalloc(encrypted_len); if (server_pubkey != NULL) { if (RSA_public_encrypt(nonce_len, in, out, server_pubkey, RSA_PKCS1_PADDING) <= 0) { err = ERR_get_error(); cfPS(cf_error, CF_FAIL, "", pp, attr, "Public encryption failed = %s\n", ERR_reason_error_string(err)); free(out); FreeRSAKey(server_pubkey); return false; } memcpy(sendbuffer + CF_RSA_PROTO_OFFSET, out, encrypted_len); } else { memcpy(sendbuffer + CF_RSA_PROTO_OFFSET, in, nonce_len); } /* proposition C1 - Send challenge / nonce */ SendTransaction(conn->sd, sendbuffer, CF_RSA_PROTO_OFFSET + encrypted_len, CF_DONE); BN_free(bn); BN_free(nonce_challenge); free(out); if (DEBUG) { RSA_print_fp(stdout, PUBKEY, 0); } /*Send the public key - we don't know if server has it */ /* proposition C2 */ memset(sendbuffer, 0, CF_EXPANDSIZE); len = BN_bn2mpi(PUBKEY->n, sendbuffer); SendTransaction(conn->sd, sendbuffer, len, CF_DONE); /* No need to encrypt the public key ... */ /* proposition C3 */ memset(sendbuffer, 0, CF_EXPANDSIZE); len = BN_bn2mpi(PUBKEY->e, sendbuffer); SendTransaction(conn->sd, sendbuffer, len, CF_DONE); /* check reply about public key - server can break connection here */ /* proposition S1 */ memset(in, 0, CF_BUFSIZE); if (ReceiveTransaction(conn->sd, in, NULL) == -1) { cfPS(cf_error, CF_INTERPT, "recv", pp, attr, "Protocol transaction broken off (1)"); FreeRSAKey(server_pubkey); return false; } if (BadProtoReply(in)) { CfOut(cf_error, "", "%s", in); FreeRSAKey(server_pubkey); return false; } /* Get challenge response - should be CF_DEFAULT_DIGEST of challenge */ /* proposition S2 */ memset(in, 0, CF_BUFSIZE); if (ReceiveTransaction(conn->sd, in, NULL) == -1) { cfPS(cf_error, CF_INTERPT, "recv", pp, attr, "Protocol transaction broken off (2)"); FreeRSAKey(server_pubkey); return false; } if (HashesMatch(digest, in, CF_DEFAULT_DIGEST) || HashesMatch(digest, in, cf_md5)) // Legacy { if (dont_implicitly_trust_server == 'y') /* challenge reply was correct */ { CfOut(cf_verbose, "", ".....................[.h.a.i.l.].................................\n"); CfOut(cf_verbose, "", "Strong authentication of server=%s connection confirmed\n", pp->this_server); } else { if (attr.copy.trustkey) { CfOut(cf_verbose, "", " -> Trusting server identity, promise to accept key from %s=%s", pp->this_server, conn->remoteip); } else { CfOut(cf_error, "", " !! Not authorized to trust the server=%s's public key (trustkey=false)\n", pp->this_server); PromiseRef(cf_verbose, pp); FreeRSAKey(server_pubkey); return false; } } } else { cfPS(cf_error, CF_INTERPT, "", pp, attr, "Challenge response from server %s/%s was incorrect!", pp->this_server, conn->remoteip); FreeRSAKey(server_pubkey); return false; } /* Receive counter challenge from server */ CfDebug("Receive counter challenge from server\n"); /* proposition S3 */ memset(in, 0, CF_BUFSIZE); encrypted_len = ReceiveTransaction(conn->sd, in, NULL); if (encrypted_len <= 0) { CfOut(cf_error, "", "Protocol transaction sent illegal cipher length"); FreeRSAKey(server_pubkey); return false; } decrypted_cchall = xmalloc(encrypted_len); if (RSA_private_decrypt(encrypted_len, in, decrypted_cchall, PRIVKEY, RSA_PKCS1_PADDING) <= 0) { err = ERR_get_error(); cfPS(cf_error, CF_INTERPT, "", pp, attr, "Private decrypt failed = %s, abandoning\n", ERR_reason_error_string(err)); FreeRSAKey(server_pubkey); return false; } /* proposition C4 */ if (FIPS_MODE) { HashString(decrypted_cchall, nonce_len, digest, CF_DEFAULT_DIGEST); } else { HashString(decrypted_cchall, nonce_len, digest, cf_md5); } CfDebug("Replying to counter challenge with hash\n"); if (FIPS_MODE) { SendTransaction(conn->sd, digest, CF_DEFAULT_DIGEST_LEN, CF_DONE); } else { SendTransaction(conn->sd, digest, CF_MD5_LEN, CF_DONE); } free(decrypted_cchall); /* If we don't have the server's public key, it will be sent */ if (server_pubkey == NULL) { RSA *newkey = RSA_new(); CfOut(cf_verbose, "", " -> Collecting public key from server!\n"); /* proposition S4 - conditional */ if ((len = ReceiveTransaction(conn->sd, in, NULL)) <= 0) { CfOut(cf_error, "", "Protocol error in RSA authentation from IP %s\n", pp->this_server); return false; } if ((newkey->n = BN_mpi2bn(in, len, NULL)) == NULL) { err = ERR_get_error(); cfPS(cf_error, CF_INTERPT, "", pp, attr, "Private key decrypt failed = %s\n", ERR_reason_error_string(err)); FreeRSAKey(newkey); return false; } /* proposition S5 - conditional */ if ((len = ReceiveTransaction(conn->sd, in, NULL)) <= 0) { cfPS(cf_inform, CF_INTERPT, "", pp, attr, "Protocol error in RSA authentation from IP %s\n", pp->this_server); FreeRSAKey(newkey); return false; } if ((newkey->e = BN_mpi2bn(in, len, NULL)) == NULL) { err = ERR_get_error(); cfPS(cf_error, CF_INTERPT, "", pp, attr, "Public key decrypt failed = %s\n", ERR_reason_error_string(err)); FreeRSAKey(newkey); return false; } server_pubkey = RSAPublicKey_dup(newkey); FreeRSAKey(newkey); } /* proposition C5 */ SetSessionKey(conn); if (conn->session_key == NULL) { CfOut(cf_error, "", "A random session key could not be established"); FreeRSAKey(server_pubkey); return false; } encrypted_len = RSA_size(server_pubkey); CfDebug("Encrypt %d bytes of session key into %d RSA bytes\n", session_size, encrypted_len); out = xmalloc(encrypted_len); if (RSA_public_encrypt(session_size, conn->session_key, out, server_pubkey, RSA_PKCS1_PADDING) <= 0) { err = ERR_get_error(); cfPS(cf_error, CF_INTERPT, "", pp, attr, "Public encryption failed = %s\n", ERR_reason_error_string(err)); free(out); FreeRSAKey(server_pubkey); return false; } SendTransaction(conn->sd, out, encrypted_len, CF_DONE); if (server_pubkey != NULL) { HashPubKey(server_pubkey, conn->digest, CF_DEFAULT_DIGEST); CfOut(cf_verbose, "", " -> Public key identity of host \"%s\" is \"%s\"", conn->remoteip, HashPrint(CF_DEFAULT_DIGEST, conn->digest)); SavePublicKey(conn->username, conn->remoteip, HashPrint(CF_DEFAULT_DIGEST, conn->digest), server_pubkey); // FIXME: username is local LastSaw(conn->remoteip, conn->digest, cf_connect); } free(out); FreeRSAKey(server_pubkey); return true; }
/* Returning NULL (an empty list) does not mean empty directory but ERROR, * since every directory has to contain at least . and .. */ Item *RemoteDirList(const char *dirname, bool encrypt, AgentConnection *conn) { char sendbuffer[CF_BUFSIZE]; char recvbuffer[CF_BUFSIZE]; char in[CF_BUFSIZE]; char out[CF_BUFSIZE]; int cipherlen = 0, tosend; if (strlen(dirname) > CF_BUFSIZE - 20) { Log(LOG_LEVEL_ERR, "Directory name too long"); return NULL; } /* We encrypt only for CLASSIC protocol. The TLS protocol is always over * encrypted layer, so it does not support encrypted (S*) commands. */ encrypt = encrypt && conn->conn_info->protocol == CF_PROTOCOL_CLASSIC; if (encrypt) { if (conn->session_key == NULL) { Log(LOG_LEVEL_ERR, "Cannot do encrypted copy without keys (use cf-key)"); return NULL; } snprintf(in, CF_BUFSIZE, "OPENDIR %s", dirname); cipherlen = EncryptString(conn->encryption_type, in, out, conn->session_key, strlen(in) + 1); snprintf(sendbuffer, CF_BUFSIZE - 1, "SOPENDIR %d", cipherlen); memcpy(sendbuffer + CF_PROTO_OFFSET, out, cipherlen); tosend = cipherlen + CF_PROTO_OFFSET; } else { snprintf(sendbuffer, CF_BUFSIZE, "OPENDIR %s", dirname); tosend = strlen(sendbuffer); } if (SendTransaction(conn->conn_info, sendbuffer, tosend, CF_DONE) == -1) { return NULL; } Item *start = NULL, *end = NULL; /* NULL == empty list */ while (true) { /* TODO check the CF_MORE flag, no need for CFD_TERMINATOR. */ int nbytes = ReceiveTransaction(conn->conn_info, recvbuffer, NULL); /* If recv error or socket closed before receiving CFD_TERMINATOR. */ if (nbytes == -1 || nbytes == 0) { /* TODO mark connection in the cache as closed. */ goto err; } if (recvbuffer[0] == '\0') { Log(LOG_LEVEL_ERR, "Empty%s server packet when listing directory '%s'!", (start == NULL) ? " first" : "", dirname); goto err; } if (encrypt) { memcpy(in, recvbuffer, nbytes); DecryptString(conn->encryption_type, in, recvbuffer, conn->session_key, nbytes); } if (FailedProtoReply(recvbuffer)) { Log(LOG_LEVEL_INFO, "Network access to '%s:%s' denied", conn->this_server, dirname); goto err; } if (BadProtoReply(recvbuffer)) { Log(LOG_LEVEL_INFO, "%s", recvbuffer + strlen("BAD: ")); goto err; } /* Double '\0' means end of packet. */ for (char *sp = recvbuffer; *sp != '\0'; sp += strlen(sp) + 1) { if (strcmp(sp, CFD_TERMINATOR) == 0) /* end of all packets */ { return start; } Item *ip = xcalloc(1, sizeof(Item)); ip->name = (char *) AllocateDirentForFilename(sp); if (start == NULL) /* First element */ { start = ip; end = ip; } else { end->next = ip; end = ip; } } } return start; err: /* free list */ for (Item *ip = start; ip != NULL; ip = start) { start = ip->next; free(ip->name); free(ip); } return NULL; }