static void encrypt_seskey (DEK *dek, DEK **seskey, byte *enckey) { gcry_cipher_hd_t hd; byte buf[33]; assert ( dek->keylen <= 32 ); if (!*seskey) { *seskey=xmalloc_clear(sizeof(DEK)); (*seskey)->keylen=dek->keylen; (*seskey)->algo=dek->algo; make_session_key(*seskey); /*log_hexdump( "thekey", c->key, c->keylen );*/ } /* The encrypted session key is prefixed with a one-octet algorithm id. */ buf[0] = (*seskey)->algo; memcpy( buf + 1, (*seskey)->key, (*seskey)->keylen ); /* We only pass already checked values to the following fucntion, thus we consider any failure as fatal. */ if (openpgp_cipher_open (&hd, dek->algo, GCRY_CIPHER_MODE_CFB, 1)) BUG (); if (gcry_cipher_setkey (hd, dek->key, dek->keylen)) BUG (); gcry_cipher_setiv (hd, NULL, 0); gcry_cipher_encrypt (hd, buf, (*seskey)->keylen + 1, NULL, 0); gcry_cipher_close (hd); memcpy( enckey, buf, (*seskey)->keylen + 1 ); wipememory( buf, sizeof buf ); /* burn key */ }
static void encode_seskey( DEK *dek, DEK **seskey, byte *enckey ) { CIPHER_HANDLE hd; byte buf[33]; assert ( dek->keylen <= 32 ); if(!*seskey) { *seskey=xmalloc_clear(sizeof(DEK)); (*seskey)->keylen=dek->keylen; (*seskey)->algo=dek->algo; make_session_key(*seskey); /*log_hexdump( "thekey", c->key, c->keylen );*/ } buf[0] = (*seskey)->algo; memcpy( buf + 1, (*seskey)->key, (*seskey)->keylen ); hd = cipher_open( dek->algo, CIPHER_MODE_CFB, 1 ); cipher_setkey( hd, dek->key, dek->keylen ); cipher_setiv( hd, NULL, 0 ); cipher_encrypt( hd, buf, buf, (*seskey)->keylen + 1 ); cipher_close( hd ); memcpy( enckey, buf, (*seskey)->keylen + 1 ); wipememory( buf, sizeof buf ); /* burn key */ }
/**************** * Filter to do a complete public key encryption. */ int encrypt_filter( void *opaque, int control, IOBUF a, byte *buf, size_t *ret_len) { size_t size = *ret_len; encrypt_filter_context_t *efx = opaque; int rc=0; if( control == IOBUFCTRL_UNDERFLOW ) { /* decrypt */ BUG(); /* not used */ } else if( control == IOBUFCTRL_FLUSH ) { /* encrypt */ if( !efx->header_okay ) { efx->cfx.dek = xmalloc_secure_clear( sizeof *efx->cfx.dek ); if( !opt.def_cipher_algo ) { /* try to get it from the prefs */ efx->cfx.dek->algo = select_algo_from_prefs(efx->pk_list,PREFTYPE_SYM,-1,NULL); if( efx->cfx.dek->algo == -1 ) { /* because 3DES is implicitly in the prefs, this can only * happen if we do not have any public keys in the list */ efx->cfx.dek->algo = DEFAULT_CIPHER_ALGO; } } else { if(!opt.expert && select_algo_from_prefs(efx->pk_list,PREFTYPE_SYM, opt.def_cipher_algo, NULL)!=opt.def_cipher_algo) log_info(_("forcing symmetric cipher %s (%d) " "violates recipient preferences\n"), cipher_algo_to_string(opt.def_cipher_algo), opt.def_cipher_algo); efx->cfx.dek->algo = opt.def_cipher_algo; } efx->cfx.dek->use_mdc = use_mdc(efx->pk_list,efx->cfx.dek->algo); make_session_key( efx->cfx.dek ); if( DBG_CIPHER ) log_hexdump("DEK is: ", efx->cfx.dek->key, efx->cfx.dek->keylen ); rc = write_pubkey_enc_from_list( efx->pk_list, efx->cfx.dek, a ); if( rc ) return rc; if(efx->symkey_s2k && efx->symkey_dek) { rc=write_symkey_enc(efx->symkey_s2k,efx->symkey_dek, efx->cfx.dek,a); if(rc) return rc; } iobuf_push_filter( a, cipher_filter, &efx->cfx ); efx->header_okay = 1; } rc = iobuf_write( a, buf, size ); } else if( control == IOBUFCTRL_FREE ) { xfree(efx->symkey_dek); xfree(efx->symkey_s2k); } else if( control == IOBUFCTRL_DESC ) { *(char**)buf = "encrypt_filter"; } return rc; }
/**************** * Encrypt the file with the given userids (or ask if none * is supplied). */ int encode_crypt( const char *filename, STRLIST remusr, int use_symkey ) { IOBUF inp = NULL, out = NULL; PACKET pkt; PKT_plaintext *pt = NULL; DEK *symkey_dek = NULL; STRING2KEY *symkey_s2k = NULL; int rc = 0, rc2 = 0; u32 filesize; cipher_filter_context_t cfx; armor_filter_context_t afx; compress_filter_context_t zfx; text_filter_context_t tfx; progress_filter_context_t pfx; PK_LIST pk_list,work_list; int do_compress = opt.compress_algo && !RFC1991; memset( &cfx, 0, sizeof cfx); memset( &afx, 0, sizeof afx); memset( &zfx, 0, sizeof zfx); memset( &tfx, 0, sizeof tfx); init_packet(&pkt); if(use_symkey && (rc=setup_symkey(&symkey_s2k,&symkey_dek))) return rc; if( (rc=build_pk_list( remusr, &pk_list, PUBKEY_USAGE_ENC)) ) return rc; if(PGP2) { for(work_list=pk_list; work_list; work_list=work_list->next) if(!(is_RSA(work_list->pk->pubkey_algo) && nbits_from_pk(work_list->pk)<=2048)) { log_info(_("you can only encrypt to RSA keys of 2048 bits or " "less in --pgp2 mode\n")); compliance_failure(); break; } } /* prepare iobufs */ inp = iobuf_open(filename); if (inp) iobuf_ioctl (inp,3,1,NULL); /* disable fd caching */ if (inp && is_secured_file (iobuf_get_fd (inp))) { iobuf_close (inp); inp = NULL; errno = EPERM; } if( !inp ) { log_error(_("can't open `%s': %s\n"), filename? filename: "[stdin]", strerror(errno) ); rc = G10ERR_OPEN_FILE; goto leave; } else if( opt.verbose ) log_info(_("reading from `%s'\n"), filename? filename: "[stdin]"); handle_progress (&pfx, inp, filename); if( opt.textmode ) iobuf_push_filter( inp, text_filter, &tfx ); if( (rc = open_outfile( filename, opt.armor? 1:0, &out )) ) goto leave; if( opt.armor ) iobuf_push_filter( out, armor_filter, &afx ); /* create a session key */ cfx.dek = xmalloc_secure_clear (sizeof *cfx.dek); if( !opt.def_cipher_algo ) { /* try to get it from the prefs */ cfx.dek->algo = select_algo_from_prefs(pk_list,PREFTYPE_SYM,-1,NULL); /* The only way select_algo_from_prefs can fail here is when mixing v3 and v4 keys, as v4 keys have an implicit preference entry for 3DES, and the pk_list cannot be empty. In this case, use 3DES anyway as it's the safest choice - perhaps the v3 key is being used in an OpenPGP implementation and we know that the implementation behind any v4 key can handle 3DES. */ if( cfx.dek->algo == -1 ) { cfx.dek->algo = CIPHER_ALGO_3DES; if( PGP2 ) { log_info(_("unable to use the IDEA cipher for all of the keys " "you are encrypting to.\n")); compliance_failure(); } } } else { if(!opt.expert && select_algo_from_prefs(pk_list,PREFTYPE_SYM, opt.def_cipher_algo,NULL)!=opt.def_cipher_algo) log_info(_("WARNING: forcing symmetric cipher %s (%d)" " violates recipient preferences\n"), cipher_algo_to_string(opt.def_cipher_algo), opt.def_cipher_algo); cfx.dek->algo = opt.def_cipher_algo; } cfx.dek->use_mdc=use_mdc(pk_list,cfx.dek->algo); /* Only do the is-file-already-compressed check if we are using a MDC. This forces compressed files to be re-compressed if we do not have a MDC to give some protection against chosen ciphertext attacks. */ if (do_compress && cfx.dek->use_mdc && is_file_compressed(filename, &rc2) ) { if (opt.verbose) log_info(_("`%s' already compressed\n"), filename); do_compress = 0; } if (rc2) { rc = rc2; goto leave; } make_session_key( cfx.dek ); if( DBG_CIPHER ) log_hexdump("DEK is: ", cfx.dek->key, cfx.dek->keylen ); rc = write_pubkey_enc_from_list( pk_list, cfx.dek, out ); if( rc ) goto leave; /* We put the passphrase (if any) after any public keys as this seems to be the most useful on the recipient side - there is no point in prompting a user for a passphrase if they have the secret key needed to decrypt. */ if(use_symkey && (rc=write_symkey_enc(symkey_s2k,symkey_dek,cfx.dek,out))) goto leave; if (!opt.no_literal) { /* setup the inner packet */ if( filename || opt.set_filename ) { char *s = make_basename( opt.set_filename ? opt.set_filename : filename, iobuf_get_real_fname( inp ) ); pt = xmalloc( sizeof *pt + strlen(s) - 1 ); pt->namelen = strlen(s); memcpy(pt->name, s, pt->namelen ); xfree(s); } else { /* no filename */ pt = xmalloc( sizeof *pt - 1 ); pt->namelen = 0; } } if (!iobuf_is_pipe_filename (filename) && *filename && !opt.textmode ) { off_t tmpsize; int overflow; if ( !(tmpsize = iobuf_get_filelength(inp, &overflow)) && !overflow ) log_info(_("WARNING: `%s' is an empty file\n"), filename ); /* We can't encode the length of very large files because OpenPGP uses only 32 bit for file sizes. So if the the size of a file is larger than 2^32 minus some bytes for packet headers, we switch to partial length encoding. */ if (tmpsize < (IOBUF_FILELENGTH_LIMIT - 65536) ) filesize = tmpsize; else filesize = 0; } else filesize = opt.set_filesize ? opt.set_filesize : 0; /* stdin */ if (!opt.no_literal) { pt->timestamp = make_timestamp(); pt->mode = opt.textmode ? 't' : 'b'; pt->len = filesize; pt->new_ctb = !pt->len && !RFC1991; pt->buf = inp; pkt.pkttype = PKT_PLAINTEXT; pkt.pkt.plaintext = pt; cfx.datalen = filesize && !do_compress? calc_packet_length( &pkt ) : 0; } else cfx.datalen = filesize && !do_compress ? filesize : 0; /* register the cipher filter */ iobuf_push_filter( out, cipher_filter, &cfx ); /* register the compress filter */ if( do_compress ) { int compr_algo = opt.compress_algo; if(compr_algo==-1) { if((compr_algo= select_algo_from_prefs(pk_list,PREFTYPE_ZIP,-1,NULL))==-1) compr_algo=DEFAULT_COMPRESS_ALGO; /* Theoretically impossible to get here since uncompressed is implicit. */ } else if(!opt.expert && select_algo_from_prefs(pk_list,PREFTYPE_ZIP, compr_algo,NULL)!=compr_algo) log_info(_("WARNING: forcing compression algorithm %s (%d)" " violates recipient preferences\n"), compress_algo_to_string(compr_algo),compr_algo); /* algo 0 means no compression */ if( compr_algo ) { if (cfx.dek && cfx.dek->use_mdc) zfx.new_ctb = 1; push_compress_filter(out,&zfx,compr_algo); } } /* do the work */ if (!opt.no_literal) { if( (rc = build_packet( out, &pkt )) ) log_error("build_packet failed: %s\n", g10_errstr(rc) ); } else { /* user requested not to create a literal packet, so we copy the plain data */ byte copy_buffer[4096]; int bytes_copied; while ((bytes_copied = iobuf_read(inp, copy_buffer, 4096)) != -1) if (iobuf_write(out, copy_buffer, bytes_copied) == -1) { rc = G10ERR_WRITE_FILE; log_error("copying input to output failed: %s\n", g10_errstr(rc) ); break; } wipememory(copy_buffer, 4096); /* burn buffer */ } /* finish the stuff */ leave: iobuf_close(inp); if( rc ) iobuf_cancel(out); else { iobuf_close(out); /* fixme: check returncode */ write_status( STATUS_END_ENCRYPTION ); } if( pt ) pt->buf = NULL; free_packet(&pkt); xfree(cfx.dek); xfree(symkey_dek); xfree(symkey_s2k); release_pk_list( pk_list ); return rc; }
/* * Filter to do a complete public key encryption. */ int encrypt_filter (void *opaque, int control, iobuf_t a, byte *buf, size_t *ret_len) { size_t size = *ret_len; encrypt_filter_context_t *efx = opaque; int rc = 0; if (control == IOBUFCTRL_UNDERFLOW) /* decrypt */ { BUG(); /* not used */ } else if ( control == IOBUFCTRL_FLUSH ) /* encrypt */ { if ( !efx->header_okay ) { efx->cfx.dek = xmalloc_secure_clear ( sizeof *efx->cfx.dek ); if ( !opt.def_cipher_algo ) { /* Try to get it from the prefs. */ efx->cfx.dek->algo = select_algo_from_prefs (efx->pk_list, PREFTYPE_SYM, -1, NULL); if (efx->cfx.dek->algo == -1 ) { /* Because 3DES is implicitly in the prefs, this can only happen if we do not have any public keys in the list. */ efx->cfx.dek->algo = DEFAULT_CIPHER_ALGO; } /* In case 3DES has been selected, print a warning if any key does not have a preference for AES. This should help to indentify why encrypting to several recipients falls back to 3DES. */ if (opt.verbose && efx->cfx.dek->algo == CIPHER_ALGO_3DES) warn_missing_aes_from_pklist (efx->pk_list); } else { if (!opt.expert && select_algo_from_prefs (efx->pk_list,PREFTYPE_SYM, opt.def_cipher_algo, NULL) != opt.def_cipher_algo) log_info(_("forcing symmetric cipher %s (%d) " "violates recipient preferences\n"), openpgp_cipher_algo_name (opt.def_cipher_algo), opt.def_cipher_algo); efx->cfx.dek->algo = opt.def_cipher_algo; } efx->cfx.dek->use_mdc = use_mdc (efx->pk_list,efx->cfx.dek->algo); make_session_key ( efx->cfx.dek ); if (DBG_CRYPTO) log_printhex ("DEK is: ", efx->cfx.dek->key, efx->cfx.dek->keylen); rc = write_pubkey_enc_from_list (efx->pk_list, efx->cfx.dek, a); if (rc) return rc; if(efx->symkey_s2k && efx->symkey_dek) { rc=write_symkey_enc(efx->symkey_s2k,efx->symkey_dek, efx->cfx.dek,a); if(rc) return rc; } iobuf_push_filter (a, cipher_filter, &efx->cfx); efx->header_okay = 1; } rc = iobuf_write (a, buf, size); } else if (control == IOBUFCTRL_FREE) { xfree (efx->symkey_dek); xfree (efx->symkey_s2k); } else if ( control == IOBUFCTRL_DESC ) { *(char**)buf = "encrypt_filter"; } return rc; }
/* * Encrypt the file with the given userids (or ask if none is * supplied). Either FILENAME or FILEFD must be given, but not both. * The caller may provide a checked list of public keys in * PROVIDED_PKS; if not the function builds a list of keys on its own. * * Note that FILEFD is currently only used by cmd_encrypt in the the * not yet finished server.c. */ int encrypt_crypt (ctrl_t ctrl, int filefd, const char *filename, strlist_t remusr, int use_symkey, pk_list_t provided_keys, int outputfd) { iobuf_t inp = NULL; iobuf_t out = NULL; PACKET pkt; PKT_plaintext *pt = NULL; DEK *symkey_dek = NULL; STRING2KEY *symkey_s2k = NULL; int rc = 0, rc2 = 0; u32 filesize; cipher_filter_context_t cfx; armor_filter_context_t *afx = NULL; compress_filter_context_t zfx; text_filter_context_t tfx; progress_filter_context_t *pfx; PK_LIST pk_list; int do_compress; if (filefd != -1 && filename) return gpg_error (GPG_ERR_INV_ARG); /* Both given. */ do_compress = !!opt.compress_algo; pfx = new_progress_context (); memset( &cfx, 0, sizeof cfx); memset( &zfx, 0, sizeof zfx); memset( &tfx, 0, sizeof tfx); init_packet(&pkt); if (use_symkey && (rc=setup_symkey(&symkey_s2k,&symkey_dek))) { release_progress_context (pfx); return rc; } if (provided_keys) pk_list = provided_keys; else { if ((rc = build_pk_list (ctrl, remusr, &pk_list, PUBKEY_USAGE_ENC))) { release_progress_context (pfx); return rc; } } /* Prepare iobufs. */ #ifdef HAVE_W32_SYSTEM if (filefd == -1) inp = iobuf_open_fd_or_name (GNUPG_INVALID_FD, filename, "rb"); else { inp = NULL; gpg_err_set_errno (ENOSYS); } #else inp = iobuf_open_fd_or_name (filefd, filename, "rb"); #endif if (inp) iobuf_ioctl (inp, IOBUF_IOCTL_NO_CACHE, 1, NULL); if (inp && is_secured_file (iobuf_get_fd (inp))) { iobuf_close (inp); inp = NULL; gpg_err_set_errno (EPERM); } if (!inp) { char xname[64]; rc = gpg_error_from_syserror (); if (filefd != -1) snprintf (xname, sizeof xname, "[fd %d]", filefd); else if (!filename) strcpy (xname, "[stdin]"); else *xname = 0; log_error (_("can't open '%s': %s\n"), *xname? xname : filename, gpg_strerror (rc) ); goto leave; } if (opt.verbose) log_info (_("reading from '%s'\n"), iobuf_get_fname_nonnull (inp)); handle_progress (pfx, inp, filename); if (opt.textmode) iobuf_push_filter (inp, text_filter, &tfx); rc = open_outfile (outputfd, filename, opt.armor? 1:0, 0, &out); if (rc) goto leave; if (opt.armor) { afx = new_armor_context (); push_armor_filter (afx, out); } /* Create a session key. */ cfx.dek = xmalloc_secure_clear (sizeof *cfx.dek); if (!opt.def_cipher_algo) { /* Try to get it from the prefs. */ cfx.dek->algo = select_algo_from_prefs (pk_list, PREFTYPE_SYM, -1, NULL); /* The only way select_algo_from_prefs can fail here is when mixing v3 and v4 keys, as v4 keys have an implicit preference entry for 3DES, and the pk_list cannot be empty. In this case, use 3DES anyway as it's the safest choice - perhaps the v3 key is being used in an OpenPGP implementation and we know that the implementation behind any v4 key can handle 3DES. */ if (cfx.dek->algo == -1) { cfx.dek->algo = CIPHER_ALGO_3DES; } /* In case 3DES has been selected, print a warning if any key does not have a preference for AES. This should help to indentify why encrypting to several recipients falls back to 3DES. */ if (opt.verbose && cfx.dek->algo == CIPHER_ALGO_3DES) warn_missing_aes_from_pklist (pk_list); } else { if (!opt.expert && (select_algo_from_prefs (pk_list, PREFTYPE_SYM, opt.def_cipher_algo, NULL) != opt.def_cipher_algo)) { log_info(_("WARNING: forcing symmetric cipher %s (%d)" " violates recipient preferences\n"), openpgp_cipher_algo_name (opt.def_cipher_algo), opt.def_cipher_algo); } cfx.dek->algo = opt.def_cipher_algo; } cfx.dek->use_mdc = use_mdc (pk_list,cfx.dek->algo); /* Only do the is-file-already-compressed check if we are using a MDC. This forces compressed files to be re-compressed if we do not have a MDC to give some protection against chosen ciphertext attacks. */ if (do_compress && cfx.dek->use_mdc && is_file_compressed(filename, &rc2)) { if (opt.verbose) log_info(_("'%s' already compressed\n"), filename); do_compress = 0; } if (rc2) { rc = rc2; goto leave; } make_session_key (cfx.dek); if (DBG_CRYPTO) log_printhex ("DEK is: ", cfx.dek->key, cfx.dek->keylen ); rc = write_pubkey_enc_from_list (pk_list, cfx.dek, out); if (rc) goto leave; /* We put the passphrase (if any) after any public keys as this seems to be the most useful on the recipient side - there is no point in prompting a user for a passphrase if they have the secret key needed to decrypt. */ if(use_symkey && (rc = write_symkey_enc(symkey_s2k,symkey_dek,cfx.dek,out))) goto leave; if (!opt.no_literal) pt = setup_plaintext_name (filename, inp); /* Get the size of the file if possible, i.e., if it is a real file. */ if (filename && *filename && !iobuf_is_pipe_filename (filename) && !opt.textmode ) { off_t tmpsize; int overflow; if ( !(tmpsize = iobuf_get_filelength(inp, &overflow)) && !overflow && opt.verbose) log_info(_("WARNING: '%s' is an empty file\n"), filename ); /* We can't encode the length of very large files because OpenPGP uses only 32 bit for file sizes. So if the the size of a file is larger than 2^32 minus some bytes for packet headers, we switch to partial length encoding. */ if (tmpsize < (IOBUF_FILELENGTH_LIMIT - 65536) ) filesize = tmpsize; else filesize = 0; } else filesize = opt.set_filesize ? opt.set_filesize : 0; /* stdin */ if (!opt.no_literal) { pt->timestamp = make_timestamp(); pt->mode = opt.textmode ? 't' : 'b'; pt->len = filesize; pt->new_ctb = !pt->len; pt->buf = inp; pkt.pkttype = PKT_PLAINTEXT; pkt.pkt.plaintext = pt; cfx.datalen = filesize && !do_compress? calc_packet_length( &pkt ) : 0; } else cfx.datalen = filesize && !do_compress ? filesize : 0; /* Register the cipher filter. */ iobuf_push_filter (out, cipher_filter, &cfx); /* Register the compress filter. */ if (do_compress) { int compr_algo = opt.compress_algo; if (compr_algo == -1) { compr_algo = select_algo_from_prefs (pk_list, PREFTYPE_ZIP, -1, NULL); if (compr_algo == -1) compr_algo = DEFAULT_COMPRESS_ALGO; /* Theoretically impossible to get here since uncompressed is implicit. */ } else if (!opt.expert && select_algo_from_prefs(pk_list, PREFTYPE_ZIP, compr_algo, NULL) != compr_algo) { log_info (_("WARNING: forcing compression algorithm %s (%d)" " violates recipient preferences\n"), compress_algo_to_string(compr_algo), compr_algo); } /* Algo 0 means no compression. */ if (compr_algo) { if (cfx.dek && cfx.dek->use_mdc) zfx.new_ctb = 1; push_compress_filter (out,&zfx,compr_algo); } } /* Do the work. */ if (!opt.no_literal) { if ((rc = build_packet( out, &pkt ))) log_error ("build_packet failed: %s\n", gpg_strerror (rc)); } else { /* User requested not to create a literal packet, so we copy the plain data. */ byte copy_buffer[4096]; int bytes_copied; while ((bytes_copied = iobuf_read (inp, copy_buffer, 4096)) != -1) { rc = iobuf_write (out, copy_buffer, bytes_copied); if (rc) { log_error ("copying input to output failed: %s\n", gpg_strerror (rc)); break; } } wipememory (copy_buffer, 4096); /* Burn the buffer. */ } /* Finish the stuff. */ leave: iobuf_close (inp); if (rc) iobuf_cancel (out); else { iobuf_close (out); /* fixme: check returncode */ write_status (STATUS_END_ENCRYPTION); } if (pt) pt->buf = NULL; free_packet (&pkt); xfree (cfx.dek); xfree (symkey_dek); xfree (symkey_s2k); if (!provided_keys) release_pk_list (pk_list); release_armor_context (afx); release_progress_context (pfx); return rc; }
/* Encrypt a session key using DEK and store a pointer to the result * at R_ENCKEY and its length at R_ENCKEYLEN. * * R_SESKEY points to the unencrypted session key (.KEY, .KEYLEN) and * the algorithm that will be used to encrypt the contents of the * SKESK packet (.ALGO). If R_SESKEY points to NULL, then a random * session key that is appropriate for DEK->ALGO is generated and * stored at R_SESKEY. If AEAD_ALGO is not 0 the given AEAD algorithm * is used for encryption. */ gpg_error_t encrypt_seskey (DEK *dek, aead_algo_t aead_algo, DEK **r_seskey, void **r_enckey, size_t *r_enckeylen) { gpg_error_t err; gcry_cipher_hd_t hd = NULL; byte *buf = NULL; DEK *seskey; *r_enckey = NULL; *r_enckeylen = 0; if (*r_seskey) seskey = *r_seskey; else { seskey = xtrycalloc (1, sizeof(DEK)); if (!seskey) { err = gpg_error_from_syserror (); goto leave; } seskey->algo = dek->algo; make_session_key (seskey); /*log_hexdump( "thekey", c->key, c->keylen );*/ } if (aead_algo) { unsigned int noncelen; enum gcry_cipher_modes ciphermode; byte ad[4]; err = openpgp_aead_algo_info (aead_algo, &ciphermode, &noncelen); if (err) goto leave; /* Allocate space for the nonce, the key, and the authentication * tag (16). */ buf = xtrymalloc_secure (noncelen + seskey->keylen + 16); if (!buf) { err = gpg_error_from_syserror (); goto leave; } gcry_randomize (buf, noncelen, GCRY_STRONG_RANDOM); err = openpgp_cipher_open (&hd, dek->algo, ciphermode, GCRY_CIPHER_SECURE); if (!err) err = gcry_cipher_setkey (hd, dek->key, dek->keylen); if (!err) err = gcry_cipher_setiv (hd, buf, noncelen); if (err) goto leave; ad[0] = (0xc0 | PKT_SYMKEY_ENC); ad[1] = 5; ad[2] = dek->algo; ad[3] = aead_algo; err = gcry_cipher_authenticate (hd, ad, 4); if (err) goto leave; memcpy (buf + noncelen, seskey->key, seskey->keylen); gcry_cipher_final (hd); err = gcry_cipher_encrypt (hd, buf + noncelen, seskey->keylen, NULL,0); if (err) goto leave; err = gcry_cipher_gettag (hd, buf + noncelen + seskey->keylen, 16); if (err) goto leave; *r_enckeylen = noncelen + seskey->keylen + 16; *r_enckey = buf; buf = NULL; } else { /* In the old version 4 SKESK the encrypted session key is * prefixed with a one-octet algorithm id. */ buf = xtrymalloc_secure (1 + seskey->keylen); if (!buf) { err = gpg_error_from_syserror (); goto leave; } buf[0] = seskey->algo; memcpy (buf + 1, seskey->key, seskey->keylen); err = openpgp_cipher_open (&hd, dek->algo, GCRY_CIPHER_MODE_CFB, 1); if (!err) err = gcry_cipher_setkey (hd, dek->key, dek->keylen); if (!err) err = gcry_cipher_setiv (hd, NULL, 0); if (!err) err = gcry_cipher_encrypt (hd, buf, seskey->keylen + 1, NULL, 0); if (err) goto leave; *r_enckeylen = seskey->keylen + 1; *r_enckey = buf; buf = NULL; } /* Return the session key in case we allocated it. */ *r_seskey = seskey; seskey = NULL; leave: gcry_cipher_close (hd); if (seskey != *r_seskey) xfree (seskey); xfree (buf); return err; }
/* * Director requests us to start a job * Basic tasks done here: * - We pickup the JobId to be run from the Director. * - We pickup the device, media, and pool from the Director * - Wait for a connection from the File Daemon (FD) * - Accept commands from the FD (i.e. run the job) * - Return when the connection is terminated or * there is an error. */ bool job_cmd(JCR *jcr) { int32_t JobId; char auth_key[MAX_NAME_LENGTH]; char seed[MAX_NAME_LENGTH]; char spool_size[MAX_NAME_LENGTH]; BSOCK *dir = jcr->dir_bsock; POOL_MEM job_name, client_name, job, fileset_name, fileset_md5, backup_format; int32_t JobType, level, spool_attributes, no_attributes, spool_data; int32_t PreferMountedVols, rerunning, protocol, dumplevel; int status; uint64_t quota = 0; JCR *ojcr; /* * Get JobId and permissions from Director */ Dmsg1(100, "<dird: %s", dir->msg); bstrncpy(spool_size, "0", sizeof(spool_size)); status = sscanf(dir->msg, jobcmd, &JobId, job.c_str(), job_name.c_str(), client_name.c_str(), &JobType, &level, fileset_name.c_str(), &no_attributes, &spool_attributes, fileset_md5.c_str(), &spool_data, &PreferMountedVols, spool_size, &rerunning, &jcr->VolSessionId, &jcr->VolSessionTime, "a, &protocol, backup_format.c_str(), &dumplevel); if (status != 20) { pm_strcpy(jcr->errmsg, dir->msg); dir->fsend(BAD_job, status, jcr->errmsg); Dmsg1(100, ">dird: %s", dir->msg); jcr->setJobStatus(JS_ErrorTerminated); return false; } jcr->rerunning = (rerunning) ? true : false; jcr->setJobProtocol(protocol); Dmsg4(100, "rerunning=%d VolSesId=%d VolSesTime=%d Protocol=%d\n", jcr->rerunning, jcr->VolSessionId, jcr->VolSessionTime, jcr->getJobProtocol()); /* * Since this job could be rescheduled, we * check to see if we have it already. If so * free the old jcr and use the new one. */ ojcr = get_jcr_by_full_name(job.c_str()); if (ojcr && !ojcr->authenticated) { Dmsg2(100, "Found ojcr=0x%x Job %s\n", (unsigned)(intptr_t)ojcr, job.c_str()); free_jcr(ojcr); } jcr->JobId = JobId; Dmsg2(800, "Start JobId=%d %p\n", JobId, jcr); /* * If job rescheduled because previous was incomplete, * the Resched flag is set and VolSessionId and VolSessionTime * are given to us (same as restarted job). */ if (!jcr->rerunning) { jcr->VolSessionId = newVolSessionId(); jcr->VolSessionTime = VolSessionTime; } bstrncpy(jcr->Job, job, sizeof(jcr->Job)); unbash_spaces(job_name); jcr->job_name = get_pool_memory(PM_NAME); pm_strcpy(jcr->job_name, job_name); unbash_spaces(client_name); jcr->client_name = get_pool_memory(PM_NAME); pm_strcpy(jcr->client_name, client_name); unbash_spaces(fileset_name); jcr->fileset_name = get_pool_memory(PM_NAME); pm_strcpy(jcr->fileset_name, fileset_name); jcr->setJobType(JobType); jcr->setJobLevel(level); jcr->no_attributes = no_attributes; jcr->spool_attributes = spool_attributes; jcr->spool_data = spool_data; jcr->spool_size = str_to_int64(spool_size); jcr->fileset_md5 = get_pool_memory(PM_NAME); pm_strcpy(jcr->fileset_md5, fileset_md5); jcr->PreferMountedVols = PreferMountedVols; jcr->RemainingQuota = quota; unbash_spaces(backup_format); jcr->backup_format = get_pool_memory(PM_NAME); pm_strcpy(jcr->backup_format, backup_format); jcr->DumpLevel = dumplevel; jcr->authenticated = false; Dmsg1(50, "Quota set as %llu\n", quota); /* * Pass back an authorization key for the File daemon */ bsnprintf(seed, sizeof(seed), "%p%d", jcr, JobId); make_session_key(auth_key, seed, 1); dir->fsend(OK_job, jcr->VolSessionId, jcr->VolSessionTime, auth_key); Dmsg2(50, ">dird jid=%u: %s", (uint32_t)jcr->JobId, dir->msg); jcr->sd_auth_key = bstrdup(auth_key); memset(auth_key, 0, sizeof(auth_key)); dispatch_new_plugin_options(jcr); generate_plugin_event(jcr, bsdEventJobStart, (void *)"JobStart"); return true; }
bool nextrun_cmd(JCR *jcr) { char auth_key[MAX_NAME_LENGTH]; char seed[MAX_NAME_LENGTH]; BSOCK *dir = jcr->dir_bsock; struct timeval tv; struct timezone tz; struct timespec timeout; int errstat = 0; switch (jcr->getJobProtocol()) { case PT_NDMP: /* * We expect a next NDMP backup stream so clear the authenticated flag * and start waiting for the Next backup to Start. */ jcr->authenticated = false; /* * Pass back a new authorization key for the File daemon */ bsnprintf(seed, sizeof(seed), "%p%d", jcr, jcr->JobId); make_session_key(auth_key, seed, 1); dir->fsend(OK_nextrun, auth_key); Dmsg2(50, ">dird jid=%u: %s", (uint32_t)jcr->JobId, dir->msg); if (jcr->sd_auth_key) { free(jcr->sd_auth_key); } jcr->sd_auth_key = bstrdup(auth_key); memset(auth_key, 0, sizeof(auth_key)); jcr->sendJobStatus(JS_WaitFD); /* wait for FD to connect */ gettimeofday(&tv, &tz); timeout.tv_nsec = tv.tv_usec * 1000; timeout.tv_sec = tv.tv_sec + me->client_wait; Dmsg3(50, "%s waiting %d sec for FD to contact SD key=%s\n", jcr->Job, (int)(timeout.tv_sec-time(NULL)), jcr->sd_auth_key); Dmsg2(800, "Wait FD for jid=%d %p\n", jcr->JobId, jcr); P(mutex); while (!jcr->authenticated && !job_canceled(jcr)) { errstat = pthread_cond_timedwait(&jcr->job_start_wait, &mutex, &timeout); if (errstat == ETIMEDOUT || errstat == EINVAL || errstat == EPERM) { break; } Dmsg1(800, "=== Auth cond errstat=%d\n", errstat); } Dmsg3(50, "Auth=%d canceled=%d errstat=%d\n", jcr->authenticated, job_canceled(jcr), errstat); V(mutex); Dmsg2(800, "Auth fail or cancel for jid=%d %p\n", jcr->JobId, jcr); if (jcr->authenticated && !job_canceled(jcr)) { Dmsg2(800, "Running jid=%d %p\n", jcr->JobId, jcr); /* * Wait for the Job to finish. As we want exclusive access to * things like the connection to the director we suspend this * thread and let the actual NDMP connection wake us after it * has performed the backup. E.g. instead of doing a busy wait * we just hang on a conditional variable. */ Dmsg2(800, "Wait for end job jid=%d %p\n", jcr->JobId, jcr); P(mutex); pthread_cond_wait(&jcr->job_end_wait, &mutex); V(mutex); } Dmsg2(800, "Done jid=%d %p\n", jcr->JobId, jcr); /* * For a NDMP backup we expect the protocol to send us either a nextrun cmd * or a finish cmd to let us know they are finished. */ return true; default: Dmsg1(200, "Nextrun_cmd: %s", jcr->dir_bsock->msg); Jmsg2(jcr, M_FATAL, 0, _("Hey!!!! JobId %u Job %s tries to use nextrun cmd while not part of protocol.\n"), (uint32_t)jcr->JobId, jcr->Job); return false; } }
/* * Director requests us to start a job * Basic tasks done here: * - We pickup the JobId to be run from the Director. * - We pickup the device, media, and pool from the Director * - Wait for a connection from the File Daemon (FD) * - Accept commands from the FD (i.e. run the job) * - Return when the connection is terminated or * there is an error. */ bool job_cmd(JCR *jcr) { int32_t JobId; char sd_auth_key[200]; char spool_size[30]; char seed[100]; BSOCK *dir = jcr->dir_bsock; POOL_MEM job_name, client_name, job, fileset_name, fileset_md5; int32_t JobType, level, spool_attributes, no_attributes, spool_data; int32_t write_part_after_job, PreferMountedVols; int32_t rerunning; int32_t is_client; int stat; JCR *ojcr; /* * Get JobId and permissions from Director */ Dmsg1(100, "<dird: %s", dir->msg); bstrncpy(spool_size, "0", sizeof(spool_size)); stat = sscanf(dir->msg, jobcmd, &JobId, job.c_str(), job_name.c_str(), client_name.c_str(), &JobType, &level, fileset_name.c_str(), &no_attributes, &spool_attributes, fileset_md5.c_str(), &spool_data, &write_part_after_job, &PreferMountedVols, spool_size, &rerunning, &jcr->VolSessionId, &jcr->VolSessionTime, &is_client, &sd_auth_key); if (stat != 19) { pm_strcpy(jcr->errmsg, dir->msg); dir->fsend(BAD_job, stat, jcr->errmsg); Dmsg1(100, ">dird: %s", dir->msg); jcr->setJobStatus(JS_ErrorTerminated); return false; } jcr->rerunning = rerunning; jcr->sd_client = is_client; if (is_client) { jcr->sd_auth_key = bstrdup(sd_auth_key); } Dmsg3(100, "rerunning=%d VolSesId=%d VolSesTime=%d\n", jcr->rerunning, jcr->VolSessionId, jcr->VolSessionTime); /* * Since this job could be rescheduled, we * check to see if we have it already. If so * free the old jcr and use the new one. */ ojcr = get_jcr_by_full_name(job.c_str()); if (ojcr && !ojcr->authenticated) { Dmsg2(100, "Found ojcr=0x%x Job %s\n", (unsigned)(intptr_t)ojcr, job.c_str()); free_jcr(ojcr); } jcr->JobId = JobId; Dmsg2(800, "Start JobId=%d %p\n", JobId, jcr); set_jcr_in_tsd(jcr); /* * If job rescheduled because previous was incomplete, * the Resched flag is set and VolSessionId and VolSessionTime * are given to us (same as restarted job). */ if (!jcr->rerunning) { jcr->VolSessionId = newVolSessionId(); jcr->VolSessionTime = VolSessionTime; } bstrncpy(jcr->Job, job, sizeof(jcr->Job)); unbash_spaces(job_name); jcr->job_name = get_pool_memory(PM_NAME); pm_strcpy(jcr->job_name, job_name); unbash_spaces(client_name); jcr->client_name = get_pool_memory(PM_NAME); pm_strcpy(jcr->client_name, client_name); unbash_spaces(fileset_name); jcr->fileset_name = get_pool_memory(PM_NAME); pm_strcpy(jcr->fileset_name, fileset_name); jcr->setJobType(JobType); jcr->setJobLevel(level); jcr->no_attributes = no_attributes; jcr->spool_attributes = spool_attributes; jcr->spool_data = spool_data; jcr->spool_size = str_to_int64(spool_size); jcr->write_part_after_job = write_part_after_job; jcr->fileset_md5 = get_pool_memory(PM_NAME); pm_strcpy(jcr->fileset_md5, fileset_md5); jcr->PreferMountedVols = PreferMountedVols; jcr->authenticated = false; /* * Pass back an authorization key for the File daemon */ if (jcr->sd_client) { bstrncpy(sd_auth_key, "xxx", 3); } else { bsnprintf(seed, sizeof(seed), "%p%d", jcr, JobId); make_session_key(sd_auth_key, seed, 1); } dir->fsend(OKjob, jcr->VolSessionId, jcr->VolSessionTime, sd_auth_key); Dmsg2(150, ">dird jid=%u: %s", (uint32_t)jcr->JobId, dir->msg); /* If not client, set key, otherwise it is already set */ if (!jcr->sd_client) { jcr->sd_auth_key = bstrdup(sd_auth_key); memset(sd_auth_key, 0, sizeof(sd_auth_key)); } new_plugins(jcr); /* instantiate the plugins */ generate_daemon_event(jcr, "JobStart"); generate_plugin_event(jcr, bsdEventJobStart, (void *)"JobStart"); return true; }