/* dirmngr.c - LDAP access * Copyright (C) 2002 Klarälvdalens Datakonsult AB * Copyright (C) 2003, 2004, 2005 g10 Code GmbH * * This file is part of DirMngr. * * DirMngr is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * DirMngr is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #include #include #include #include #include #include #include #include #include #include #include /* needed for the malloc hooks */ #define JNLIB_NEED_LOG_LOGV #include "crlcache.h" #include "crlfetch.h" #include "dirmngr.h" #include "ocsp.h" #include "certcache.h" #include "validate.h" #include "misc.h" /* To avoid DoS attacks we limit the size of a certificate to something reasonable. */ #define MAX_CERT_LENGTH (8*1024) #define PARM_ERROR(t) assuan_set_error (ctx, \ gpg_error (GPG_ERR_ASS_PARAMETER), (t)) /* Data used to associate an Assuan context with local server data */ struct server_local_s { assuan_context_t assuan_ctx; }; /* Copy the % and + escaped string S into the buffer D and replace the escape sequences. Note, that it is sufficient to allocate the target string D as long as the source string S, i.e.: strlen(s)+1. NOte further that If S contains an escaped binary nul the resulting string D will contain the 0 as well as all other characters but it will be impossible to know whether this is the original EOS or a copied Nul. */ static void strcpy_escaped_plus (char *d, const unsigned char *s) { while (*s) { if (*s == '%' && s[1] && s[2]) { s++; *d++ = xtoi_2 ( s); s += 2; } else if (*s == '+') *d++ = ' ', s++; else *d++ = *s++; } *d = 0; } /* Common code for get_cert_local and get_issuer_cert_local. */ static ksba_cert_t do_get_cert_local (ctrl_t ctrl, const char *name, const char *command) { unsigned char *value; size_t valuelen; int rc; char *buf; ksba_cert_t cert; if (name) { buf = xmalloc ( strlen (command) + 1 + strlen(name) + 1); strcpy (stpcpy (stpcpy (buf, command), " "), name); } else buf = xstrdup (command); rc = assuan_inquire (ctrl->server_local->assuan_ctx, buf, &value, &valuelen, MAX_CERT_LENGTH); xfree (buf); if (rc) { log_error (_("assuan_inquire(%s) failed: %s\n"), command, gpg_strerror (rc)); return NULL; } if (!valuelen) { xfree (value); return NULL; } rc = ksba_cert_new (&cert); if (!rc) { rc = ksba_cert_init_from_mem (cert, value, valuelen); if (rc) { ksba_cert_release (cert); cert = NULL; } } xfree (value); return cert; } /* Ask back to return a certificate for name, given as a regular gpgsm certificate indentificates (e.g. fingerprint or one of the other methods). Alternatively, NULL may be used for NAME to return the current target certificate. Either return the certificate in a KSBA object or NULL if it is not available. */ ksba_cert_t get_cert_local (ctrl_t ctrl, const char *name) { if (!ctrl || !ctrl->server_local || !ctrl->server_local->assuan_ctx) { if (opt.debug) log_debug ("get_cert_local called w/o context\n"); return NULL; } return do_get_cert_local (ctrl, name, "SENDCERT"); } /* Ask back to return the issuing certificate for name, given as a regular gpgsm certificate indentificates (e.g. fingerprint or one of the other methods). Alternatively, NULL may be used for NAME to return thecurrent target certificate. Either return the certificate in a KSBA object or NULL if it is not available. */ ksba_cert_t get_issuing_cert_local (ctrl_t ctrl, const char *name) { if (!ctrl || !ctrl->server_local || !ctrl->server_local->assuan_ctx) { if (opt.debug) log_debug ("get_issuing_cert_local called w/o context\n"); return NULL; } return do_get_cert_local (ctrl, name, "SENDISSUERCERT"); } /* Ask back to return a certificate with subject NAME and a subjectKeyIdentifier of KEYID. */ ksba_cert_t get_cert_local_ski (ctrl_t ctrl, const char *name, ksba_sexp_t keyid) { unsigned char *value; size_t valuelen; int rc; char *buf; ksba_cert_t cert; char *hexkeyid; if (!ctrl || !ctrl->server_local || !ctrl->server_local->assuan_ctx) { if (opt.debug) log_debug ("get_cert_local_ski called w/o context\n"); return NULL; } if (!name || !keyid) { log_debug ("get_cert_local_ski called with insufficient arguments\n"); return NULL; } hexkeyid = serial_hex (keyid); if (!hexkeyid) { log_debug ("serial_hex() failed\n"); return NULL; } buf = xtrymalloc (15 + strlen (hexkeyid) + 2 + strlen(name) + 1); if (!buf) { log_error ("can't allocate enough memory: %s\n", strerror (errno)); xfree (hexkeyid); return NULL; } strcpy (stpcpy (stpcpy (stpcpy (buf, "SENDCERT_SKI "), hexkeyid)," /"),name); xfree (hexkeyid); rc = assuan_inquire (ctrl->server_local->assuan_ctx, buf, &value, &valuelen, MAX_CERT_LENGTH); xfree (buf); if (rc) { log_error (_("assuan_inquire(%s) failed: %s\n"), "SENDCERT_SKI", gpg_strerror (rc)); return NULL; } if (!valuelen) { xfree (value); return NULL; } rc = ksba_cert_new (&cert); if (!rc) { rc = ksba_cert_init_from_mem (cert, value, valuelen); if (rc) { ksba_cert_release (cert); cert = NULL; } } xfree (value); return cert; } /* Ask the client to return the certificate asscociated with the current command. This is sometimes needed because the client usually sends us just the cert ID, assuming that the request can be satisfied from the cache, where the cert ID is used as key. */ static int inquire_cert_and_load_crl (assuan_context_t ctx) { ctrl_t ctrl = assuan_get_pointer (ctx); gpg_error_t err; unsigned char *value = NULL; size_t valuelen; ksba_cert_t cert = NULL; err = assuan_inquire( ctx, "SENDCERT", &value, &valuelen, 0); if (err) return err; /* { */ /* FILE *fp = fopen ("foo.der", "r"); */ /* value = xmalloc (2000); */ /* valuelen = fread (value, 1, 2000, fp); */ /* fclose (fp); */ /* } */ if (!valuelen) /* No data returned; return a comprehensible error. */ return gpg_error (GPG_ERR_MISSING_CERT); err = ksba_cert_new (&cert); if (err) goto leave; err = ksba_cert_init_from_mem (cert, value, valuelen); if(err) goto leave; xfree (value); value = NULL; err = crl_cache_reload_crl (ctrl, cert); leave: ksba_cert_release (cert); xfree (value); return err; } /* Handle OPTION commands. */ static int option_handler (assuan_context_t ctx, const char *key, const char *value) { ctrl_t ctrl = assuan_get_pointer (ctx); if (!strcmp (key, "force-crl-refresh")) { int i = *value? atoi (value) : 0; ctrl->force_crl_refresh = i; } else return gpg_error (GPG_ERR_UNKNOWN_OPTION); return 0; } /* IS_VALID | This command checks whether the certificate identified by the certificate_id is valid. This is done by consulting CRLs or whatever has been configured. Note, that the returned error codes are from gpg-error.h. The command may callback using the inquire function. See the manual for details. The certificate_id is a hex encoded string consisting of two parts, delimited by a single dot. The first part is the SHA-1 hash of the issuer name and the second part the serial number. Alternatively the certificate's fingerprint may be given in which case an OCSP request is done before consulting the CRL. */ static int cmd_isvalid (assuan_context_t ctx, char *line) { ctrl_t ctrl = assuan_get_pointer (ctx); char *issuerhash, *serialno; gpg_error_t err; int did_inquire = 0; int ocsp_mode = 0; issuerhash = xstrdup (line); /* We need to work on a copy of the line because that same Assuan context may be used for an inquiry. That is because Assuan reuses its line buffer. */ serialno = strchr (issuerhash, '.'); if (serialno) *serialno++ = 0; else { char *endp = strchr (issuerhash, ' '); if (endp) *endp = 0; if (strlen (issuerhash) != 40) { xfree (issuerhash); return PARM_ERROR (_("serialno missing in cert ID")); } ocsp_mode = 1; } again: if (ocsp_mode) { /* Note, that we ignore the given issuer hash and instead rely on the current certificate semantics used with this command. */ if (!opt.allow_ocsp) err = gpg_error (GPG_ERR_NOT_SUPPORTED); else err = ocsp_isvalid (ctrl, NULL, NULL); /* Fixme: If we got no ocsp response we should fall back to CRL mode. Thus we need to clear OCSP_MODE, get the issuerhash and the serialno from the current certificate and jump to again. */ } else { switch (crl_cache_isvalid (ctrl, issuerhash, serialno, ctrl->force_crl_refresh)) { case CRL_CACHE_VALID: err = 0; break; case CRL_CACHE_INVALID: err = gpg_error (GPG_ERR_CERT_REVOKED); break; case CRL_CACHE_DONTKNOW: if (did_inquire) err = gpg_error (GPG_ERR_NO_CRL_KNOWN); else if (!(err = inquire_cert_and_load_crl (ctx))) { did_inquire = 1; goto again; } break; case CRL_CACHE_CANTUSE: err = gpg_error (GPG_ERR_NO_CRL_KNOWN); break; default: log_fatal ("crl_cache_isvalid returned invalid code\n"); } } if (err) log_error (_("command %s failed: %s\n"), "ISVALID", gpg_strerror (err)); xfree (issuerhash); return err; } /* If the line contains a SHA-1 fingerprint as the first argument, return the FPR vuffer on success. The function checks that the fingerprint consists of valid characters and prints and error message if it does not and returns NULL. Fingerprints are considered optional and thus no explicit error is returned. NULL is also returned if there is no fingerprint at all available. FPR must be a caller provided buffer of at least 20 bytes. Note that colons within the fingerprint are allowed to separate 2 hex digits; this allows for easier cutting and pasting using the usual fingerprint rendering. */ static unsigned char * get_fingerprint_from_line (const char *line, unsigned char *fpr) { const char *s; int i; for (s=line, i=0; *s && *s != ' '; s++ ) { if ( hexdigitp (s) && hexdigitp (s+1) ) { if ( i >= 20 ) return NULL; /* Fingerprint too long. */ fpr[i++] = xtoi_2 (s); s++; } else if ( *s != ':' ) return NULL; /* Invalid. */ } if ( i != 20 ) return NULL; /* Fingerprint to short. */ return fpr; } /* CHECKCRL [] Check whether the certificate with FINGERPRINT (SHA-1 hash of the entire X.509 certificate blob) is valid or not by consulting the CRL responsible for this certificate. If the fingerprint has not been given or the certificate is not know, the function inquires the certficate using the INQUIRE TARGETCERT and the caller is expected to return the certificate for the request (which should match FINGERPRINT) as a binary blob. Processing then takes place without further interaction; in particular dirmngr tries to locate other required certificate by its own mechanism which includes a local certificate store as well as a list of trusted root certificates. The return value is the usual gpg-error code or 0 for ducesss; i.e. the certificate validity has been confirmed by a valid CRL. */ static int cmd_checkcrl (assuan_context_t ctx, char *line) { ctrl_t ctrl = assuan_get_pointer (ctx); gpg_error_t err; unsigned char fprbuffer[20], *fpr; ksba_cert_t cert; fpr = get_fingerprint_from_line (line, fprbuffer); cert = fpr? get_cert_byfpr (fpr) : NULL; if (!cert) { /* We do not have this certificate yet or the fingerprint has not been given. Inquire it from the client. */ unsigned char *value = NULL; size_t valuelen; err = assuan_inquire (ctrl->server_local->assuan_ctx, "TARGETCERT", &value, &valuelen, MAX_CERT_LENGTH); if (err) { log_error (_("assuan_inquire failed: %s\n"), gpg_strerror (err)); goto leave; } if (!valuelen) /* No data returned; return a comprehensible error. */ err = gpg_error (GPG_ERR_MISSING_CERT); else { err = ksba_cert_new (&cert); if (!err) err = ksba_cert_init_from_mem (cert, value, valuelen); } xfree (value); if(err) goto leave; } assert (cert); err = crl_cache_cert_isvalid (ctrl, cert, ctrl->force_crl_refresh); if (gpg_err_code (err) == GPG_ERR_NO_CRL_KNOWN) { err = crl_cache_reload_crl (ctrl, cert); if (!err) err = crl_cache_cert_isvalid (ctrl, cert, 0); } leave: if (err) log_error (_("command %s failed: %s\n"), "CHECKCRL", gpg_strerror (err)); ksba_cert_release (cert); return err; } /* CHECKOCSP [] Check whether the certificate with FINGERPRINT (SHA-1 hash of the entire X.509 certificate blob) is valid or not by asking an OCSP responder responsible for this certificate. The optional fingerprint may be used for a quick check in case an OCSP check has been down for this certificate recently (we always cache OCSP responses for a couple of minutes). If the fingerprint has not been given or there is no cached result, the function inquires the certificate using the INQUIRE TARGETCERT and the caller is expected to return the certificate for the request (which should match FINGERPRINT) as a binary blob. Processing then takes place without further interaction; in particular dirmngr tries to locate other required certificates by its own mechanism which includes a local certificate store as well as a list of trusted root certifciates. The return value is the usual gpg-error code or 0 for ducesss; i.e. the certificate validity has been confirmed by a valid CRL. */ static int cmd_checkocsp (assuan_context_t ctx, char *line) { ctrl_t ctrl = assuan_get_pointer (ctx); gpg_error_t err; unsigned char fprbuffer[20], *fpr; ksba_cert_t cert; fpr = get_fingerprint_from_line (line, fprbuffer); cert = fpr? get_cert_byfpr (fpr) : NULL; if (!cert) { /* We do not have this certificate yet or the fingerprint has not been given. Inquire it from the client. */ unsigned char *value = NULL; size_t valuelen; err = assuan_inquire (ctrl->server_local->assuan_ctx, "TARGETCERT", &value, &valuelen, MAX_CERT_LENGTH); if (err) { log_error (_("assuan_inquire failed: %s\n"), gpg_strerror (err)); goto leave; } if (!valuelen) /* No data returned; return a comprehensible error. */ err = gpg_error (GPG_ERR_MISSING_CERT); else { err = ksba_cert_new (&cert); if (!err) err = ksba_cert_init_from_mem (cert, value, valuelen); } xfree (value); if(err) goto leave; } assert (cert); if (!opt.allow_ocsp) err = gpg_error (GPG_ERR_NOT_SUPPORTED); else err = ocsp_isvalid (ctrl, cert, NULL); leave: if (err) log_error (_("command %s failed: %s\n"), "CHECKOCSP", gpg_strerror (err)); ksba_cert_release (cert); return err; } /* LOOKUP Lookup certificates matching PATTERN. To allow for multiple patterns (which are ORed) quoting is required: Spaces are to be translated into "+" or into "%20"; obviously this requires that the usual escape quoting rules are applied. */ static int cmd_lookup (assuan_context_t ctx, char *line) { ctrl_t ctrl = assuan_get_pointer (ctx); gpg_error_t err = 0; char *p; strlist_t sl, list = NULL; int truncated = 0, truncation_forced = 0; int count = 0; unsigned char *value = NULL; size_t valuelen; ldap_server_t ldapserver; cert_fetch_context_t fetch_context; int any_no_data = 0; /* Break the line down into an STRLIST */ for (p=line; *p; line = p) { while (*p && *p != ' ') p++; if (*p) *p++ = 0; if (*line) { sl = xtrymalloc (sizeof *sl + strlen (line)); if (!sl) { err = gpg_error_from_errno (errno); goto leave; } memset (sl, 0, sizeof *sl); strcpy_escaped_plus (sl->d, line); sl->next = list; list = sl; } } /* Loop over all configured servers. */ for (ldapserver = opt.ldapservers; ldapserver && ldapserver->host && !truncation_forced; ldapserver = ldapserver->next) { if (DBG_LOOKUP) log_debug ("cmd_lookup: trying %s:%d base=%s\n", ldapserver->host, ldapserver->port, ldapserver->base?ldapserver->base : "[default]"); /* Fetch certificates matching pattern */ err = start_cert_fetch (ctrl, &fetch_context, list, ldapserver); if ( gpg_err_code (err) == GPG_ERR_NO_DATA ) { if (DBG_LOOKUP) log_debug ("cmd_lookup: no data\n"); err = 0; any_no_data = 1; continue; } if (err) { log_error (_("start_cert_fetch failed: %s\n"), gpg_strerror (err)); goto leave; } /* Fetch the certificates for this query. */ while (!truncation_forced) { xfree (value); value = NULL; err = fetch_next_cert (fetch_context, &value, &valuelen); if (gpg_err_code (err) == GPG_ERR_NO_DATA ) { err = 0; any_no_data = 1; break; /* Ready. */ } if (gpg_err_code (err) == GPG_ERR_TRUNCATED) { truncated = 1; err = 0; break; /* Ready. */ } if (gpg_err_code (err) == GPG_ERR_EOF) { err = 0; break; /* Ready. */ } if (!err && !value) { err = gpg_error (GPG_ERR_BUG); goto leave; } if (err) { log_error (_("fetch_next_cert failed: %s\n"), gpg_strerror (err)); end_cert_fetch (fetch_context); goto leave; } if (DBG_LOOKUP) log_debug ("cmd_lookup: returning one cert%s\n", truncated? " (truncated)":""); /* Send the data, flush the buffer and then send an END line as a certificate delimiter. */ err = assuan_send_data (ctx, value, valuelen); if (!err) err = assuan_send_data (ctx, NULL, 0); if (!err) err = assuan_write_line (ctx, "END"); if (err) { log_error (_("error sending data: %s\n"), gpg_strerror (err)); end_cert_fetch (fetch_context); goto leave; } if (++count >= opt.max_replies ) { truncation_forced = 1; log_info (_("max_replies %d exceeded\n"), opt.max_replies ); } } end_cert_fetch (fetch_context); } if (truncated || truncation_forced ) { char str[50]; sprintf (str, "%d", count); assuan_write_status (ctx, "TRUNCATED", str); } if (!err && !count && any_no_data) err = gpg_error (GPG_ERR_NO_DATA); leave: if (err) log_error (_("command %s failed: %s\n"), "LOOKUP", gpg_strerror (err)); free_strlist (list); return err; } /* LOADCRL Load the CRL in the file with name FILENAME into our cache. Note that FILENAME should be given with an absolute path because Dirmngrs cwd is not known. This command is usually used by gpgsm using the invocation "gpgsm --call-dirmngr loadcrl ". A direct invocation of Dirmngr is not useful because gpgsm might need to callback gpgsm to ask for the CA's certificate. */ static int cmd_loadcrl (assuan_context_t ctx, char *line) { ctrl_t ctrl = assuan_get_pointer (ctx); gpg_error_t err; char *buf; buf = xmalloc (strlen (line)+1); strcpy_escaped_plus (buf, line); err = crl_cache_load (ctrl, buf); xfree (buf); if (err) log_error (_("command %s failed: %s\n"), "LOADCRL", gpg_strerror (err)); return err; } /* LISTCRLS List the content of all CRLs in a readable format. This command is usually used by gpgsm using the invocation "gpgsm --call-dirmngr listcrls". It may also be used directly using "dirmngr --list-crls". */ static int cmd_listcrls (assuan_context_t ctx, char *line) { gpg_error_t err; FILE *fp = assuan_get_data_fp (ctx); if (!fp) return PARM_ERROR (_("no data stream")); err = crl_cache_list (fp); if (err) log_error (_("command %s failed: %s\n"), "LISTCRLS", gpg_strerror (err)); return err; } /* CACHECERT Put a certificate into the internal cache. This command might be useful if a client knows in advance certificates required for a test and wnats to make sure they get added to the internal cache. It is also helpful for debugging. To get the actual certificate, this command immediately inquires it using INQUIRE TARGETCERT and the caller is expected to return the certificate for the request as a binary blob. */ static int cmd_cachecert (assuan_context_t ctx, char *line) { ctrl_t ctrl = assuan_get_pointer (ctx); gpg_error_t err; ksba_cert_t cert = NULL; unsigned char *value = NULL; size_t valuelen; err = assuan_inquire (ctrl->server_local->assuan_ctx, "TARGETCERT", &value, &valuelen, MAX_CERT_LENGTH); if (err) { log_error (_("assuan_inquire failed: %s\n"), gpg_strerror (err)); goto leave; } if (!valuelen) /* No data returned; return a comprehensible error. */ err = gpg_error (GPG_ERR_MISSING_CERT); else { err = ksba_cert_new (&cert); if (!err) err = ksba_cert_init_from_mem (cert, value, valuelen); } xfree (value); if(err) goto leave; err = cache_cert (cert); leave: if (err) log_error (_("command %s failed: %s\n"), "CACHECERT", gpg_strerror (err)); ksba_cert_release (cert); return err; } /* VALIDATE Validate a certificate using the certificate validation fucntion used internally by dirmngr. This command is only useful for debugging. To get the actual certificate, this command immediately inquires it using INQUIRE TARGETCERT and the caller is expected to return the certificate for the request as a binary blob. */ static int cmd_validate (assuan_context_t ctx, char *line) { ctrl_t ctrl = assuan_get_pointer (ctx); gpg_error_t err; ksba_cert_t cert = NULL; unsigned char *value = NULL; size_t valuelen; err = assuan_inquire (ctrl->server_local->assuan_ctx, "TARGETCERT", &value, &valuelen, MAX_CERT_LENGTH); if (err) { log_error (_("assuan_inquire failed: %s\n"), gpg_strerror (err)); goto leave; } if (!valuelen) /* No data returned; return a comprehensible error. */ err = gpg_error (GPG_ERR_MISSING_CERT); else { err = ksba_cert_new (&cert); if (!err) err = ksba_cert_init_from_mem (cert, value, valuelen); } xfree (value); if(err) goto leave; /* If we have this certificate already in our cache, use the cached version for validation because this will take care of any cached results. */ { unsigned char fpr[20]; ksba_cert_t tmpcert; cert_compute_fpr (cert, fpr); tmpcert = get_cert_byfpr (fpr); if (tmpcert) { ksba_cert_release (cert); cert = tmpcert; } } err = validate_cert_chain (ctrl, cert, NULL, VALIDATE_MODE_CERT); leave: if (err) log_error (_("command %s failed: %s\n"), "VALIDATE", gpg_strerror (err)); ksba_cert_release (cert); return err; } /* Tell the assuan library about our commands. */ static int register_commands (assuan_context_t ctx) { static struct { const char *name; int (*handler)(assuan_context_t, char *line); } table[] = { { "ISVALID", cmd_isvalid }, { "CHECKCRL", cmd_checkcrl }, { "CHECKOCSP", cmd_checkocsp }, { "LOOKUP", cmd_lookup }, { "LOADCRL", cmd_loadcrl }, { "LISTCRLS", cmd_listcrls }, { "CACHECERT", cmd_cachecert }, { "VALIDATE", cmd_validate }, { "INPUT", NULL }, { "OUTPUT", NULL }, { NULL } }; int i, j, rc; for (i=j=0; table[i].name; i++) { rc = assuan_register_command (ctx, table[i].name, table[i].handler); if (rc) return rc; } return 0; } /* Startup the server and run the main command loop. With FD = -1 used stdin/stdout. */ void start_command_handler (int fd) { static const char hello[] = "Dirmngr " VERSION " at your service"; static char *hello_line; int rc; assuan_context_t ctx; ctrl_t ctrl; ctrl = xtrycalloc (1, sizeof *ctrl); if (ctrl) ctrl->server_local = xtrycalloc (1, sizeof *ctrl->server_local); if (!ctrl || !ctrl->server_local) { log_error (_("can't allocate control structure: %s\n"), strerror (errno)); xfree (ctrl); return; } dirmngr_init_default_ctrl (ctrl); if (fd == -1) { int filedes[2]; filedes[0] = 0; filedes[1] = 1; rc = assuan_init_pipe_server (&ctx, filedes); } else { rc = assuan_init_socket_server_ext (&ctx, fd, 2); } if (rc) { log_error (_("failed to initialize the server: %s\n"), gpg_strerror(rc)); dirmngr_exit (2); } rc = register_commands (ctx); if (rc) { log_error (_("failed to the register commands with Assuan: %s\n"), gpg_strerror(rc)); dirmngr_exit (2); } if (!hello_line) { size_t n; const char *cfgname; cfgname = opt.config_filename? opt.config_filename : "[none]"; n = (30 + strlen (opt.homedir) + strlen (cfgname) + strlen (hello) + 1); hello_line = xmalloc (n+1); snprintf (hello_line, n, "Home: %s\n" "Config: %s\n" "%s", opt.homedir, cfgname, hello); hello_line[n] = 0; } assuan_set_hello_line (ctx, hello_line); assuan_register_option_handler (ctx, option_handler); ctrl->server_local->assuan_ctx = ctx; assuan_set_pointer (ctx, ctrl); if (DBG_ASSUAN) assuan_set_log_stream (ctx, log_get_stream ()); for (;;) { rc = assuan_accept (ctx); if (rc == -1) break; if (rc) { log_info (_("Assuan accept problem: %s\n"), gpg_strerror (rc)); break; } if (opt.verbose) { pid_t apid; uid_t auid; gid_t agid; if (!assuan_get_peercred (ctx, &apid, &auid, &agid)) log_info ("connection from process %ld (%ld:%ld)\n", (long)apid, (long)auid, (long)agid); } rc = assuan_process (ctx); if (rc) { log_info (_("Assuan processing failed: %s\n"), gpg_strerror (rc)); continue; } } ldap_wrapper_connection_cleanup (ctrl); ctrl->server_local->assuan_ctx = NULL; assuan_deinit_server (ctx); if (ctrl->refcount) log_error ("oops: connection control structure still referenced (%d)\n", ctrl->refcount); else { release_ctrl_ocsp_certs (ctrl); xfree (ctrl->server_local); xfree (ctrl); } } /* Send a status line back to the client. KEYWORD is the status keyword, the optioal string argumenst are blank separated added to the line, the last argument must be a NULL. */ gpg_error_t dirmngr_status (ctrl_t ctrl, const char *keyword, ...) { gpg_error_t err = 0; va_list arg_ptr; const char *text; va_start (arg_ptr, keyword); if (ctrl->server_local) { assuan_context_t ctx = ctrl->server_local->assuan_ctx; char buf[950], *p; size_t n; p = buf; n = 0; while ( (text = va_arg (arg_ptr, const char *)) ) { if (n) { *p++ = ' '; n++; } for ( ; *text && n < DIM (buf)-2; n++) *p++ = *text++; } *p = 0; err = assuan_write_status (ctx, keyword, buf); } va_end (arg_ptr); return err; } /* Note, that we ignore CTRL for now but use the first connection to send the progress info back. */ gpg_error_t dirmngr_tick (ctrl_t ctrl) { static time_t next_tick = 0; gpg_error_t err = 0; time_t now = time (NULL); if (!next_tick) { next_tick = now + 1; } else if ( now > next_tick ) { if (ctrl) { err = dirmngr_status (ctrl, "PROGRESS", "tick", "? 0 0", NULL); if (err) { /* Take this as in indication for a cancel request. */ err = gpg_error (GPG_ERR_CANCELED); } now = time (NULL); } next_tick = now + 1; } return err; }