/* The writer function for the memory stream. */ static ssize_t format_name_writer (void *cookie, const void *buffer, size_t size) { struct format_name_cookie *c = cookie; char *p; if (!c->buffer) { p = xtrymalloc (size + 1 + 1); if (p) { c->size = size + 1; c->buffer = p; c->len = 0; } } else if (c->len + size < c->len) { p = NULL; gpg_err_set_errno (ENOMEM); } else if (c->size < c->len + size) { p = xtryrealloc (c->buffer, c->len + size + 1); if (p) { c->size = c->len + size; c->buffer = p; } } else p = c->buffer; if (!p) { c->error = errno; xfree (c->buffer); c->buffer = NULL; gpg_err_set_errno (c->error); return -1; } memcpy (p + c->len, buffer, size); c->len += size; p[c->len] = 0; /* Terminate string. */ return (ssize_t)size; }
/* Try the help files depending on the locale. */ static char * findkey_locale (const char *key, const char *locname, int only_current_locale, const char *dirname) { const char *s; char *fname, *ext, *p; char *result; fname = xtrymalloc (strlen (dirname) + 6 + strlen (locname) + 4 + 1); if (!fname) return NULL; ext = stpcpy (stpcpy (fname, dirname), "/help."); /* Search with locale name and territory. ("help.LL_TT.txt") */ if (strchr (locname, '_')) { strcpy (stpcpy (ext, locname), ".txt"); result = findkey_fname (key, fname); } else result = NULL; /* No territory. */ if (!result) { /* Search with just the locale name - if any. ("help.LL.txt") */ if (*locname) { for (p=ext, s=locname; *s && *s != '_';) *p++ = *s++; strcpy (p, ".txt"); result = findkey_fname (key, fname); } else result = NULL; } if (!result && (!only_current_locale || !*locname) ) { /* Last try: Search in file without any locale info. ("help.txt") */ strcpy (ext, "txt"); result = findkey_fname (key, fname); } xfree (fname); return result; }
static int encode_session_key (DEK dek, gcry_sexp_t * r_data) { gcry_sexp_t data; char *p; int rc; p = xtrymalloc (64 + 2 * dek->keylen); if (!p) return gpg_error_from_syserror (); strcpy (p, "(data\n (flags pkcs1)\n (value #"); bin2hex (dek->key, dek->keylen, p + strlen (p)); strcat (p, "#))\n"); rc = gcry_sexp_sscan (&data, NULL, p, strlen (p)); xfree (p); *r_data = data; return rc; }
/* Unescape special characters in INFO and write unescaped string into newly allocated memory in *INFO_FROBBED. Returns proper error code. */ static gpg_error_t frob_info_msg (const char *info, char **info_frobbed) { gpg_error_t err = 0; *info_frobbed = xtrymalloc (strlen (info) + 1); if (!*info_frobbed) { err = gpg_error_from_errno (errno); goto out; } strcpy_escaped (*info_frobbed, info); out: return err; }
/* Give the name of the curve NAME, store the curve parameters into P, A, B, G, and N if they point to NULL value. Note that G is returned in standard uncompressed format. Also update MODEL and DIALECT if they are not NULL. */ gpg_err_code_t _gcry_ecc_update_curve_param (const char *name, enum gcry_mpi_ec_models *model, enum ecc_dialects *dialect, gcry_mpi_t *p, gcry_mpi_t *a, gcry_mpi_t *b, gcry_mpi_t *g, gcry_mpi_t *n) { int idx; idx = find_domain_parms_idx (name); if (idx < 0) return GPG_ERR_UNKNOWN_CURVE; if (g) { char *buf; size_t len; len = 4; len += strlen (domain_parms[idx].g_x+2); len += strlen (domain_parms[idx].g_y+2); len++; buf = xtrymalloc (len); if (!buf) return gpg_err_code_from_syserror (); strcpy (stpcpy (stpcpy (buf, "0x04"), domain_parms[idx].g_x+2), domain_parms[idx].g_y+2); *g = scanval (buf); xfree (buf); } if (model) *model = domain_parms[idx].model; if (dialect) *dialect = domain_parms[idx].dialect; if (p) *p = scanval (domain_parms[idx].p); if (a) *a = scanval (domain_parms[idx].a); if (b) *b = scanval (domain_parms[idx].b); if (n) *n = scanval (domain_parms[idx].n); return 0; }
/* Create a new hostinfo object, fill in NAME and put it into HOSTTABLE. Return the index into hosttable on success or -1 on error. */ static int create_new_hostinfo (const char *name) { hostinfo_t hi, *newtable; int newsize; int idx, rc; hi = xtrymalloc (sizeof *hi + strlen (name)); if (!hi) return -1; strcpy (hi->name, name); hi->pool = NULL; hi->poolidx = -1; hi->lastused = (time_t)(-1); hi->lastfail = (time_t)(-1); hi->v4 = 0; hi->v6 = 0; /* Add it to the hosttable. */ for (idx=0; idx < hosttable_size; idx++) if (!hosttable[idx]) { hosttable[idx] = hi; return idx; } /* Need to extend the hosttable. */ newsize = hosttable_size + INITIAL_HOSTTABLE_SIZE; newtable = xtryrealloc (hosttable, newsize * sizeof *hosttable); if (!newtable) { xfree (hi); return -1; } hosttable = newtable; idx = hosttable_size; hosttable_size = newsize; rc = idx; hosttable[idx++] = hi; while (idx < hosttable_size) hosttable[idx++] = NULL; return rc; }
/* Return a malloced canonical S-Expression with the serial number converted from the hex string HEXSN. Return NULL on memory error. */ ksba_sexp_t hexsn_to_sexp (const char *hexsn) { char *buffer, *p; size_t len; char numbuf[40]; len = unhexify (NULL, hexsn); snprintf (numbuf, sizeof numbuf, "(%u:", (unsigned int)len); buffer = xtrymalloc (strlen (numbuf) + len + 2 ); if (!buffer) return NULL; p = stpcpy (buffer, numbuf); len = unhexify (p, hexsn); p[len] = ')'; p[len+1] = 0; return buffer; }
/* Print the S-Expression in BUF to extended STREAM, which has a valid length of BUFLEN, as a human readable string in one line to FP. */ static void pretty_es_print_sexp (estream_t fp, const unsigned char *buf, size_t buflen) { size_t len; gcry_sexp_t sexp; char *result, *p; if ( gcry_sexp_sscan (&sexp, NULL, (const char*)buf, buflen) ) { es_fputs (_("[Error - invalid encoding]"), fp); return; } len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_ADVANCED, NULL, 0); assert (len); result = xtrymalloc (len); if (!result) { es_fputs (_("[Error - out of core]"), fp); gcry_sexp_release (sexp); return; } len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_ADVANCED, result, len); assert (len); for (p = result; len; len--, p++) { if (*p == '\n') { if (len > 1) /* Avoid printing the trailing LF. */ es_fputs ("\\n", fp); } else if (*p == '\r') es_fputs ("\\r", fp); else if (*p == '\v') es_fputs ("\\v", fp); else if (*p == '\t') es_fputs ("\\t", fp); else es_putc (*p, fp); } xfree (result); gcry_sexp_release (sexp); }
/* If there is a SRV record, take the highest ranked possibility. This is a hack, as we don't proceed downwards. */ static void srv_replace(const char *srvtag) { #ifdef USE_DNS_SRV struct srventry *srvlist=NULL; if(!srvtag) return; if(1+strlen(srvtag)+6+strlen(opt->host)+1<=MAXDNAME) { char srvname[MAXDNAME]; strcpy(srvname,"_"); strcat(srvname,srvtag); strcat(srvname,"._tcp."); strcat(srvname,opt->host); getsrv(srvname,&srvlist); } if(srvlist) { char *newname,*newport; newname=strdup(srvlist->target); newport=xtrymalloc(MAX_PORT); if(newname && newport) { free(opt->host); free(opt->port); opt->host=newname; snprintf(newport,MAX_PORT,"%u",srvlist->port); opt->port=newport; } else { free(newname); free(newport); } } #endif }
static gpg_error_t cmd_delkeys (assuan_context_t ctx, char *line) { ctrl_t ctrl = assuan_get_pointer (ctx); char *p; strlist_t list, sl; int rc; /* break the line down into an strlist_t */ list = NULL; for (p=line; *p; line = p) { while (*p && *p != ' ') p++; if (*p) *p++ = 0; if (*line) { sl = xtrymalloc (sizeof *sl + strlen (line)); if (!sl) { free_strlist (list); return out_of_core (); } sl->flags = 0; strcpy_escaped_plus (sl->d, line); sl->next = list; list = sl; } } rc = gpgsm_delete (ctrl, list); free_strlist (list); /* close and reset the fd */ close_message_fd (ctrl); assuan_close_input_fd (ctx); assuan_close_output_fd (ctx); return rc; }
/* Add the GeneralNames object GNAMES to the list of extensions in CR. Use OID as object identifier for the extensions. */ static gpg_error_t add_general_names_to_extn (ksba_certreq_t cr, struct general_names_s *gnames, const char *oid) { struct general_names_s *g; size_t n, n1, n2; struct extn_list_s *e; unsigned char *der; /* Calculate the required size. */ n1 = 0; for (g=gnames; g; g = g->next) n1 += g->datalen; n2 = _ksba_ber_count_tl (TYPE_SEQUENCE, CLASS_UNIVERSAL, 1, n1); n2 += n1; /* Allocate memory and encode all. */ e = xtrymalloc (sizeof *e + n2 - 1); if (!e) return gpg_error_from_errno (errno); e->oid = oid; e->critical = 0; e->derlen = n2; der = e->der; n = _ksba_ber_encode_tl (der, TYPE_SEQUENCE, CLASS_UNIVERSAL, 1, n1); if (!n) return gpg_error (GPG_ERR_BUG); /* (no need to cleanup after a bug) */ der += n; for (g=gnames; g; g = g->next) { memcpy (der, g->data, g->datalen); der += g->datalen; } assert (der - e->der == n2); e->next = cr->extn_list; cr->extn_list = e; return 0; }
/* Fire up a thread to send (DATA,DATALEN) to the file descriptor FD. On success the thread receives the ownership over FD. The thread ID is stored at R_TID. WRITER_ERR is the address of an gpg_error_t variable to receive a possible write error after the thread has finished. */ static gpg_error_t start_writer (int fd, const void *data, size_t datalen, estream_t stream, npth_t *r_thread, gpg_error_t *err_addr) { gpg_error_t err; struct writer_thread_parms *parm; npth_attr_t tattr; npth_t thread; int ret; memset (r_thread, '\0', sizeof (*r_thread)); *err_addr = 0; parm = xtrymalloc (sizeof *parm); if (!parm) return my_error_from_syserror (); parm->fd = fd; parm->data = data; parm->datalen = datalen; parm->stream = stream; parm->err_addr = err_addr; npth_attr_init (&tattr); npth_attr_setdetachstate (&tattr, NPTH_CREATE_JOINABLE); ret = npth_create (&thread, &tattr, writer_thread_main, parm); if (ret) { err = my_error_from_errno (ret); log_error ("error spawning writer thread: %s\n", gpg_strerror (err)); } else { npth_setname_np (thread, "fd-writer"); err = 0; *r_thread = thread; } npth_attr_destroy (&tattr); return err; }
gpg_error_t log_create (log_handle_t *handle) { gpg_error_t err = 0; *handle = xtrymalloc (sizeof (**handle)); if (!*handle) { err = gpg_error_from_errno (errno); goto out; } (*handle)->backend = LOG_BACKEND_NONE; (*handle)->min_level = LOG_LEVEL_INFO; (*handle)->flags = 0; (*handle)->prefix[0] = 0; out: return err; }
static int find_iccsn (const unsigned char *buffer, size_t length, char **serial) { size_t n; const unsigned char *s; char *p; s = find_simple_tlv (buffer, length, 0x5A, &n); if (!s) return gpg_error (GPG_ERR_CARD); length -= s - buffer; if (n > length) { /* Oops, it does not fit into the buffer. This is an invalid encoding (or the buffer is too short. However, I have some test cards with such an invalid encoding and therefore I use this ugly workaround to return something I can further experiment with. */ if (n == 0x0D && length+1 == n) { log_debug ("enabling BMI testcard workaround\n"); n--; } else return gpg_error (GPG_ERR_CARD); /* Bad encoding; does not fit into buffer. */ } if (!n) return gpg_error (GPG_ERR_CARD); /* Well, that is too short. */ *serial = p = xtrymalloc (2*n+1); if (!*serial) return gpg_error (gpg_err_code_from_errno (errno)); for (; n; n--, p += 2, s++) sprintf (p, "%02X", *s); *p = 0; return 0; }
/* Fire up a thread to receive data from the file descriptor FD. On success the thread receives the ownership over FD. The thread ID is stored at R_TID. After the thread has finished an error from the thread will be stored at ERR_ADDR. */ static gpg_error_t start_reader (int fd, membuf_t *mb, npth_t *r_thread, gpg_error_t *err_addr) { gpg_error_t err; struct reader_thread_parms *parm; npth_attr_t tattr; npth_t thread; int ret; memset (r_thread, '\0', sizeof (*r_thread)); *err_addr = 0; parm = xtrymalloc (sizeof *parm); if (!parm) return gpg_error_from_syserror (); parm->fd = fd; parm->mb = mb; parm->err_addr = err_addr; npth_attr_init (&tattr); npth_attr_setdetachstate (&tattr, NPTH_CREATE_JOINABLE); ret = npth_create (&thread, &tattr, reader_thread_main, parm); if (ret) { err = gpg_error_from_errno (ret); log_error ("error spawning reader thread: %s\n", gpg_strerror (err)); } else { npth_setname_np (thread, "fd-reader"); err = 0; *r_thread = thread; } npth_attr_destroy (&tattr); return err; }
/* Create a newly alloced string from STRING with all spaces and control characters converted to plus signs or %xx sequences. The function returns the new string or NULL in case of a malloc failure. Note that we also escape the quote character to work around a bug in the mingw32 runtime which does not correcty handle command line quoting. We correctly double the quote mark when calling a program (i.e. gpg-protect-tool), but the pre-main code does not notice the double quote as an escaped quote. We do this also on POSIX systems for consistency. */ char * percent_plus_escape (const char *string) { char *buffer, *p; const char *s; size_t length; for (length=1, s=string; *s; s++) { if (*s == '+' || *s == '\"' || *s == '%' || *(const unsigned char *)s < 0x20) length += 3; else length++; } buffer = p = xtrymalloc (length); if (!buffer) return NULL; for (s=string; *s; s++) { if (*s == '+' || *s == '\"' || *s == '%' || *(const unsigned char *)s < 0x20) { snprintf (p, 4, "%%%02X", *(unsigned char *)s); p += 3; } else if (*s == ' ') *p++ = '+'; else *p++ = *s; } *p = 0; return buffer; }
/* Build a command line for use with W32's CreateProcess. On success CMDLINE gets the address of a newly allocated string. */ static gpg_error_t build_w32_commandline (const char *pgmname, const char * const *argv, char **cmdline) { int i, n; const char *s; char *buf, *p; *cmdline = NULL; n = 0; s = pgmname; n += strlen (s) + 1 + 2; /* (1 space, 2 quoting */ for (; *s; s++) if (*s == '\"') n++; /* Need to double inner quotes. */ for (i=0; (s=argv[i]); i++) { n += strlen (s) + 1 + 2; /* (1 space, 2 quoting */ for (; *s; s++) if (*s == '\"') n++; /* Need to double inner quotes. */ } n++; buf = p = xtrymalloc (n); if (!buf) return gpg_error_from_syserror (); p = build_w32_commandline_copy (p, pgmname); for (i=0; argv[i]; i++) { *p++ = ' '; p = build_w32_commandline_copy (p, argv[i]); } *cmdline= buf; return 0; }
/* Append UID to LIST and return the new item. On success LIST is * updated. On error ERRNO is set and NULL returned. */ static uidinfo_list_t append_to_uidinfo_list (uidinfo_list_t *list, const char *uid, time_t created) { uidinfo_list_t r, sl; sl = xtrymalloc (sizeof *sl + strlen (uid)); if (!sl) return NULL; strcpy (sl->uid, uid); sl->created = created; sl->mbox = mailbox_from_userid (uid); sl->next = NULL; if (!*list) *list = sl; else { for (r = *list; r->next; r = r->next ) ; r->next = sl; } return sl; }
/* Make sure that BUF can be modified. This is done by taking a copy of the image. The function may return with an error to indicate an out of core condition. */ int _tgpg_make_buffer_mutable (bufdesc_t buf) { size_t len; if (buf->buffer) return 0; assert (buf->image); /* Make sure to allocate at least 1 one for the sake of broken malloc implementations. */ len = buf->length; if (!len) len = 1; buf->buffer = xtrymalloc (len); if (!buf->buffer) return TGPG_SYSERROR; buf->allocated = len; memcpy (buf->buffer, buf->image, buf->length); buf->image = buf->buffer; return 0; }
/**************** * This function allocates an MPI which is optimized to hold * a value as large as the one given in the argument and allocates it * with the same flags as A. */ gcry_mpi_t _gcry_mpi_alloc_like( gcry_mpi_t a ) { gcry_mpi_t b; if( a && (a->flags & 4) ) { int n = (a->sign+7)/8; void *p = _gcry_is_secure(a->d)? xtrymalloc_secure (n) : xtrymalloc (n); memcpy( p, a->d, n ); b = mpi_set_opaque( NULL, p, a->sign ); } else if( a ) { b = mpi_is_secure(a)? mpi_alloc_secure( a->nlimbs ) : mpi_alloc( a->nlimbs ); b->nlimbs = 0; b->sign = 0; b->flags = a->flags; } else b = NULL; return b; }
/* Same as hex2str but this function allocated a new string. Returns NULL on error. If R_COUNT is not NULL, the number of scanned bytes will be stored there. ERRNO is set on error. */ char * hex2str_alloc (const char *hexstring, size_t *r_count) { const char *tail; size_t nbytes; char *result; tail = hex2str (hexstring, NULL, 0, &nbytes); if (!tail) { if (r_count) *r_count = 0; return NULL; } if (r_count) *r_count = tail - hexstring; result = xtrymalloc (nbytes+1); if (!result) return NULL; if (!hex2str (hexstring, result, nbytes+1, NULL)) BUG (); return result; }
/* Create a simple S-expression from the hex string at LINE. Returns a newly allocated buffer with that canonical encoded S-expression or NULL in case of an error. On return the number of characters scanned in LINE will be stored at NSCANNED. This fucntions stops converting at the first character not representing a hexdigit. Odd numbers of hex digits are allowed; a leading zero is then assumed. If no characters have been found, NULL is returned.*/ unsigned char * make_simple_sexp_from_hexstr (const char *line, size_t *nscanned) { size_t n, len; const char *s; unsigned char *buf; unsigned char *p; char numbuf[50], *numbufp; size_t numbuflen; for (n=0, s=line; hexdigitp (s); s++, n++) ; if (nscanned) *nscanned = n; if (!n) return NULL; len = ((n+1) & ~0x01)/2; numbufp = smklen (numbuf, sizeof numbuf, len, &numbuflen); buf = xtrymalloc (1 + numbuflen + len + 1 + 1); if (!buf) return NULL; buf[0] = '('; p = (unsigned char *)stpcpy ((char *)buf+1, numbufp); s = line; if ((n&1)) { *p++ = xtoi_1 (s); s++; n--; } for (; n > 1; n -=2, s += 2) *p++ = xtoi_2 (s); *p++ = ')'; *p = 0; /* (Not really neaded.) */ return buf; }
/** * ksba_reader_set_mem: * @r: Reader object * @buffer: Data * @length: Length of Data (bytes) * * Intialize the reader object with @length bytes from @buffer and set * the read position to the beginning. It is possible to reuse this * reader object with another buffer if the reader object has * already been initialized using this function. * * Return value: 0 on success or an error code. **/ gpg_error_t ksba_reader_set_mem (ksba_reader_t r, const void *buffer, size_t length) { if (!r || !buffer) return gpg_error (GPG_ERR_INV_VALUE); if (r->type == READER_TYPE_MEM) { /* Reuse this reader */ xfree (r->u.mem.buffer); r->type = 0; } if (r->type) return gpg_error (GPG_ERR_CONFLICT); r->u.mem.buffer = xtrymalloc (length); if (!r->u.mem.buffer) return gpg_error (GPG_ERR_ENOMEM); memcpy (r->u.mem.buffer, buffer, length); r->u.mem.size = length; r->u.mem.readpos = 0; r->type = READER_TYPE_MEM; r->eof = 0; return 0; }
/* Run an integrity check on the binary. Returns 0 on success. */ static int check_binary_integrity (void) { #ifdef ENABLE_HMAC_BINARY_CHECK gpg_error_t err; Dl_info info; unsigned char digest[32]; int dlen; char *fname = NULL; const char key[] = "What am I, a doctor or a moonshuttle conductor?"; if (!dladdr ("gcry_check_version", &info)) err = gpg_error_from_syserror (); else { dlen = _gcry_hmac256_file (digest, sizeof digest, info.dli_fname, key, strlen (key)); if (dlen < 0) err = gpg_error_from_syserror (); else if (dlen != 32) err = gpg_error (GPG_ERR_INTERNAL); else { fname = xtrymalloc (strlen (info.dli_fname) + 1 + 5 + 1 ); if (!fname) err = gpg_error_from_syserror (); else { FILE *fp; char *p; /* Prefix the basename with a dot. */ strcpy (fname, info.dli_fname); p = strrchr (fname, '/'); if (p) p++; else p = fname; memmove (p+1, p, strlen (p)+1); *p = '.'; strcat (fname, ".hmac"); /* Open the file. */ fp = fopen (fname, "r"); if (!fp) err = gpg_error_from_syserror (); else { /* A buffer of 64 bytes plus one for a LF and one to detect garbage. */ unsigned char buffer[64+1+1]; const unsigned char *s; int n; /* The HMAC files consists of lowercase hex digits with an optional trailing linefeed or optional with two trailing spaces. The latter format allows the use of the usual sha1sum format. Fail if there is any garbage. */ err = gpg_error (GPG_ERR_SELFTEST_FAILED); n = fread (buffer, 1, sizeof buffer, fp); if (n == 64 || (n == 65 && buffer[64] == '\n') || (n == 66 && buffer[64] == ' ' && buffer[65] == ' ')) { buffer[64] = 0; for (n=0, s= buffer; n < 32 && loxdigit_p (s) && loxdigit_p (s+1); n++, s += 2) buffer[n] = loxtoi_2 (s); if ( n == 32 && !memcmp (digest, buffer, 32) ) err = 0; } fclose (fp); } } } } reporter ("binary", 0, fname, err? gpg_strerror (err):NULL); #ifdef HAVE_SYSLOG if (err) syslog (LOG_USER|LOG_ERR, "Libgcrypt error: " "integrity check using `%s' failed: %s", fname? fname:"[?]", gpg_strerror (err)); #endif /*HAVE_SYSLOG*/ xfree (fname); return !!err; #else return 0; #endif }
/* Perform an encrypt operation. Encrypt the data received on DATA-FD and write it to OUT_FP. The recipients are take from the certificate given in recplist; if this is NULL it will be encrypted for a default recipient */ int gpgsm_encrypt (ctrl_t ctrl, certlist_t recplist, int data_fd, FILE *out_fp) { int rc = 0; Base64Context b64writer = NULL; gpg_error_t err; ksba_writer_t writer; ksba_reader_t reader = NULL; ksba_cms_t cms = NULL; ksba_stop_reason_t stopreason; KEYDB_HANDLE kh = NULL; struct encrypt_cb_parm_s encparm; DEK dek = NULL; int recpno; FILE *data_fp = NULL; certlist_t cl; int count; memset (&encparm, 0, sizeof encparm); audit_set_type (ctrl->audit, AUDIT_TYPE_ENCRYPT); /* Check that the certificate list is not empty and that at least one certificate is not flagged as encrypt_to; i.e. is a real recipient. */ for (cl = recplist; cl; cl = cl->next) if (!cl->is_encrypt_to) break; if (!cl) { log_error(_("no valid recipients given\n")); gpgsm_status (ctrl, STATUS_NO_RECP, "0"); audit_log_i (ctrl->audit, AUDIT_GOT_RECIPIENTS, 0); rc = gpg_error (GPG_ERR_NO_PUBKEY); goto leave; } for (count = 0, cl = recplist; cl; cl = cl->next) count++; audit_log_i (ctrl->audit, AUDIT_GOT_RECIPIENTS, count); kh = keydb_new (0); if (!kh) { log_error (_("failed to allocated keyDB handle\n")); rc = gpg_error (GPG_ERR_GENERAL); goto leave; } data_fp = fdopen ( dup (data_fd), "rb"); if (!data_fp) { rc = gpg_error (gpg_err_code_from_errno (errno)); log_error ("fdopen() failed: %s\n", strerror (errno)); goto leave; } err = ksba_reader_new (&reader); if (err) rc = err; if (!rc) rc = ksba_reader_set_cb (reader, encrypt_cb, &encparm); if (rc) goto leave; encparm.fp = data_fp; ctrl->pem_name = "ENCRYPTED MESSAGE"; rc = gpgsm_create_writer (&b64writer, ctrl, out_fp, NULL, &writer); if (rc) { log_error ("can't create writer: %s\n", gpg_strerror (rc)); goto leave; } err = ksba_cms_new (&cms); if (err) { rc = err; goto leave; } err = ksba_cms_set_reader_writer (cms, reader, writer); if (err) { log_debug ("ksba_cms_set_reader_writer failed: %s\n", gpg_strerror (err)); rc = err; goto leave; } audit_log (ctrl->audit, AUDIT_GOT_DATA); /* We are going to create enveloped data with uninterpreted data as inner content */ err = ksba_cms_set_content_type (cms, 0, KSBA_CT_ENVELOPED_DATA); if (!err) err = ksba_cms_set_content_type (cms, 1, KSBA_CT_DATA); if (err) { log_debug ("ksba_cms_set_content_type failed: %s\n", gpg_strerror (err)); rc = err; goto leave; } /* Create a session key */ dek = xtrycalloc_secure (1, sizeof *dek); if (!dek) rc = out_of_core (); else { dek->algoid = opt.def_cipher_algoid; rc = init_dek (dek); } if (rc) { log_error ("failed to create the session key: %s\n", gpg_strerror (rc)); goto leave; } err = ksba_cms_set_content_enc_algo (cms, dek->algoid, dek->iv, dek->ivlen); if (err) { log_error ("ksba_cms_set_content_enc_algo failed: %s\n", gpg_strerror (err)); rc = err; goto leave; } encparm.dek = dek; /* Use a ~8k (AES) or ~4k (3DES) buffer */ encparm.bufsize = 500 * dek->ivlen; encparm.buffer = xtrymalloc (encparm.bufsize); if (!encparm.buffer) { rc = out_of_core (); goto leave; } audit_log_s (ctrl->audit, AUDIT_SESSION_KEY, dek->algoid); /* Gather certificates of recipients, encrypt the session key for each and store them in the CMS object */ for (recpno = 0, cl = recplist; cl; recpno++, cl = cl->next) { unsigned char *encval; rc = encrypt_dek (dek, cl->cert, &encval); if (rc) { audit_log_cert (ctrl->audit, AUDIT_ENCRYPTED_TO, cl->cert, rc); log_error ("encryption failed for recipient no. %d: %s\n", recpno, gpg_strerror (rc)); goto leave; } err = ksba_cms_add_recipient (cms, cl->cert); if (err) { audit_log_cert (ctrl->audit, AUDIT_ENCRYPTED_TO, cl->cert, err); log_error ("ksba_cms_add_recipient failed: %s\n", gpg_strerror (err)); rc = err; xfree (encval); goto leave; } err = ksba_cms_set_enc_val (cms, recpno, encval); xfree (encval); audit_log_cert (ctrl->audit, AUDIT_ENCRYPTED_TO, cl->cert, err); if (err) { log_error ("ksba_cms_set_enc_val failed: %s\n", gpg_strerror (err)); rc = err; goto leave; } } /* Main control loop for encryption. */ recpno = 0; do { err = ksba_cms_build (cms, &stopreason); if (err) { log_debug ("ksba_cms_build failed: %s\n", gpg_strerror (err)); rc = err; goto leave; } } while (stopreason != KSBA_SR_READY); if (encparm.readerror) { log_error ("error reading input: %s\n", strerror (encparm.readerror)); rc = gpg_error (gpg_err_code_from_errno (encparm.readerror)); goto leave; } rc = gpgsm_finish_writer (b64writer); if (rc) { log_error ("write failed: %s\n", gpg_strerror (rc)); goto leave; } audit_log (ctrl->audit, AUDIT_ENCRYPTION_DONE); log_info ("encrypted data created\n"); leave: ksba_cms_release (cms); gpgsm_destroy_writer (b64writer); ksba_reader_release (reader); keydb_release (kh); xfree (dek); if (data_fp) fclose (data_fp); xfree (encparm.buffer); return rc; }
static gpg_error_t parse_keyblock_image (iobuf_t iobuf, int pk_no, int uid_no, const u32 *sigstatus, kbnode_t *r_keyblock) { gpg_error_t err; PACKET *pkt; kbnode_t keyblock = NULL; kbnode_t node, *tail; int in_cert, save_mode; u32 n_sigs; int pk_count, uid_count; *r_keyblock = NULL; pkt = xtrymalloc (sizeof *pkt); if (!pkt) return gpg_error_from_syserror (); init_packet (pkt); save_mode = set_packet_list_mode (0); in_cert = 0; n_sigs = 0; tail = NULL; pk_count = uid_count = 0; while ((err = parse_packet (iobuf, pkt)) != -1) { if (gpg_err_code (err) == GPG_ERR_UNKNOWN_PACKET) { free_packet (pkt); init_packet (pkt); continue; } if (err) { log_error ("parse_keyblock_image: read error: %s\n", gpg_strerror (err)); err = gpg_error (GPG_ERR_INV_KEYRING); break; } if (pkt->pkttype == PKT_COMPRESSED) { log_error ("skipped compressed packet in keybox blob\n"); free_packet(pkt); init_packet(pkt); continue; } if (pkt->pkttype == PKT_RING_TRUST) { log_info ("skipped ring trust packet in keybox blob\n"); free_packet(pkt); init_packet(pkt); continue; } if (!in_cert && pkt->pkttype != PKT_PUBLIC_KEY) { log_error ("parse_keyblock_image: first packet in a keybox blob " "is not a public key packet\n"); err = gpg_error (GPG_ERR_INV_KEYRING); break; } if (in_cert && (pkt->pkttype == PKT_PUBLIC_KEY || pkt->pkttype == PKT_SECRET_KEY)) { log_error ("parse_keyblock_image: " "multiple keyblocks in a keybox blob\n"); err = gpg_error (GPG_ERR_INV_KEYRING); break; } in_cert = 1; if (pkt->pkttype == PKT_SIGNATURE && sigstatus) { PKT_signature *sig = pkt->pkt.signature; n_sigs++; if (n_sigs > sigstatus[0]) { log_error ("parse_keyblock_image: " "more signatures than found in the meta data\n"); err = gpg_error (GPG_ERR_INV_KEYRING); break; } if (sigstatus[n_sigs]) { sig->flags.checked = 1; if (sigstatus[n_sigs] == 1 ) ; /* missing key */ else if (sigstatus[n_sigs] == 2 ) ; /* bad signature */ else if (sigstatus[n_sigs] < 0x10000000) ; /* bad flag */ else { sig->flags.valid = 1; /* Fixme: Shall we set the expired flag here? */ } } } node = new_kbnode (pkt); switch (pkt->pkttype) { case PKT_PUBLIC_KEY: case PKT_PUBLIC_SUBKEY: case PKT_SECRET_KEY: case PKT_SECRET_SUBKEY: if (++pk_count == pk_no) node->flag |= 1; break; case PKT_USER_ID: if (++uid_count == uid_no) node->flag |= 2; break; default: break; } if (!keyblock) keyblock = node; else *tail = node; tail = &node->next; pkt = xtrymalloc (sizeof *pkt); if (!pkt) { err = gpg_error_from_syserror (); break; } init_packet (pkt); } set_packet_list_mode (save_mode); if (err == -1 && keyblock) err = 0; /* Got the entire keyblock. */ if (!err && sigstatus && n_sigs != sigstatus[0]) { log_error ("parse_keyblock_image: signature count does not match\n"); err = gpg_error (GPG_ERR_INV_KEYRING); } if (err) release_kbnode (keyblock); else *r_keyblock = keyblock; free_packet (pkt); xfree (pkt); return err; }
/* helper for the rfc2253 string parser */ static const unsigned char * parse_dn_part (struct dn_array_s *array, const unsigned char *string) { static struct { const char *label; const char *oid; } label_map[] = { /* Warning: When adding new labels, make sure that the buffer below we be allocated large enough. */ {"EMail", "1.2.840.113549.1.9.1" }, {"T", "2.5.4.12" }, {"GN", "2.5.4.42" }, {"SN", "2.5.4.4" }, {"NameDistinguisher", "0.2.262.1.10.7.20"}, {"ADDR", "2.5.4.16" }, {"BC", "2.5.4.15" }, {"D", "2.5.4.13" }, {"PostalCode", "2.5.4.17" }, {"Pseudo", "2.5.4.65" }, {"SerialNumber", "2.5.4.5" }, {NULL, NULL} }; const unsigned char *s, *s1; size_t n; char *p; int i; /* Parse attributeType */ for (s = string+1; *s && *s != '='; s++) ; if (!*s) return NULL; /* error */ n = s - string; if (!n) return NULL; /* empty key */ /* We need to allocate a few bytes more due to the possible mapping from the shorter OID to the longer label. */ array->key = p = xtrymalloc (n+10); if (!array->key) return NULL; memcpy (p, string, n); p[n] = 0; trim_trailing_spaces (p); if (digitp (p)) { for (i=0; label_map[i].label; i++ ) if ( !strcmp (p, label_map[i].oid) ) { strcpy (p, label_map[i].label); break; } } string = s + 1; if (*string == '#') { /* hexstring */ string++; for (s=string; hexdigitp (s); s++) s++; n = s - string; if (!n || (n & 1)) return NULL; /* Empty or odd number of digits. */ n /= 2; array->value = p = xtrymalloc (n+1); if (!p) return NULL; for (s1=string; n; s1 += 2, n--, p++) { *(unsigned char *)p = xtoi_2 (s1); if (!*p) *p = 0x01; /* Better print a wrong value than truncating the string. */ } *p = 0; } else { /* regular v3 quoted string */ for (n=0, s=string; *s; s++) { if (*s == '\\') { /* pair */ s++; if (*s == ',' || *s == '=' || *s == '+' || *s == '<' || *s == '>' || *s == '#' || *s == ';' || *s == '\\' || *s == '\"' || *s == ' ') n++; else if (hexdigitp (s) && hexdigitp (s+1)) { s++; n++; } else return NULL; /* invalid escape sequence */ } else if (*s == '\"') return NULL; /* invalid encoding */ else if (*s == ',' || *s == '=' || *s == '+' || *s == '<' || *s == '>' || *s == ';' ) break; else n++; } array->value = p = xtrymalloc (n+1); if (!p) return NULL; for (s=string; n; s++, n--) { if (*s == '\\') { s++; if (hexdigitp (s)) { *(unsigned char *)p++ = xtoi_2 (s); s++; } else *p++ = *s; } else *p++ = *s; } *p = 0; } return s; }
/* Return the information about the secret key specified by the binary keygrip GRIP. If the key is a shadowed one the shadow information will be stored at the address R_SHADOW_INFO as an allocated S-expression. */ gpg_error_t agent_key_info_from_file (ctrl_t ctrl, const unsigned char *grip, int *r_keytype, unsigned char **r_shadow_info) { gpg_error_t err; unsigned char *buf; size_t len; int keytype; (void)ctrl; if (r_keytype) *r_keytype = PRIVATE_KEY_UNKNOWN; if (r_shadow_info) *r_shadow_info = NULL; { gcry_sexp_t sexp; err = read_key_file (grip, &sexp); if (err) { if (gpg_err_code (err) == GPG_ERR_ENOENT) return gpg_error (GPG_ERR_NOT_FOUND); else return err; } err = make_canon_sexp (sexp, &buf, &len); gcry_sexp_release (sexp); if (err) return err; } keytype = agent_private_key_type (buf); switch (keytype) { case PRIVATE_KEY_CLEAR: break; case PRIVATE_KEY_PROTECTED: /* If we ever require it we could retrieve the comment fields from such a key. */ break; case PRIVATE_KEY_SHADOWED: if (r_shadow_info) { const unsigned char *s; size_t n; err = agent_get_shadow_info (buf, &s); if (!err) { n = gcry_sexp_canon_len (s, 0, NULL, NULL); assert (n); *r_shadow_info = xtrymalloc (n); if (!*r_shadow_info) err = gpg_error_from_syserror (); else memcpy (*r_shadow_info, s, n); } } break; default: err = gpg_error (GPG_ERR_BAD_SECKEY); break; } if (!err && r_keytype) *r_keytype = keytype; xfree (buf); return err; }
/* Return the public key for the keygrip GRIP. The result is stored at RESULT. This function extracts the public key from the private key database. On failure an error code is returned and NULL stored at RESULT. */ gpg_error_t agent_public_key_from_file (ctrl_t ctrl, const unsigned char *grip, gcry_sexp_t *result) { int i, idx, rc; gcry_sexp_t s_skey; const char *algoname; gcry_sexp_t uri_sexp, comment_sexp; const char *uri, *comment; size_t uri_length, comment_length; char *format, *p; void *args[4+2+2+1]; /* Size is max. # of elements + 2 for uri + 2 for comment + end-of-list. */ int argidx; gcry_sexp_t list, l2; const char *name; const char *s; size_t n; const char *elems; gcry_mpi_t *array; (void)ctrl; *result = NULL; rc = read_key_file (grip, &s_skey); if (rc) return rc; list = gcry_sexp_find_token (s_skey, "shadowed-private-key", 0 ); if (!list) list = gcry_sexp_find_token (s_skey, "protected-private-key", 0 ); if (!list) list = gcry_sexp_find_token (s_skey, "private-key", 0 ); if (!list) { log_error ("invalid private key format\n"); gcry_sexp_release (s_skey); return gpg_error (GPG_ERR_BAD_SECKEY); } l2 = gcry_sexp_cadr (list); gcry_sexp_release (list); list = l2; name = gcry_sexp_nth_data (list, 0, &n); if (n==3 && !memcmp (name, "rsa", 3)) { algoname = "rsa"; elems = "ne"; } else if (n==3 && !memcmp (name, "dsa", 3)) { algoname = "dsa"; elems = "pqgy"; } else if (n==3 && !memcmp (name, "elg", 3)) { algoname = "elg"; elems = "pgy"; } else { log_error ("unknown private key algorithm\n"); gcry_sexp_release (list); gcry_sexp_release (s_skey); return gpg_error (GPG_ERR_BAD_SECKEY); } /* Allocate an array for the parameters and copy them out of the secret key. FIXME: We should have a generic copy function. */ array = xtrycalloc (strlen(elems) + 1, sizeof *array); if (!array) { rc = gpg_error_from_syserror (); gcry_sexp_release (list); gcry_sexp_release (s_skey); return rc; } for (idx=0, s=elems; *s; s++, idx++ ) { l2 = gcry_sexp_find_token (list, s, 1); if (!l2) { /* Required parameter not found. */ for (i=0; i<idx; i++) gcry_mpi_release (array[i]); xfree (array); gcry_sexp_release (list); gcry_sexp_release (s_skey); return gpg_error (GPG_ERR_BAD_SECKEY); } array[idx] = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG); gcry_sexp_release (l2); if (!array[idx]) { /* Required parameter is invalid. */ for (i=0; i<idx; i++) gcry_mpi_release (array[i]); xfree (array); gcry_sexp_release (list); gcry_sexp_release (s_skey); return gpg_error (GPG_ERR_BAD_SECKEY); } } gcry_sexp_release (list); list = NULL; uri = NULL; uri_length = 0; uri_sexp = gcry_sexp_find_token (s_skey, "uri", 0); if (uri_sexp) uri = gcry_sexp_nth_data (uri_sexp, 1, &uri_length); comment = NULL; comment_length = 0; comment_sexp = gcry_sexp_find_token (s_skey, "comment", 0); if (comment_sexp) comment = gcry_sexp_nth_data (comment_sexp, 1, &comment_length); gcry_sexp_release (s_skey); s_skey = NULL; /* FIXME: The following thing is pretty ugly code; we should investigate how to make it cleaner. Probably code to handle canonical S-expressions in a memory buffer is better suioted for such a task. After all that is what we do in protect.c. Neeed to find common patterns and write a straightformward API to use them. */ assert (sizeof (size_t) <= sizeof (void*)); format = xtrymalloc (15+7*strlen (elems)+10+15+1+1); if (!format) { rc = gpg_error_from_syserror (); for (i=0; array[i]; i++) gcry_mpi_release (array[i]); xfree (array); gcry_sexp_release (uri_sexp); gcry_sexp_release (comment_sexp); return rc; } argidx = 0; p = stpcpy (stpcpy (format, "(public-key("), algoname); for (idx=0, s=elems; *s; s++, idx++ ) { *p++ = '('; *p++ = *s; p = stpcpy (p, " %m)"); assert (argidx < DIM (args)); args[argidx++] = &array[idx]; } *p++ = ')'; if (uri) { p = stpcpy (p, "(uri %b)"); assert (argidx+1 < DIM (args)); args[argidx++] = (void *)&uri_length; args[argidx++] = (void *)&uri; } if (comment) { p = stpcpy (p, "(comment %b)"); assert (argidx+1 < DIM (args)); args[argidx++] = (void *)&comment_length; args[argidx++] = (void*)&comment; } *p++ = ')'; *p = 0; assert (argidx < DIM (args)); args[argidx] = NULL; rc = gcry_sexp_build_array (&list, NULL, format, args); xfree (format); for (i=0; array[i]; i++) gcry_mpi_release (array[i]); xfree (array); gcry_sexp_release (uri_sexp); gcry_sexp_release (comment_sexp); if (!rc) *result = list; return rc; }
/* Return the secret key as an S-Exp in RESULT after locating it using the GRIP. Stores NULL at RESULT if the operation shall be diverted to a token; in this case an allocated S-expression with the shadow_info part from the file is stored at SHADOW_INFO. CACHE_MODE defines now the cache shall be used. DESC_TEXT may be set to present a custom description for the pinentry. LOOKUP_TTL is an optional function to convey a TTL to the cache manager; we do not simply pass the TTL value because the value is only needed if an unprotect action was needed and looking up the TTL may have some overhead (e.g. scanning the sshcontrol file). */ gpg_error_t agent_key_from_file (ctrl_t ctrl, const char *desc_text, const unsigned char *grip, unsigned char **shadow_info, cache_mode_t cache_mode, lookup_ttl_t lookup_ttl, gcry_sexp_t *result) { int rc; unsigned char *buf; size_t len, buflen, erroff; gcry_sexp_t s_skey; int got_shadow_info = 0; *result = NULL; if (shadow_info) *shadow_info = NULL; rc = read_key_file (grip, &s_skey); if (rc) return rc; /* For use with the protection functions we also need the key as an canonical encoded S-expression in a buffer. Create this buffer now. */ rc = make_canon_sexp (s_skey, &buf, &len); if (rc) return rc; switch (agent_private_key_type (buf)) { case PRIVATE_KEY_CLEAR: break; /* no unprotection needed */ case PRIVATE_KEY_PROTECTED: { char *desc_text_final; char *comment = NULL; /* 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); } desc_text_final = NULL; if (desc_text) rc = modify_description (desc_text, comment? comment:"", s_skey, &desc_text_final); gcry_free (comment); if (!rc) { rc = unprotect (ctrl, desc_text_final, &buf, grip, cache_mode, lookup_ttl); if (rc) log_error ("failed to unprotect the secret key: %s\n", gpg_strerror (rc)); } xfree (desc_text_final); } break; case PRIVATE_KEY_SHADOWED: if (shadow_info) { const unsigned char *s; size_t n; rc = agent_get_shadow_info (buf, &s); if (!rc) { n = gcry_sexp_canon_len (s, 0, NULL,NULL); assert (n); *shadow_info = xtrymalloc (n); if (!*shadow_info) rc = out_of_core (); else { memcpy (*shadow_info, s, n); rc = 0; got_shadow_info = 1; } } if (rc) log_error ("get_shadow_info failed: %s\n", gpg_strerror (rc)); } else rc = gpg_error (GPG_ERR_UNUSABLE_SECKEY); break; default: log_error ("invalid private key format\n"); rc = gpg_error (GPG_ERR_BAD_SECKEY); break; } gcry_sexp_release (s_skey); s_skey = NULL; if (rc || got_shadow_info) { xfree (buf); return rc; } buflen = gcry_sexp_canon_len (buf, 0, NULL, NULL); rc = gcry_sexp_sscan (&s_skey, &erroff, (char*)buf, buflen); wipememory (buf, buflen); 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; }