/* Create a key description for the CERT, this may be passed to the pinentry. The caller must free the returned string. NULL may be returned on error. */ char * gpgsm_format_keydesc (ksba_cert_t cert) { char *name, *subject, *buffer; ksba_isotime_t t; char created[20]; char expires[20]; char *sn; ksba_sexp_t sexp; char *orig_codeset; name = ksba_cert_get_subject (cert, 0); subject = name? gpgsm_format_name2 (name, 0) : NULL; ksba_free (name); name = NULL; sexp = ksba_cert_get_serial (cert); sn = sexp? gpgsm_format_serial (sexp) : NULL; ksba_free (sexp); ksba_cert_get_validity (cert, 0, t); if (*t) sprintf (created, "%.4s-%.2s-%.2s", t, t+4, t+6); else *created = 0; ksba_cert_get_validity (cert, 1, t); if (*t) sprintf (expires, "%.4s-%.2s-%.2s", t, t+4, t+6); else *expires = 0; orig_codeset = i18n_switchto_utf8 (); name = xtryasprintf (_("Please enter the passphrase to unlock the" " secret key for the X.509 certificate:\n" "\"%s\"\n" "S/N %s, ID 0x%08lX,\n" "created %s, expires %s.\n" ), subject? subject:"?", sn? sn: "?", gpgsm_get_short_fingerprint (cert, NULL), created, expires); i18n_switchback (orig_codeset); if (!name) { xfree (subject); xfree (sn); return NULL; } xfree (subject); xfree (sn); buffer = percent_plus_escape (name); xfree (name); return buffer; }
/* Create a new runner context. On success a new runner object is stored at R_RUNNER. On failure NULL is stored at this address and an error code returned. */ gpg_error_t runner_new (runner_t *r_runner, const char *name) { static unsigned int namecounter; /* Global name counter. */ char *namebuffer; runner_t runner, r; *r_runner = NULL; runner = xtrycalloc (1, sizeof *runner); if (!runner) return gpg_error_from_syserror (); /* Bump up the namecounter. In case we ever had an overflow we check that this number is currently not in use. The algorithm is a bit lame but should be sufficient because such an wrap is not very likely: Assuming that we do a mount 10 times a second, then we would overwrap on a 32 bit system after 13 years. */ do { namecounter++; for (r = running_threads; r; r = r->next_running) if (r->identifier == namecounter) break; } while (r); runner->identifier = namecounter; runner->name = namebuffer = xtryasprintf ("%s-%d", name, namecounter); if (!runner->name) { xfree (runner); return gpg_error_from_syserror (); } runner->refcount = 1; runner->pid = (pid_t)(-1); runner->in_fd = -1; runner->out_fd = -1; *r_runner = runner; return 0; }
/* Issue an SCD SERIALNO openpgp command and if SERIALNO is not NULL ask the user to insert the requested card. */ gpg_error_t select_openpgp (const char *serialno) { gpg_error_t err; /* Send the serialno command to initialize the connection. Without a given S/N we don't care about the data returned. If the card has already been initialized, this is a very fast command. We request the openpgp card because that is what we expect. Note that an opt.limit_card_insert_tries of 1 means: No tries at all whereas 0 means do not limit the number of tries. Due to the sue of a pinentry prompt with a cancel option we use it here in a boolean sense. */ if (!serialno || opt.limit_card_insert_tries == 1) err = assuan_transact (agent_ctx, "SCD SERIALNO openpgp", NULL, NULL, NULL, NULL, NULL, NULL); else { char *this_sn = NULL; char *desc; int ask; char *want_sn; char *p; want_sn = xtrystrdup (serialno); if (!want_sn) return gpg_error_from_syserror (); p = strchr (want_sn, '/'); if (p) *p = 0; do { ask = 0; err = assuan_transact (agent_ctx, "SCD SERIALNO openpgp", NULL, NULL, NULL, NULL, get_serialno_cb, &this_sn); if (gpg_err_code (err) == GPG_ERR_CARD_NOT_PRESENT) ask = 1; else if (gpg_err_code (err) == GPG_ERR_NOT_SUPPORTED) ask = 2; else if (err) ; else if (this_sn) { if (strcmp (want_sn, this_sn)) ask = 2; } xfree (this_sn); this_sn = NULL; if (ask) { char *formatted = NULL; char *ocodeset = i18n_switchto_utf8 (); if (!strncmp (want_sn, "D27600012401", 12) && strlen (want_sn) == 32 ) formatted = xtryasprintf ("(%.4s) %.8s", want_sn + 16, want_sn + 20); err = 0; desc = xtryasprintf ("%s:\n\n" " \"%s\"", ask == 1 ? _("Please insert the card with serial number") : _("Please remove the current card and " "insert the one with serial number"), formatted? formatted : want_sn); if (!desc) err = gpg_error_from_syserror (); xfree (formatted); i18n_switchback (ocodeset); if (!err) err = gpg_agent_get_confirmation (desc); xfree (desc); } } while (ask && !err); xfree (want_sn); } return err; }
/* Return an allocated utf-8 string describing the key PK. If ESCAPED is true spaces and control characters are percent or plus escaped. MODE describes the use of the key description; use one of the FORMAT_KEYDESC_ macros. */ char * gpg_format_keydesc (PKT_public_key *pk, int mode, int escaped) { char *uid; size_t uidlen; const char *algo_name; const char *timestr; char *orig_codeset; char *maink; char *desc; const char *prompt; const char *trailer = ""; int is_subkey; is_subkey = (pk->main_keyid[0] && pk->main_keyid[1] && pk->keyid[0] != pk->main_keyid[0] && pk->keyid[1] != pk->main_keyid[1]); algo_name = openpgp_pk_algo_name (pk->pubkey_algo); timestr = strtimestamp (pk->timestamp); uid = get_user_id (is_subkey? pk->main_keyid:pk->keyid, &uidlen); orig_codeset = i18n_switchto_utf8 (); if (is_subkey) maink = xtryasprintf (_(" (main key ID %s)"), keystr (pk->main_keyid)); else maink = NULL; switch (mode) { case FORMAT_KEYDESC_NORMAL: prompt = _("Please enter the passphrase to unlock the" " OpenPGP secret key:"); break; case FORMAT_KEYDESC_IMPORT: prompt = _("Please enter the passphrase to import the" " OpenPGP secret key:"); break; case FORMAT_KEYDESC_EXPORT: if (is_subkey) prompt = _("Please enter the passphrase to export the" " OpenPGP secret subkey:"); else prompt = _("Please enter the passphrase to export the" " OpenPGP secret key:"); break; case FORMAT_KEYDESC_DELKEY: if (is_subkey) prompt = _("Do you really want to permanently delete the" " OpenPGP secret subkey key:"); else prompt = _("Do you really want to permanently delete the" " OpenPGP secret key:"); trailer = "?"; break; default: prompt = "?"; break; } desc = xtryasprintf (_("%s\n" "\"%.*s\"\n" "%u-bit %s key, ID %s,\n" "created %s%s.\n%s"), prompt, (int)uidlen, uid, nbits_from_pk (pk), algo_name, keystr (pk->keyid), timestr, maink?maink:"", trailer); xfree (maink); xfree (uid); i18n_switchback (orig_codeset); if (escaped) { char *tmp = percent_plus_escape (desc); xfree (desc); desc = tmp; } return desc; }
/* Callback function to try the unprotection from the passpharse query code. */ static int try_unprotect_cb (struct pin_entry_info_s *pi) { struct try_unprotect_arg_s *arg = pi->check_cb_arg; size_t dummy; gpg_error_t err; gnupg_isotime_t now, protected_at, tmptime; char *desc = NULL; assert (!arg->unprotected_key); arg->change_required = 0; err = agent_unprotect (arg->protected_key, pi->pin, protected_at, &arg->unprotected_key, &dummy); if (err) return err; if (!opt.max_passphrase_days || arg->ctrl->in_passwd) return 0; /* No regular passphrase change required. */ if (!*protected_at) { /* No protection date known - must force passphrase change. */ desc = xtrystrdup (_("Note: This passphrase has never been changed.%0A" "Please change it now.")); if (!desc) return gpg_error_from_syserror (); } else { gnupg_get_isotime (now); gnupg_copy_time (tmptime, protected_at); err = add_days_to_isotime (tmptime, opt.max_passphrase_days); if (err) return err; if (strcmp (now, tmptime) > 0 ) { /* Passphrase "expired". */ desc = xtryasprintf (_("This passphrase has not been changed%%0A" "since %.4s-%.2s-%.2s. Please change it now."), protected_at, protected_at+4, protected_at+6); if (!desc) return gpg_error_from_syserror (); } } if (desc) { /* Change required. */ if (opt.enforce_passphrase_constraints) { err = agent_get_confirmation (arg->ctrl, desc, _("Change passphrase"), NULL, 0); if (!err) arg->change_required = 1; } else { err = agent_get_confirmation (arg->ctrl, desc, _("Change passphrase"), _("I'll change it later"), 0); if (!err) arg->change_required = 1; else if (gpg_err_code (err) == GPG_ERR_CANCELED) err = 0; } xfree (desc); } return 0; }
/* Startup the server. CTRL must have been allocated by the caller and set to the default values. */ int gpg_server (ctrl_t ctrl) { int rc; #ifndef HAVE_W32_SYSTEM int filedes[2]; #endif assuan_context_t ctx = NULL; static const char hello[] = ("GNU Privacy Guard's OpenPGP server " VERSION " ready"); /* We use a pipe based server so that we can work from scripts. assuan_init_pipe_server will automagically detect when we are called with a socketpair and ignore FILEDES in this case. */ #ifndef HAVE_W32_SYSTEM filedes[0] = assuan_fdopen (0); filedes[1] = assuan_fdopen (1); #endif rc = assuan_new (&ctx); if (rc) { log_error ("failed to allocate the assuan context: %s\n", gpg_strerror (rc)); goto leave; } #ifdef HAVE_W32_SYSTEM rc = gpg_error (GPG_ERR_NOT_IMPLEMENTED); #else rc = assuan_init_pipe_server (ctx, filedes); #endif if (rc) { log_error ("failed to initialize the server: %s\n", gpg_strerror (rc)); goto leave; } rc = register_commands (ctx); if (rc) { log_error ("failed to the register commands with Assuan: %s\n", gpg_strerror(rc)); goto leave; } assuan_set_pointer (ctx, ctrl); if (opt.verbose || opt.debug) { char *tmp = NULL; tmp = xtryasprintf ("Home: %s\n" "Config: %s\n" "%s", opt.homedir, "fixme: need config filename", hello); if (tmp) { assuan_set_hello_line (ctx, tmp); xfree (tmp); } } else assuan_set_hello_line (ctx, hello); assuan_register_reset_notify (ctx, reset_notify); assuan_register_input_notify (ctx, input_notify); assuan_register_output_notify (ctx, output_notify); assuan_register_option_handler (ctx, option_handler); ctrl->server_local = xtrycalloc (1, sizeof *ctrl->server_local); if (!ctrl->server_local) { rc = gpg_error_from_syserror (); goto leave; } ctrl->server_local->assuan_ctx = ctx; ctrl->server_local->message_fd = GNUPG_INVALID_FD; for (;;) { rc = assuan_accept (ctx); if (rc == -1) { rc = 0; break; } else if (rc) { log_info ("Assuan accept problem: %s\n", gpg_strerror (rc)); break; } rc = assuan_process (ctx); if (rc) { log_info ("Assuan processing failed: %s\n", gpg_strerror (rc)); continue; } } leave: if (ctrl->server_local) { release_pk_list (ctrl->server_local->recplist); xfree (ctrl->server_local); ctrl->server_local = NULL; } assuan_release (ctx); return rc; }
static gpg_error_t keyserver_get (ctrl_t ctrl, KEYDB_SEARCH_DESC *desc, int ndesc, struct keyserver_spec *keyserver) { gpg_error_t err = 0; char **pattern; int idx, npat; estream_t datastream; /* Create an array filled with a search pattern for each key. The array is delimited by a NULL entry. */ pattern = xtrycalloc (ndesc+1, sizeof *pattern); if (!pattern) return gpg_error_from_syserror (); for (npat=idx=0; idx < ndesc; idx++) { int quiet = 0; if (desc[idx].mode == KEYDB_SEARCH_MODE_FPR20 || desc[idx].mode == KEYDB_SEARCH_MODE_FPR16) { pattern[npat] = xtrymalloc (2+2*20+1); if (!pattern[npat]) err = gpg_error_from_syserror (); else { strcpy (pattern[npat], "0x"); bin2hex (desc[idx].u.fpr, desc[idx].mode == KEYDB_SEARCH_MODE_FPR20? 20 : 16, pattern[npat]+2); npat++; } } else if(desc[idx].mode == KEYDB_SEARCH_MODE_LONG_KID) { pattern[npat] = xtryasprintf ("0x%08lX%08lX", (ulong)desc[idx].u.kid[0], (ulong)desc[idx].u.kid[1]); if (!pattern[npat]) err = gpg_error_from_syserror (); else npat++; } else if(desc[idx].mode == KEYDB_SEARCH_MODE_SHORT_KID) { pattern[npat] = xtryasprintf ("0x%08lX", (ulong)desc[idx].u.kid[1]); if (!pattern[npat]) err = gpg_error_from_syserror (); else npat++; } else if(desc[idx].mode == KEYDB_SEARCH_MODE_EXACT) { /* FIXME: We don't need this. It is used as a dummy by keyserver_fetch which passes an entire URL. Better use a separate function here. */ pattern[npat] = xtrystrdup ("0x0000000000000000"); if (!pattern[npat]) err = gpg_error_from_syserror (); else { npat++; quiet = 1; } } else if (desc[idx].mode == KEYDB_SEARCH_MODE_NONE) continue; else BUG(); if (err) { for (idx=0; idx < npat; idx++) xfree (pattern[idx]); xfree (pattern); return err; } if (!quiet && keyserver) { if (keyserver->host) log_info (_("requesting key %s from %s server %s\n"), keystr_from_desc (&desc[idx]), keyserver->scheme, keyserver->host); else log_info (_("requesting key %s from %s\n"), keystr_from_desc (&desc[idx]), keyserver->uri); } } err = gpg_dirmngr_ks_get (ctrl, pattern, &datastream); for (idx=0; idx < npat; idx++) xfree (pattern[idx]); xfree (pattern); if (!err) { void *stats_handle; stats_handle = import_new_stats_handle(); /* FIXME: Check whether this comment should be moved to dirmngr. Slurp up all the key data. In the future, it might be nice to look for KEY foo OUTOFBAND and FAILED indicators. It's harmless to ignore them, but ignoring them does make gpg complain about "no valid OpenPGP data found". One way to do this could be to continue parsing this line-by-line and make a temp iobuf for each key. */ import_keys_es_stream (ctrl, datastream, stats_handle, NULL, NULL, opt.keyserver_options.import_options); import_print_stats (stats_handle); import_release_stats_handle (stats_handle); } es_fclose (datastream); return err; }
/* Insert the given fpr into our trustdb. We expect FPR to be an all uppercase hexstring of 40 characters. FLAG is either 'P' or 'C'. This function does first check whether that key has already been put into the trustdb and returns success in this case. Before a FPR actually gets inserted, the user is asked by means of the Pinentry whether this is actual want he wants to do. */ gpg_error_t agent_marktrusted (ctrl_t ctrl, const char *name, const char *fpr, int flag) { gpg_error_t err = 0; char *desc; char *fname; estream_t fp; char *fprformatted; char *nameformatted; int is_disabled; int yes_i_trust; /* Check whether we are at all allowed to modify the trustlist. This is useful so that the trustlist may be a symlink to a global trustlist with only admin priviliges to modify it. Of course this is not a secure way of denying access, but it avoids the usual clicking on an Okay button most users are used to. */ fname = make_filename (opt.homedir, "trustlist.txt", NULL); if ( access (fname, W_OK) && errno != ENOENT) { xfree (fname); return gpg_error (GPG_ERR_EPERM); } xfree (fname); if (!agent_istrusted (ctrl, fpr, &is_disabled)) { return 0; /* We already got this fingerprint. Silently return success. */ } /* This feature must explicitly been enabled. */ if (!opt.allow_mark_trusted) return gpg_error (GPG_ERR_NOT_SUPPORTED); if (is_disabled) { /* There is an disabled entry in the trustlist. Return an error so that the user won't be asked again for that one. Changing this flag with the integrated marktrusted feature is and will not be made possible. */ return gpg_error (GPG_ERR_NOT_TRUSTED); } /* Insert a new one. */ nameformatted = reformat_name (name, "%0A "); if (!nameformatted) return gpg_error_from_syserror (); /* First a general question whether this is trusted. */ desc = xtryasprintf ( /* TRANSLATORS: This prompt is shown by the Pinentry and has one special property: A "%%0A" is used by Pinentry to insert a line break. The double percent sign is actually needed because it is also a printf format string. If you need to insert a plain % sign, you need to encode it as "%%25". The "%s" gets replaced by the name as stored in the certificate. */ _("Do you ultimately trust%%0A" " \"%s\"%%0A" "to correctly certify user certificates?"), nameformatted); if (!desc) { xfree (nameformatted); return out_of_core (); } err = agent_get_confirmation (ctrl, desc, _("Yes"), _("No"), 1); xfree (desc); if (!err) yes_i_trust = 1; else if (gpg_err_code (err) == GPG_ERR_NOT_CONFIRMED) yes_i_trust = 0; else { xfree (nameformatted); return err; } fprformatted = insert_colons (fpr); if (!fprformatted) { xfree (nameformatted); return out_of_core (); } /* If the user trusts this certificate he has to verify the fingerprint of course. */ if (yes_i_trust) { desc = xtryasprintf ( /* TRANSLATORS: This prompt is shown by the Pinentry and has one special property: A "%%0A" is used by Pinentry to insert a line break. The double percent sign is actually needed because it is also a printf format string. If you need to insert a plain % sign, you need to encode it as "%%25". The second "%s" gets replaced by a hexdecimal fingerprint string whereas the first one receives the name as stored in the certificate. */ _("Please verify that the certificate identified as:%%0A" " \"%s\"%%0A" "has the fingerprint:%%0A" " %s"), nameformatted, fprformatted); if (!desc) { xfree (fprformatted); xfree (nameformatted); return out_of_core (); } /* TRANSLATORS: "Correct" is the label of a button and intended to be hit if the fingerprint matches the one of the CA. The other button is "the default "Cancel" of the Pinentry. */ err = agent_get_confirmation (ctrl, desc, _("Correct"), _("Wrong"), 1); xfree (desc); if (gpg_err_code (err) == GPG_ERR_NOT_CONFIRMED) yes_i_trust = 0; else if (err) { xfree (fprformatted); xfree (nameformatted); return err; } } /* Now check again to avoid duplicates. We take the lock to make sure that nobody else plays with our file and force a reread. */ lock_trusttable (); agent_reload_trustlist (); if (!agent_istrusted (ctrl, fpr, &is_disabled) || is_disabled) { unlock_trusttable (); xfree (fprformatted); xfree (nameformatted); return is_disabled? gpg_error (GPG_ERR_NOT_TRUSTED) : 0; } fname = make_filename (opt.homedir, "trustlist.txt", NULL); if ( access (fname, F_OK) && errno == ENOENT) { fp = es_fopen (fname, "wx,mode=-rw-r"); if (!fp) { err = gpg_error_from_syserror (); log_error ("can't create `%s': %s\n", fname, gpg_strerror (err)); xfree (fname); unlock_trusttable (); xfree (fprformatted); xfree (nameformatted); return err; } es_fputs (headerblurb, fp); es_fclose (fp); } fp = es_fopen (fname, "a+,mode=-rw-r"); if (!fp) { err = gpg_error_from_syserror (); log_error ("can't open `%s': %s\n", fname, gpg_strerror (err)); xfree (fname); unlock_trusttable (); xfree (fprformatted); xfree (nameformatted); return err; } /* Append the key. */ es_fputs ("\n# ", fp); xfree (nameformatted); nameformatted = reformat_name (name, "\n# "); if (!nameformatted || strchr (name, '\n')) { /* Note that there should never be a LF in NAME but we better play safe and print a sanitized version in this case. */ es_write_sanitized (fp, name, strlen (name), NULL, NULL); } else es_fputs (nameformatted, fp); es_fprintf (fp, "\n%s%s %c\n", yes_i_trust?"":"!", fprformatted, flag); if (es_ferror (fp)) err = gpg_error_from_syserror (); if (es_fclose (fp)) err = gpg_error_from_syserror (); agent_reload_trustlist (); xfree (fname); unlock_trusttable (); xfree (fprformatted); xfree (nameformatted); return err; }
/* Delete the key with GRIP from the disk after having asked for confirmation using DESC_TEXT. If FORCE is set the function won't require a confirmation via Pinentry or warns if the key is also used by ssh. Common error codes are: GPG_ERR_NO_SECKEY GPG_ERR_KEY_ON_CARD GPG_ERR_NOT_CONFIRMED */ gpg_error_t agent_delete_key (ctrl_t ctrl, const char *desc_text, const unsigned char *grip, int force) { gpg_error_t err; gcry_sexp_t s_skey = NULL; unsigned char *buf = NULL; size_t len; char *desc_text_final = NULL; char *comment = NULL; ssh_control_file_t cf = NULL; char hexgrip[40+4+1]; char *default_desc = NULL; err = read_key_file (grip, &s_skey); if (gpg_err_code (err) == GPG_ERR_ENOENT) err = gpg_error (GPG_ERR_NO_SECKEY); if (err) goto leave; err = make_canon_sexp (s_skey, &buf, &len); if (err) goto leave; switch (agent_private_key_type (buf)) { case PRIVATE_KEY_CLEAR: case PRIVATE_KEY_OPENPGP_NONE: case PRIVATE_KEY_PROTECTED: bin2hex (grip, 20, hexgrip); if (!force) { if (!desc_text) { default_desc = xtryasprintf (L_("Do you really want to delete the key identified by keygrip%%0A" " %s%%0A %%C%%0A?"), hexgrip); desc_text = default_desc; } /* Note, that we will take the comment as a C string for display purposes; i.e. all stuff beyond a Nul character is ignored. */ { gcry_sexp_t comment_sexp; comment_sexp = gcry_sexp_find_token (s_skey, "comment", 0); if (comment_sexp) comment = gcry_sexp_nth_string (comment_sexp, 1); gcry_sexp_release (comment_sexp); } if (desc_text) err = modify_description (desc_text, comment? comment:"", s_skey, &desc_text_final); if (err) goto leave; err = agent_get_confirmation (ctrl, desc_text_final, L_("Delete key"), L_("No"), 0); if (err) goto leave; cf = ssh_open_control_file (); if (cf) { if (!ssh_search_control_file (cf, hexgrip, NULL, NULL, NULL)) { err = agent_get_confirmation (ctrl, L_("Warning: This key is also listed for use with SSH!\n" "Deleting the key might remove your ability to " "access remote machines."), L_("Delete key"), L_("No"), 0); if (err) goto leave; } } } err = remove_key_file (grip); break; case PRIVATE_KEY_SHADOWED: err = remove_key_file (grip); break; default: log_error ("invalid private key format\n"); err = gpg_error (GPG_ERR_BAD_SECKEY); break; } leave: ssh_close_control_file (cf); gcry_free (comment); xfree (desc_text_final); xfree (default_desc); xfree (buf); gcry_sexp_release (s_skey); return err; }
/* Map the host name NAME to the actual to be used host name. This allows us to manage round robin DNS names. We use our own strategy to choose one of the hosts. For example we skip those hosts which failed for some time and we stick to one host for a time independent of DNS retry times. If FORCE_RESELECT is true a new host is always selected. The selected host is stored as a malloced string at R_HOST; on error NULL is stored. If we know the port used by the selected host, a string representation is written to R_PORTSTR, otherwise it is left untouched. If R_HTTPFLAGS is not NULL it will receive flags which are to be passed to http_open. If R_POOLNAME is not NULL a malloced name of the pool is stored or NULL if it is not a pool. */ static gpg_error_t map_host (ctrl_t ctrl, const char *name, int force_reselect, char **r_host, char *r_portstr, unsigned int *r_httpflags, char **r_poolname) { gpg_error_t err = 0; hostinfo_t hi; int idx; *r_host = NULL; if (r_httpflags) *r_httpflags = 0; if (r_poolname) *r_poolname = NULL; /* No hostname means localhost. */ if (!name || !*name) { *r_host = xtrystrdup ("localhost"); return *r_host? 0 : gpg_error_from_syserror (); } /* See whether the host is in our table. */ idx = find_hostinfo (name); if (idx == -1 && is_onion_address (name)) { idx = create_new_hostinfo (name); if (idx == -1) return gpg_error_from_syserror (); hi = hosttable[idx]; hi->onion = 1; } else if (idx == -1) { /* We never saw this host. Allocate a new entry. */ dns_addrinfo_t aibuf, ai; int *reftbl; size_t reftblsize; int refidx; int is_pool = 0; char *cname; #ifdef USE_DNS_SRV char *srvrecord; struct srventry *srvs; int srvscount; #endif /* USE_DNS_SRV */ reftblsize = 100; reftbl = xtrymalloc (reftblsize * sizeof *reftbl); if (!reftbl) return gpg_error_from_syserror (); refidx = 0; idx = create_new_hostinfo (name); if (idx == -1) { err = gpg_error_from_syserror (); xfree (reftbl); return err; } hi = hosttable[idx]; #ifdef USE_DNS_SRV /* Check for SRV records. */ srvrecord = xtryasprintf ("_hkp._tcp.%s", name); if (srvrecord == NULL) { err = gpg_error_from_syserror (); xfree (reftbl); return err; } srvscount = getsrv (srvrecord, &srvs); xfree (srvrecord); if (srvscount < 0) { err = gpg_error_from_syserror (); xfree (reftbl); return err; } if (srvscount > 0) { int i; is_pool = srvscount > 1; for (i = 0; i < srvscount; i++) { err = resolve_dns_name (srvs[i].target, 0, AF_UNSPEC, SOCK_STREAM, &ai, &cname); if (err) continue; dirmngr_tick (ctrl); add_host (name, is_pool, ai, srvs[i].port, reftbl, reftblsize, &refidx); } xfree (srvs); } #endif /* USE_DNS_SRV */ /* Find all A records for this entry and put them into the pool list - if any. */ err = resolve_dns_name (name, 0, 0, SOCK_STREAM, &aibuf, &cname); if (err) { log_error ("resolving '%s' failed: %s\n", name, gpg_strerror (err)); err = 0; } else { /* First figure out whether this is a pool. For a pool we use a different strategy than for a plain server: We use the canonical name of the pool as the virtual host along with the IP addresses. If it is not a pool, we use the specified name. */ if (! is_pool) is_pool = arecords_is_pool (aibuf); if (is_pool && cname) { hi->cname = cname; cname = NULL; } for (ai = aibuf; ai; ai = ai->next) { if (ai->family != AF_INET && ai->family != AF_INET6) continue; dirmngr_tick (ctrl); add_host (name, is_pool, ai, 0, reftbl, reftblsize, &refidx); } } reftbl[refidx] = -1; xfree (cname); free_dns_addrinfo (aibuf); if (refidx && is_pool) { assert (!hi->pool); hi->pool = xtryrealloc (reftbl, (refidx+1) * sizeof *reftbl); if (!hi->pool) { err = gpg_error_from_syserror (); log_error ("shrinking index table in map_host failed: %s\n", gpg_strerror (err)); xfree (reftbl); return err; } qsort (hi->pool, refidx, sizeof *reftbl, sort_hostpool); } else xfree (reftbl); } hi = hosttable[idx]; if (hi->pool) { /* Deal with the pool name before selecting a host. */ if (r_poolname) { *r_poolname = xtrystrdup (hi->cname? hi->cname : hi->name); if (!*r_poolname) return gpg_error_from_syserror (); } /* If the currently selected host is now marked dead, force a re-selection . */ if (force_reselect) hi->poolidx = -1; else if (hi->poolidx >= 0 && hi->poolidx < hosttable_size && hosttable[hi->poolidx] && hosttable[hi->poolidx]->dead) hi->poolidx = -1; /* Select a host if needed. */ if (hi->poolidx == -1) { hi->poolidx = select_random_host (hi->pool); if (hi->poolidx == -1) { log_error ("no alive host found in pool '%s'\n", name); if (r_poolname) { xfree (*r_poolname); *r_poolname = NULL; } return gpg_error (GPG_ERR_NO_KEYSERVER); } } assert (hi->poolidx >= 0 && hi->poolidx < hosttable_size); hi = hosttable[hi->poolidx]; assert (hi); } if (hi->dead) { log_error ("host '%s' marked as dead\n", hi->name); if (r_poolname) { xfree (*r_poolname); *r_poolname = NULL; } return gpg_error (GPG_ERR_NO_KEYSERVER); } if (r_httpflags) { /* If the hosttable does not indicate that a certain host supports IPv<N>, we explicit set the corresponding http flags. The reason for this is that a host might be listed in a pool as not v6 only but actually support v6 when later the name is resolved by our http layer. */ if (!hi->v4) *r_httpflags |= HTTP_FLAG_IGNORE_IPv4; if (!hi->v6) *r_httpflags |= HTTP_FLAG_IGNORE_IPv6; /* Note that we do not set the HTTP_FLAG_FORCE_TOR for onion addresses because the http module detects this itself. This also allows us to use an onion address without Tor mode being enabled. */ } *r_host = xtrystrdup (hi->name); if (!*r_host) { err = gpg_error_from_syserror (); if (r_poolname) { xfree (*r_poolname); *r_poolname = NULL; } return err; } if (hi->port) snprintf (r_portstr, 6 /* five digits and the sentinel */, "%hu", hi->port); return 0; }