int log_get_fd () { return logstream? es_fileno(logstream) : -1; }
/* Read the key identified by GRIP from the private key directory and return it as an gcrypt S-expression object in RESULT. On failure returns an error code and stores NULL at RESULT. */ static gpg_error_t read_key_file (const unsigned char *grip, gcry_sexp_t *result) { int rc; char *fname; estream_t fp; struct stat st; unsigned char *buf; size_t buflen, erroff; gcry_sexp_t s_skey; char hexgrip[40+4+1]; *result = NULL; bin2hex (grip, 20, hexgrip); strcpy (hexgrip+40, ".key"); fname = make_filename (opt.homedir, GNUPG_PRIVATE_KEYS_DIR, hexgrip, NULL); fp = es_fopen (fname, "rb"); if (!fp) { rc = gpg_error_from_syserror (); if (gpg_err_code (rc) != GPG_ERR_ENOENT) log_error ("can't open '%s': %s\n", fname, strerror (errno)); xfree (fname); return rc; } if (fstat (es_fileno (fp), &st)) { rc = gpg_error_from_syserror (); log_error ("can't stat '%s': %s\n", fname, strerror (errno)); xfree (fname); es_fclose (fp); return rc; } buflen = st.st_size; buf = xtrymalloc (buflen+1); if (!buf) { rc = gpg_error_from_syserror (); log_error ("error allocating %zu bytes for '%s': %s\n", buflen, fname, strerror (errno)); xfree (fname); es_fclose (fp); xfree (buf); return rc; } if (es_fread (buf, buflen, 1, fp) != 1) { rc = gpg_error_from_syserror (); log_error ("error reading %zu bytes from '%s': %s\n", buflen, fname, strerror (errno)); xfree (fname); es_fclose (fp); xfree (buf); return rc; } /* Convert the file into a gcrypt S-expression object. */ rc = gcry_sexp_sscan (&s_skey, &erroff, (char*)buf, buflen); xfree (fname); es_fclose (fp); xfree (buf); if (rc) { log_error ("failed to build S-Exp (off=%u): %s\n", (unsigned int)erroff, gpg_strerror (rc)); return rc; } *result = s_skey; return 0; }
static gpgrt_ssize_t fun_writer (void *cookie_arg, const void *buffer, size_t size) { struct fun_cookie_s *cookie = cookie_arg; /* FIXME: Use only estream with a callback for socket writing. This avoids the ugly mix of fd and estream code. */ /* Note that we always try to reconnect to the socket but print error messages only the first time an error occurred. If RUNNING_DETACHED is set we don't fall back to stderr and even do not print any error messages. This is needed because detached processes often close stderr and by writing to file descriptor 2 we might send the log message to a file not intended for logging (e.g. a pipe or network connection). */ if (cookie->want_socket && cookie->fd == -1) { #ifdef WITH_IPV6 struct sockaddr_in6 srvr_addr_in6; #endif struct sockaddr_in srvr_addr_in; #ifndef HAVE_W32_SYSTEM struct sockaddr_un srvr_addr_un; #endif size_t addrlen; struct sockaddr *srvr_addr = NULL; unsigned short port = 0; int af = AF_LOCAL; int pf = PF_LOCAL; const char *name = cookie->name; /* Not yet open or meanwhile closed due to an error. */ cookie->is_socket = 0; /* Check whether this is a TCP socket or a local socket. */ if (!strncmp (name, "tcp://", 6) && name[6]) { name += 6; af = AF_INET; pf = PF_INET; } #ifndef HAVE_W32_SYSTEM else if (!strncmp (name, "socket://", 9) && name[9]) name += 9; #endif if (af == AF_LOCAL) { #ifdef HAVE_W32_SYSTEM addrlen = 0; #else memset (&srvr_addr, 0, sizeof srvr_addr); srvr_addr_un.sun_family = af; strncpy (srvr_addr_un.sun_path, name, sizeof (srvr_addr_un.sun_path)-1); srvr_addr_un.sun_path[sizeof (srvr_addr_un.sun_path)-1] = 0; srvr_addr = (struct sockaddr *)&srvr_addr_un; addrlen = SUN_LEN (&srvr_addr_un); #endif } else { char *addrstr, *p; #ifdef HAVE_INET_PTON void *addrbuf = NULL; #endif /*HAVE_INET_PTON*/ addrstr = xtrymalloc (strlen (name) + 1); if (!addrstr) addrlen = 0; /* This indicates an error. */ else if (*name == '[') { /* Check for IPv6 literal address. */ strcpy (addrstr, name+1); p = strchr (addrstr, ']'); if (!p || p[1] != ':' || !parse_portno (p+2, &port)) { gpg_err_set_errno (EINVAL); addrlen = 0; } else { *p = 0; #ifdef WITH_IPV6 af = AF_INET6; pf = PF_INET6; memset (&srvr_addr_in6, 0, sizeof srvr_addr_in6); srvr_addr_in6.sin6_family = af; srvr_addr_in6.sin6_port = htons (port); #ifdef HAVE_INET_PTON addrbuf = &srvr_addr_in6.sin6_addr; #endif /*HAVE_INET_PTON*/ srvr_addr = (struct sockaddr *)&srvr_addr_in6; addrlen = sizeof srvr_addr_in6; #else gpg_err_set_errno (EAFNOSUPPORT); addrlen = 0; #endif } } else { /* Check for IPv4 literal address. */ strcpy (addrstr, name); p = strchr (addrstr, ':'); if (!p || !parse_portno (p+1, &port)) { gpg_err_set_errno (EINVAL); addrlen = 0; } else { *p = 0; memset (&srvr_addr_in, 0, sizeof srvr_addr_in); srvr_addr_in.sin_family = af; srvr_addr_in.sin_port = htons (port); #ifdef HAVE_INET_PTON addrbuf = &srvr_addr_in.sin_addr; #endif /*HAVE_INET_PTON*/ srvr_addr = (struct sockaddr *)&srvr_addr_in; addrlen = sizeof srvr_addr_in; } } if (addrlen) { #ifdef HAVE_INET_PTON if (inet_pton (af, addrstr, addrbuf) != 1) addrlen = 0; #else /*!HAVE_INET_PTON*/ /* We need to use the old function. If we are here v6 support isn't enabled anyway and thus we can do fine without. Note that Windows has a compatible inet_pton function named inetPton, but only since Vista. */ srvr_addr_in.sin_addr.s_addr = inet_addr (addrstr); if (srvr_addr_in.sin_addr.s_addr == INADDR_NONE) addrlen = 0; #endif /*!HAVE_INET_PTON*/ } xfree (addrstr); } cookie->fd = addrlen? socket (pf, SOCK_STREAM, 0) : -1; if (cookie->fd == -1) { if (!cookie->quiet && !running_detached && isatty (es_fileno (es_stderr))) es_fprintf (es_stderr, "failed to create socket for logging: %s\n", strerror(errno)); } else { if (connect (cookie->fd, srvr_addr, addrlen) == -1) { if (!cookie->quiet && !running_detached && isatty (es_fileno (es_stderr))) es_fprintf (es_stderr, "can't connect to '%s': %s\n", cookie->name, strerror(errno)); sock_close (cookie->fd); cookie->fd = -1; } } if (cookie->fd == -1) { if (!running_detached) { /* Due to all the problems with apps not running detached but being called with stderr closed or used for a different purposes, it does not make sense to switch to stderr. We therefore disable it. */ if (!cookie->quiet) { /* fputs ("switching logging to stderr\n", stderr);*/ cookie->quiet = 1; } cookie->fd = -1; /*fileno (stderr);*/ } } else /* Connection has been established. */ { cookie->quiet = 0; cookie->is_socket = 1; } } log_socket = cookie->fd; if (cookie->fd != -1) { #ifdef HAVE_W32CE_SYSTEM if (cookie->use_writefile) { DWORD nwritten; WriteFile ((HANDLE)cookie->fd, buffer, size, &nwritten, NULL); return (gpgrt_ssize_t)size; /* Okay. */ } #endif if (!writen (cookie->fd, buffer, size, cookie->is_socket)) return (gpgrt_ssize_t)size; /* Okay. */ } if (!running_detached && cookie->fd != -1 && isatty (es_fileno (es_stderr))) { if (*cookie->name) es_fprintf (es_stderr, "error writing to '%s': %s\n", cookie->name, strerror(errno)); else es_fprintf (es_stderr, "error writing to file descriptor %d: %s\n", cookie->fd, strerror(errno)); } if (cookie->is_socket && cookie->fd != -1) { sock_close (cookie->fd); cookie->fd = -1; log_socket = -1; } return (gpgrt_ssize_t)size; }
/* Common function to either set the logging to a file or a file descriptor. */ static void set_file_fd (const char *name, int fd) { estream_t fp; int want_socket; #ifdef HAVE_W32CE_SYSTEM int use_writefile = 0; #endif struct fun_cookie_s *cookie; /* Close an open log stream. */ if (logstream) { es_fclose (logstream); logstream = NULL; } /* Figure out what kind of logging we want. */ if (name && !strcmp (name, "-")) { name = NULL; fd = es_fileno (es_stderr); } want_socket = 0; if (name && !strncmp (name, "tcp://", 6) && name[6]) want_socket = 1; #ifndef HAVE_W32_SYSTEM else if (name && !strncmp (name, "socket://", 9) && name[9]) want_socket = 2; #endif /*HAVE_W32_SYSTEM*/ #ifdef HAVE_W32CE_SYSTEM else if (name && !strcmp (name, "GPG2:")) { HANDLE hd; ActivateDevice (L"Drivers\\"GNUPG_NAME"_Log", 0); /* Ignore a filename and write the debug output to the GPG2: device. */ hd = CreateFile (L"GPG2:", GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); fd = (hd == INVALID_HANDLE_VALUE)? -1 : (int)hd; name = NULL; force_prefixes = 1; use_writefile = 1; } #endif /*HAVE_W32CE_SYSTEM*/ /* Setup a new stream. */ /* The xmalloc below is justified because we can expect that this function is called only during initialization and there is no easy way out of this error condition. */ cookie = xmalloc (sizeof *cookie + (name? strlen (name):0)); strcpy (cookie->name, name? name:""); cookie->quiet = 0; cookie->is_socket = 0; cookie->want_socket = want_socket; #ifdef HAVE_W32CE_SYSTEM cookie->use_writefile = use_writefile; #endif if (!name) cookie->fd = fd; else if (want_socket) cookie->fd = -1; else { do cookie->fd = open (name, O_WRONLY|O_APPEND|O_CREAT, (S_IRUSR|S_IRGRP|S_IROTH|S_IWUSR|S_IWGRP|S_IWOTH)); while (cookie->fd == -1 && errno == EINTR); } log_socket = cookie->fd; { es_cookie_io_functions_t io = { NULL }; io.func_write = fun_writer; io.func_close = fun_closer; fp = es_fopencookie (cookie, "w", io); } /* On error default to a stderr based estream. */ if (!fp) fp = es_stderr; es_setvbuf (fp, NULL, _IOLBF, 0); logstream = fp; /* We always need to print the prefix and the pid for socket mode, so that the server reading the socket can do something meaningful. */ force_prefixes = want_socket; missing_lf = 0; }
/* Try to connect to the dirmngr via socket or fork it off and work by pipes. Handle the server's initial greeting */ static assuan_context_t start_dirmngr (int only_daemon) { int rc; char *infostr, *p; assuan_context_t ctx; int try_default = 0; infostr = opt.force_pipe_server? NULL : getenv (DIRMNGR_INFO_NAME); if (only_daemon && (!infostr || !*infostr)) { if (dirmngr_user_socket_name ()) infostr = xstrdup (dirmngr_user_socket_name ()); else infostr = xstrdup (dirmngr_sys_socket_name ()); try_default = 1; } rc = assuan_new (&ctx); if (rc) { log_error (_("failed to allocate assuan context: %s\n"), gpg_strerror (rc)); return NULL; } if (!infostr || !*infostr) { const char *pgmname; const char *argv[3]; assuan_fd_t no_close_list[3]; int i; if (only_daemon) { log_error (_("apparently no running dirmngr\n")); return NULL; } if (opt.verbose) log_info (_("no running dirmngr - starting one\n")); if (!opt.dirmngr_program || !*opt.dirmngr_program) opt.dirmngr_program = "./dirmngr"; if ( !(pgmname = strrchr (opt.dirmngr_program, '/'))) pgmname = opt.dirmngr_program; else pgmname++; argv[0] = pgmname; argv[1] = "--server"; argv[2] = NULL; i=0; if (log_get_fd () != -1) no_close_list[i++] = assuan_fd_from_posix_fd (log_get_fd ()); no_close_list[i++] = assuan_fd_from_posix_fd (es_fileno (es_stderr)); no_close_list[i] = ASSUAN_INVALID_FD; /* Connect to the agent and perform initial handshaking. */ rc = assuan_pipe_connect (ctx, opt.dirmngr_program, argv, no_close_list, NULL, NULL, 0); } else /* Connect to a daemon. */ { int prot; int pid; infostr = xstrdup (infostr); if (!try_default && *infostr) { if ( !(p = strchr (infostr, ':')) || p == infostr) { log_error (_("malformed %s environment variable\n"), DIRMNGR_INFO_NAME); xfree (infostr); if (only_daemon) return NULL; /* Try again by starting a new instance. */ opt.force_pipe_server = 1; return start_dirmngr (0); } *p++ = 0; pid = atoi (p); while (*p && *p != ':') p++; prot = *p? atoi (p+1) : 0; if (prot != 1) { log_error (_("dirmngr protocol version %d is not supported\n"), prot); xfree (infostr); if (only_daemon) return NULL; opt.force_pipe_server = 1; return start_dirmngr (0); } } else pid = -1; rc = assuan_socket_connect (ctx, infostr, pid, 0); xfree (infostr); if (gpg_err_code(rc) == GPG_ERR_ASS_CONNECT_FAILED && !only_daemon) { log_error (_("can't connect to the dirmngr - trying fall back\n")); opt.force_pipe_server = 1; return start_dirmngr (0); } } if (rc) { assuan_release (ctx); log_error (_("can't connect to the dirmngr: %s\n"), gpg_strerror (rc)); return NULL; } return ctx; }
/* Read the key identified by GRIP from the private key directory and return it as an gcrypt S-expression object in RESULT. On failure returns an error code and stores NULL at RESULT. */ static gpg_error_t read_key_file (const unsigned char *grip, gcry_sexp_t *result) { int rc; char *fname; estream_t fp; struct stat st; unsigned char *buf; size_t buflen, erroff; gcry_sexp_t s_skey; char hexgrip[40+4+1]; char first; *result = NULL; bin2hex (grip, 20, hexgrip); strcpy (hexgrip+40, ".key"); fname = make_filename (opt.homedir, GNUPG_PRIVATE_KEYS_DIR, hexgrip, NULL); fp = es_fopen (fname, "rb"); if (!fp) { rc = gpg_error_from_syserror (); if (gpg_err_code (rc) != GPG_ERR_ENOENT) log_error ("can't open '%s': %s\n", fname, strerror (errno)); xfree (fname); return rc; } if (es_fread (&first, 1, 1, fp) != 1) { rc = gpg_error_from_syserror (); log_error ("error reading first byte from '%s': %s\n", fname, strerror (errno)); xfree (fname); es_fclose (fp); return rc; } rc = es_fseek (fp, 0, SEEK_SET); if (rc) { log_error ("error seeking in '%s': %s\n", fname, strerror (errno)); xfree (fname); es_fclose (fp); return rc; } if (first != '(') { /* Key is in extended format. */ pkc_t pk; int line; rc = pkc_parse (&pk, &line, fp); es_fclose (fp); if (rc) log_error ("error parsing '%s' line %d: %s\n", fname, line, gpg_strerror (rc)); else { rc = pkc_get_private_key (pk, result); pkc_release (pk); if (rc) log_error ("error getting private key from '%s': %s\n", fname, gpg_strerror (rc)); } xfree (fname); return rc; } if (fstat (es_fileno (fp), &st)) { rc = gpg_error_from_syserror (); log_error ("can't stat '%s': %s\n", fname, strerror (errno)); xfree (fname); es_fclose (fp); return rc; } buflen = st.st_size; buf = xtrymalloc (buflen+1); if (!buf) { rc = gpg_error_from_syserror (); log_error ("error allocating %zu bytes for '%s': %s\n", buflen, fname, strerror (errno)); xfree (fname); es_fclose (fp); xfree (buf); return rc; } if (es_fread (buf, buflen, 1, fp) != 1) { rc = gpg_error_from_syserror (); log_error ("error reading %zu bytes from '%s': %s\n", buflen, fname, strerror (errno)); xfree (fname); es_fclose (fp); xfree (buf); return rc; } /* Convert the file into a gcrypt S-expression object. */ rc = gcry_sexp_sscan (&s_skey, &erroff, (char*)buf, buflen); xfree (fname); es_fclose (fp); xfree (buf); if (rc) { log_error ("failed to build S-Exp (off=%u): %s\n", (unsigned int)erroff, gpg_strerror (rc)); return rc; } *result = s_skey; return 0; }
static gpg_error_t write_extended_private_key (char *fname, estream_t fp, const void *buf, size_t len) { gpg_error_t err; pkc_t pk = NULL; gcry_sexp_t key = NULL; int remove = 0; int line; err = pkc_parse (&pk, &line, fp); if (err) { log_error ("error parsing '%s' line %d: %s\n", fname, line, gpg_strerror (err)); goto leave; } err = gcry_sexp_sscan (&key, NULL, buf, len); if (err) goto leave; err = pkc_set_private_key (pk, key); if (err) goto leave; err = es_fseek (fp, 0, SEEK_SET); if (err) goto leave; err = pkc_write (pk, fp); if (err) { log_error ("error writing '%s': %s\n", fname, gpg_strerror (err)); remove = 1; goto leave; } if (ftruncate (es_fileno (fp), es_ftello (fp))) { err = gpg_error_from_syserror (); log_error ("error truncating '%s': %s\n", fname, gpg_strerror (err)); remove = 1; goto leave; } if (es_fclose (fp)) { err = gpg_error_from_syserror (); log_error ("error closing '%s': %s\n", fname, gpg_strerror (err)); remove = 1; goto leave; } else fp = NULL; bump_key_eventcounter (); leave: if (fp) es_fclose (fp); if (remove) gnupg_remove (fname); xfree (fname); gcry_sexp_release (key); pkc_release (pk); return err; }
/* Write an S-expression formatted key to our key storage. With FORCE passed as true an existing key with the given GRIP will get overwritten. */ int agent_write_private_key (const unsigned char *grip, const void *buffer, size_t length, int force) { char *fname; estream_t fp; char hexgrip[40+4+1]; bin2hex (grip, 20, hexgrip); strcpy (hexgrip+40, ".key"); fname = make_filename (opt.homedir, GNUPG_PRIVATE_KEYS_DIR, hexgrip, NULL); /* FIXME: Write to a temp file first so that write failures during key updates won't lead to a key loss. */ if (!force && !access (fname, F_OK)) { log_error ("secret key file '%s' already exists\n", fname); xfree (fname); return gpg_error (GPG_ERR_EEXIST); } fp = es_fopen (fname, force? "rb+,mode=-rw" : "wbx,mode=-rw"); if (!fp) { gpg_error_t tmperr = gpg_error_from_syserror (); log_error ("can't create '%s': %s\n", fname, gpg_strerror (tmperr)); xfree (fname); return tmperr; } /* See if an existing key is in extended format. */ if (force) { gpg_error_t rc; char first; if (es_fread (&first, 1, 1, fp) != 1) { rc = gpg_error_from_syserror (); log_error ("error reading first byte from '%s': %s\n", fname, strerror (errno)); xfree (fname); es_fclose (fp); return rc; } rc = es_fseek (fp, 0, SEEK_SET); if (rc) { log_error ("error seeking in '%s': %s\n", fname, strerror (errno)); xfree (fname); es_fclose (fp); return rc; } if (first != '(') { /* Key is in extended format. */ return write_extended_private_key (fname, fp, buffer, length); } } if (es_fwrite (buffer, length, 1, fp) != 1) { gpg_error_t tmperr = gpg_error_from_syserror (); log_error ("error writing '%s': %s\n", fname, gpg_strerror (tmperr)); es_fclose (fp); gnupg_remove (fname); xfree (fname); return tmperr; } /* When force is given, the file might have to be truncated. */ if (force && ftruncate (es_fileno (fp), es_ftello (fp))) { gpg_error_t tmperr = gpg_error_from_syserror (); log_error ("error truncating '%s': %s\n", fname, gpg_strerror (tmperr)); es_fclose (fp); gnupg_remove (fname); xfree (fname); return tmperr; } if (es_fclose (fp)) { gpg_error_t tmperr = gpg_error_from_syserror (); log_error ("error closing '%s': %s\n", fname, gpg_strerror (tmperr)); gnupg_remove (fname); xfree (fname); return tmperr; } bump_key_eventcounter (); xfree (fname); return 0; }
void import_ownertrust (ctrl_t ctrl, const char *fname ) { estream_t fp; int is_stdin=0; char line[256]; char *p; size_t n, fprlen; unsigned int otrust; byte fpr[MAX_FINGERPRINT_LEN]; int any = 0; int rc; init_trustdb (ctrl, 0); if( iobuf_is_pipe_filename (fname) ) { fp = es_stdin; fname = "[stdin]"; is_stdin = 1; } else if( !(fp = es_fopen( fname, "r" )) ) { log_error ( _("can't open '%s': %s\n"), fname, strerror(errno) ); return; } if (is_secured_file (es_fileno (fp))) { es_fclose (fp); gpg_err_set_errno (EPERM); log_error (_("can't open '%s': %s\n"), fname, strerror(errno) ); return; } while (es_fgets (line, DIM(line)-1, fp)) { TRUSTREC rec; if( !*line || *line == '#' ) continue; n = strlen(line); if( line[n-1] != '\n' ) { log_error (_("error in '%s': %s\n"), fname, _("line too long") ); /* ... or last line does not have a LF */ break; /* can't continue */ } for(p = line; *p && *p != ':' ; p++ ) if( !hexdigitp(p) ) break; if( *p != ':' ) { log_error (_("error in '%s': %s\n"), fname, _("colon missing") ); continue; } fprlen = p - line; if( fprlen != 32 && fprlen != 40 && fprlen != 64) { log_error (_("error in '%s': %s\n"), fname, _("invalid fingerprint") ); continue; } if( sscanf(p, ":%u:", &otrust ) != 1 ) { log_error (_("error in '%s': %s\n"), fname, _("ownertrust value missing")); continue; } if( !otrust ) continue; /* no otrust defined - no need to update or insert */ /* Convert the ascii fingerprint to binary */ for(p=line, fprlen=0; fprlen < MAX_FINGERPRINT_LEN && *p != ':'; p += 2 ) fpr[fprlen++] = HEXTOBIN(p[0]) * 16 + HEXTOBIN(p[1]); while (fprlen < MAX_FINGERPRINT_LEN) fpr[fprlen++] = 0; rc = tdbio_search_trust_byfpr (fpr, &rec); if( !rc ) { /* found: update */ if (rec.r.trust.ownertrust != otrust) { if (!opt.quiet) { if( rec.r.trust.ownertrust ) log_info("changing ownertrust from %u to %u\n", rec.r.trust.ownertrust, otrust ); else log_info("setting ownertrust to %u\n", otrust ); } rec.r.trust.ownertrust = otrust; write_record (ctrl, &rec); any = 1; } } else if (gpg_err_code (rc) == GPG_ERR_NOT_FOUND) { /* insert */ if (!opt.quiet) log_info("inserting ownertrust of %u\n", otrust ); memset (&rec, 0, sizeof rec); rec.recnum = tdbio_new_recnum (ctrl); rec.rectype = RECTYPE_TRUST; memcpy (rec.r.trust.fingerprint, fpr, 20); rec.r.trust.ownertrust = otrust; write_record (ctrl, &rec); any = 1; } else /* error */ log_error (_("error finding trust record in '%s': %s\n"), fname, gpg_strerror (rc)); } if (es_ferror (fp)) log_error ( _("read error in '%s': %s\n"), fname, strerror(errno) ); if (!is_stdin) es_fclose (fp); if (any) { revalidation_mark (ctrl); rc = tdbio_sync (); if (rc) log_error (_("trustdb: sync failed: %s\n"), gpg_strerror (rc) ); } }