/* Return a certificate object for the given fingerprint. STRING is expected to be a SHA-1 fingerprint in standard hex notation with or without colons. If no matching certificate is available in the cache NULL is returned. The caller must release a returned certificate. Note that although we are using reference counting the caller should not just compare the pointers to check for identical certificates. */ ksba_cert_t get_cert_byhexfpr (const char *string) { unsigned char fpr[20]; const char *s; int i; if (strchr (string, ':')) { for (s=string,i=0; i < 20 && hexdigitp (s) && hexdigitp(s+1);) { if (s[2] && s[2] != ':') break; /* Invalid string. */ fpr[i++] = xtoi_2 (s); s += 2; if (i!= 20 && *s == ':') s++; } } else { for (s=string,i=0; i < 20 && hexdigitp (s) && hexdigitp(s+1); s+=2 ) fpr[i++] = xtoi_2 (s); } if (i!=20 || *s) { log_error (_("invalid SHA1 fingerprint string '%s'\n"), string); return NULL; } return get_cert_byfpr (fpr); }
/* Get the manufacturer of the token. */ void slot_token_version (slot_iterator_t id, CK_BYTE *hw_major, CK_BYTE *hw_minor, CK_BYTE *fw_major, CK_BYTE *fw_minor) { struct slot *slot = scute_table_data (slots, id); /* slots_update() makes sure serialno is valid. */ *hw_major = xtoi_2 (slot->info.serialno + 12); *hw_minor = xtoi_2 (slot->info.serialno + 14); *fw_major = 0; *fw_minor = 0; }
/* Do the percent and plus/space unescaping from STRING to BUFFER and return the length of the valid buffer. Plus unescaping is only done if WITHPLUS is true. An escaped Nul character will be replaced by NULREPL. */ static size_t do_unescape (unsigned char *buffer, const unsigned char *string, int withplus, int nulrepl) { unsigned char *p = buffer; while (*string) { if (*string == '%' && string[1] && string[2]) { string++; *p = xtoi_2 (string); if (!*p) *p = nulrepl; string++; } else if (*string == '+' && withplus) *p = ' '; else *p = *string; p++; string++; } return (p - buffer); }
static size_t do_unescape_inplace (char *string, int withplus, int nulrepl) { unsigned char *p, *p0; p = p0 = string; while (*string) { if (*string == '%' && string[1] && string[2]) { string++; *p = xtoi_2 (string); if (!*p) *p = nulrepl; string++; } else if (*string == '+' && withplus) *p = ' '; else *p = *string; p++; string++; } return (p - p0); }
/* Return a new malloced string by unescaping the string S. Escaping is percent escaping and '+'/space mapping. A binary Nul will silently be replaced by a 0xFF. Function returns NULL to indicate an out of memory status. PArsing stops at the end of the string or a white space character. */ static char * unescape_passphrase_string (const unsigned char *s) { char *buffer, *d; buffer = d = xtrymalloc_secure (strlen ((const char*)s)+1); if (!buffer) return NULL; while (*s && !spacep (s)) { if (*s == '%' && s[1] && s[2]) { s++; *d = xtoi_2 (s); if (!*d) *d = '\xff'; d++; s += 2; } else if (*s == '+') { *d++ = ' '; s++; } else *d++ = *s++; } *d = 0; return buffer; }
/* Convert STRING consisting of hex characters into its binary representation and store that at BUFFER. BUFFER needs to be of LENGTH bytes. The function check that the STRING will convert exactly to LENGTH bytes. Colons between the hex digits are allowed, if one colon has been given a colon is expected very 2 characters. The string is delimited by either end of string or a white space character. The function returns -1 on error or the length of the parsed string. */ int hexcolon2bin (const char *string, void *buffer, size_t length) { int i; const char *s = string; int need_colon = 0; for (i=0; i < length; ) { if (i==1 && *s == ':') /* Skip colons between hex digits. */ { need_colon = 1; s++; } else if (need_colon && *s == ':') s++; else if (need_colon) return -1; /* Colon expected. */ if (!hexdigitp (s) || !hexdigitp (s+1)) return -1; /* Invalid hex digits. */ ((unsigned char*)buffer)[i++] = xtoi_2 (s); s += 2; } if (*s == ':') return -1; /* Trailing colons are not allowed. */ if (*s && (!isascii (*s) || !isspace (*s)) ) return -1; /* Not followed by Nul or white space. */ if (i != length) return -1; /* Not of expected length. */ if (*s) s++; /* Skip the delimiter. */ return s - string; }
/* Convert STRING consisting of hex characters into its binary representation and store that at BUFFER. BUFFER needs to be of LENGTH bytes. The function returns -1 on error or the length of the parsed string. */ int convert_hexstring_to_char (const char *hex_string, size_t length, void *char_string) { int i; const char *s = hex_string; char_string = malloc(length); char *p; p = malloc(length); for (i=0; i < length; ) { if (!hexdigitp (s) || !hexdigitp (s+1)) return -1; /* Invalid hex digits. */ ((unsigned char*)char_string)[i++] = xtoi_2 (s); p[i++] = xtoi_2 (s); s += 2; } if (*s && (!isascii (*s) || !isspace (*s)) ) return -1; /* Not followed by Nul or white space. */ if (i != length) return -1; /* Not of expected length. */ if (*s) s++; /* Skip the delimiter. */ return (int)(s - hex_string); }
/* This function also does deescaping for data lines. */ gpg_error_t assuan_client_read_response (assuan_context_t ctx, char **line_r, int *linelen_r) { gpg_error_t rc; char *line = NULL; int linelen = 0; *line_r = NULL; *linelen_r = 0; do { do { rc = _assuan_read_line (ctx); } while (_assuan_error_is_eagain (ctx, rc)); if (rc) return rc; line = ctx->inbound.line; linelen = ctx->inbound.linelen; } while (!linelen); /* For data lines, we deescape immediately. The user will never have to worry about it. */ if (linelen >= 1 && line[0] == 'D' && line[1] == ' ') { char *s, *d; for (s=d=line; linelen; linelen--) { if (*s == '%' && linelen > 2) { /* handle escaping */ s++; *d++ = xtoi_2 (s); s += 2; linelen -= 2; } else *d++ = *s++; } *d = 0; /* add a hidden string terminator */ linelen = d - line; ctx->inbound.linelen = linelen; } *line_r = line; *linelen_r = linelen; return 0; }
/* Get the manufacturer of the token. */ char * slot_token_manufacturer (slot_iterator_t id) { struct slot *slot = scute_table_data (slots, id); unsigned int uval; /* slots_update() makes sure this is valid. */ uval = xtoi_2 (slot->info.serialno + 16) * 256 + xtoi_2 (slot->info.serialno + 18); /* Note: Make sure that there is no colon or linefeed in the string. */ switch (uval) { case 0x0001: return "PPC Card Systems"; case 0x0002: return "Prism"; case 0x0003: return "OpenFortress"; case 0x0004: return "Wewid AB"; case 0x0005: return "ZeitControl"; case 0x002A: return "Magrathea"; case 0x0000: case 0xffff: return "test card"; default: return (uval & 0xff00) == 0xff00? "unmanaged S/N range":"unknown"; } /* Not reached. */ }
/* Take a 20 byte hexencoded string and put it into the the provided 20 byte buffer FPR in binary format. */ static int unhexify_fpr (const char *hexstr, unsigned char *fpr) { const char *s; int n; for (s=hexstr, n=0; hexdigitp (s); s++, n++) ; if (*s || (n != 40)) return 0; /* no fingerprint (invalid or wrong length). */ for (s=hexstr, n=0; *s; s += 2, n++) fpr[n] = xtoi_2 (s); return 1; /* okay */ }
/* Convert the hex encoded STRING back into binary and store the result into the provided buffer RESULT. The actual size of that buffer will be returned. The caller should provide RESULT of at least strlen(STRING)/2 bytes. There is no error detection, the parsing stops at the first non hex character. With RESULT given as NULL, the fucntion does only return the size of the buffer which would be needed. */ size_t unhexify (unsigned char *result, const char *string) { const char *s; size_t n; for (s=string,n=0; hexdigitp (s) && hexdigitp(s+1); s += 2) { if (result) result[n] = xtoi_2 (s); n++; } return n; }
/* Note, that it is sufficient to allocate the target string D as long as the source string S, i.e.: strlen(s)+1; */ static void strcpy_escaped (char *d, const char *s) { while (*s) { if (*s == '%' && s[1] && s[2]) { s++; *d++ = xtoi_2 ( s); s += 2; } else *d++ = *s++; } *d = 0; }
/* Convert STRING consisting of hex characters into its binary representation and return it as an allocated buffer. The valid length of the buffer is returned at R_LENGTH. The string is delimited by end of string. The function returns NULL on error. */ static void * hex2buffer (const char *string, size_t *r_length) { const char *s; unsigned char *buffer; size_t length; buffer = xmalloc (strlen(string)/2+1); length = 0; for (s=string; *s; s +=2 ) { if (!hexdigitp (s) || !hexdigitp (s+1)) return NULL; /* Invalid hex digits. */ ((unsigned char*)buffer)[length++] = xtoi_2 (s); } *r_length = length; return buffer; }
/* Convert STRING consisting of hex characters into its binary representation and return it as an allocated buffer. The valid length of the buffer is returned at R_LENGTH. The string is delimited by end of string. The function returns NULL on error. */ static void * data_from_hex (const char *string, size_t *r_length) { const char *s; unsigned char *buffer; size_t length; buffer = gcry_xmalloc (strlen(string)/2+1); length = 0; for (s=string; *s; s +=2 ) { if (!hexdigitp (s) || !hexdigitp (s+1)) die ("error parsing hex string `%s'\n", string); ((unsigned char*)buffer)[length++] = xtoi_2 (s); } *r_length = length; return buffer; }
/* Do an in-place percent unescaping of STRING. Returns STRING. Noet that this function does not do a '+'-to-space unescaping.*/ char * unpercent_string (char *string) { char *s = string; char *d = string; while (*s) { if (*s == '%' && s[1] && s[2]) { s++; *d++ = xtoi_2 ( s); s += 2; } else *d++ = *s++; } *d = 0; return string; }
/* Convert HEXSTRING consisting of hex characters into string and store that at BUFFER. HEXSTRING is either delimited by end of string or a white space character. The function makes sure that the resulting string in BUFFER is terminated by a Nul byte. Note that the retruned string may include embedded Nul bytes; the extra Nul byte at the end is used to make sure tha the result can always be used as a C-string. BUFSIZE is the available length of BUFFER; if the converted result plus a possible required extra Nul character does not fit into this buffer, the function returns NULL and won't change the existing content of BUFFER. In-place conversion is possible as long as BUFFER points to HEXSTRING. If BUFFER is NULL and BUFSIZE is 0 the function scans HEXSTRING but does not store anything. This may be used to find the end of HEXSTRING. On success the function returns a pointer to the next character after HEXSTRING (which is either end-of-string or a the next white space). If BUFLEN is not NULL the number of valid vytes in BUFFER is stored there (an extra Nul byte is not counted); this will even be done if BUFFER has been passed as NULL. */ const char * hex2str (const char *hexstring, char *buffer, size_t bufsize, size_t *buflen) { const char *s = hexstring; int idx, count; int need_nul = 0; if (buflen) *buflen = 0; for (s=hexstring, count=0; hexdigitp (s) && hexdigitp (s+1); s += 2, count++) ; if (*s && (!isascii (*s) || !isspace (*s)) ) { gpg_err_set_errno (EINVAL); return NULL; /* Not followed by Nul or white space. */ } /* We need to append a nul character. However we don't want that if the hexstring already ends with "00". */ need_nul = ((s == hexstring) || !(s[-2] == '0' && s[-1] == '0')); if (need_nul) count++; if (buffer) { if (count > bufsize) { gpg_err_set_errno (EINVAL); return NULL; /* Too long. */ } for (s=hexstring, idx=0; hexdigitp (s) && hexdigitp (s+1); s += 2) ((unsigned char*)buffer)[idx++] = xtoi_2 (s); if (need_nul) buffer[idx] = 0; } if (buflen) *buflen = count - need_nul; return s; }
/* Convert STRING consisting of hex characters into its binary representation and store that at BUFFER. BUFFER needs to be of LENGTH bytes. The function checks that the STRING will convert exactly to LENGTH bytes. The string is delimited by either end of string or a white space character. The function returns -1 on error or the length of the parsed string. */ int hex2bin (const char *string, void *buffer, size_t length) { int i; const char *s = string; for (i=0; i < length; ) { if (!hexdigitp (s) || !hexdigitp (s+1)) return -1; /* Invalid hex digits. */ ((unsigned char*)buffer)[i++] = xtoi_2 (s); s += 2; } if (*s && (!isascii (*s) || !isspace (*s)) ) return -1; /* Not followed by Nul or white space. */ if (i != length) return -1; /* Not of expected length. */ if (*s) s++; /* Skip the delimiter. */ return s - string; }
/**************** * Remove all %xx escapes; this is done inplace. * Returns: New length of the string. */ static int remove_percent_escapes (unsigned char *string) { int n = 0; unsigned char *p, *s; for (p = s = string; *s; s++) { if (*s == '%') { if (s[1] && s[2] && hexdigitp (s+1) && hexdigitp (s+2)) { s++; *p = xtoi_2 (s); s++; p++; n++; } else { *p++ = *s++; if (*s) *p++ = *s++; if (*s) *p++ = *s++; if (*s) *p = 0; return -1; /* Bad URI. */ } } else { *p++ = *s; n++; } } *p = 0; /* Always keep a string terminator. */ return n; }
/* 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; }
/* 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; }
/* Ask the gpg-agent for a passphrase and present the user with a DESCRIPTION, a PROMPT and optionally with a TRYAGAIN extra text. If a CACHEID is not NULL it is used to locate the passphrase in in the cache and store it under this ID. If OPT_CHECK is true gpg-agent is asked to apply some checks on the passphrase security. If ERRORCODE is not NULL it should point a variable receiving an errorcode; this error code might be 0 if the user canceled the operation. The function returns NULL to indicate an error. */ char * simple_pwquery (const char *cacheid, const char *tryagain, const char *prompt, const char *description, int opt_check, int *errorcode) { int fd = -1; int nread; char *result = NULL; char *pw = NULL; char *p; int rc, i; rc = agent_open (&fd); if (rc) goto leave; if (!cacheid) cacheid = "X"; if (!tryagain) tryagain = "X"; if (!prompt) prompt = "X"; if (!description) description = "X"; { char *line; /* We allocate 3 times the needed space so that there is enough space for escaping. */ line = spwq_malloc (15 + 10 + 3*strlen (cacheid) + 1 + 3*strlen (tryagain) + 1 + 3*strlen (prompt) + 1 + 3*strlen (description) + 1 + 2); if (!line) { rc = SPWQ_OUT_OF_CORE; goto leave; } strcpy (line, "GET_PASSPHRASE "); p = line+15; if (opt_check) p = stpcpy (p, "--check "); p = copy_and_escape (p, cacheid); *p++ = ' '; p = copy_and_escape (p, tryagain); *p++ = ' '; p = copy_and_escape (p, prompt); *p++ = ' '; p = copy_and_escape (p, description); *p++ = '\n'; rc = writen (fd, line, p - line); spwq_free (line); if (rc) goto leave; } /* get response */ pw = spwq_secure_malloc (500); nread = readline (fd, pw, 499); if (nread < 0) { rc = -nread; goto leave; } if (nread < 3) { rc = SPWQ_PROTOCOL_ERROR; goto leave; } if (pw[0] == 'O' && pw[1] == 'K' && pw[2] == ' ') { /* we got a passphrase - convert it back from hex */ size_t pwlen = 0; for (i=3; i < nread && hexdigitp (pw+i); i+=2) pw[pwlen++] = xtoi_2 (pw+i); pw[pwlen] = 0; /* make a C String */ result = pw; pw = NULL; } else if ((nread > 7 && !memcmp (pw, "ERR 111", 7) && (pw[7] == ' ' || pw[7] == '\n') ) || ((nread > 4 && !memcmp (pw, "ERR ", 4) && (strtoul (pw+4, NULL, 0) & 0xffff) == 99)) ) { /* 111 is the old Assuan code for canceled which might still be in use by old installations. 99 is GPG_ERR_CANCELED as used by modern gpg-agents; 0xffff is used to mask out the error source. */ #ifdef SPWQ_USE_LOGGING log_info (_("canceled by user\n") ); #endif *errorcode = 0; /* Special error code to indicate Cancel. */ } else if (nread > 4 && !memcmp (pw, "ERR ", 4)) { switch ( (strtoul (pw+4, NULL, 0) & 0xffff) ) { case 85: rc = SPWQ_NO_PIN_ENTRY; break; default: rc = SPWQ_GENERAL_ERROR; break; } } else { #ifdef SPWQ_USE_LOGGING log_error (_("problem with the agent\n")); #endif rc = SPWQ_ERR_RESPONSE; } leave: if (errorcode) *errorcode = rc; if (fd != -1) close (fd); if (pw) spwq_secure_free (pw); return result; }
int _assuan_inquire_ext_cb (assuan_context_t ctx) { int rc; unsigned char *line; int linelen; struct membuf *mb; unsigned char *p; line = (unsigned char *) ctx->inbound.line; linelen = ctx->inbound.linelen; mb = ctx->inquire_membuf; if (line[0] == 'C' && line[1] == 'A' && line[2] == 'N') { rc = _assuan_error (ASSUAN_Canceled); goto leave; } if (line[0] == 'E' && line[1] == 'N' && line[2] == 'D' && (!line[3] || line[3] == ' ')) { rc = 0; goto leave; } if (line[0] != 'D' || line[1] != ' ' || mb == NULL) { rc = _assuan_error (ASSUAN_Unexpected_Command); goto leave; } if (linelen < 3) return 0; line += 2; linelen -= 2; p = line; while (linelen) { for (;linelen && *p != '%'; linelen--, p++) ; put_membuf (mb, line, p-line); if (linelen > 2) { /* handle escaping */ unsigned char tmp[1]; p++; *tmp = xtoi_2 (p); p += 2; linelen -= 3; put_membuf (mb, tmp, 1); } line = p; } if (mb->too_large) { rc = _assuan_error (ASSUAN_Too_Much_Data); goto leave; } return 0; leave: { size_t buf_len = 0; unsigned char *buf = NULL; if (mb) { buf = get_membuf (mb, &buf_len); if (!buf) rc = _assuan_error (ASSUAN_Out_Of_Core); free_membuf (mb); free (mb); ctx->inquire_membuf = NULL; } ctx->in_inquire = 0; rc = (ctx->inquire_cb) (ctx->inquire_cb_data, rc, buf, buf_len); } return rc; }
/** * assuan_inquire: * @ctx: An assuan context * @keyword: The keyword used for the inquire * @r_buffer: Returns an allocated buffer * @r_length: Returns the length of this buffer * @maxlen: If not 0, the size limit of the inquired data. * * A Server may use this to Send an inquire. r_buffer, r_length and * maxlen may all be NULL/0 to indicate that no real data is expected. * * Return value: 0 on success or an ASSUAN error code **/ assuan_error_t assuan_inquire (assuan_context_t ctx, const char *keyword, unsigned char **r_buffer, size_t *r_length, size_t maxlen) { assuan_error_t rc; struct membuf mb; char cmdbuf[LINELENGTH-10]; /* (10 = strlen ("INQUIRE ")+CR,LF) */ unsigned char *line, *p; int linelen; int nodataexpected; if (!ctx || !keyword || (10 + strlen (keyword) >= sizeof (cmdbuf))) return _assuan_error (ASSUAN_Invalid_Value); nodataexpected = !r_buffer && !r_length && !maxlen; if (!nodataexpected && (!r_buffer || !r_length)) return _assuan_error (ASSUAN_Invalid_Value); if (!ctx->is_server) return _assuan_error (ASSUAN_Not_A_Server); if (ctx->in_inquire) return _assuan_error (ASSUAN_Nested_Commands); ctx->in_inquire = 1; if (nodataexpected) memset (&mb, 0, sizeof mb); /* avoid compiler warnings */ else init_membuf (&mb, maxlen? maxlen:1024, maxlen); strcpy (stpcpy (cmdbuf, "INQUIRE "), keyword); rc = assuan_write_line (ctx, cmdbuf); if (rc) goto leave; for (;;) { do { do rc = _assuan_read_line (ctx); while (_assuan_error_is_eagain (rc)); if (rc) goto leave; line = (unsigned char *) ctx->inbound.line; linelen = ctx->inbound.linelen; } while (*line == '#' || !linelen); if (line[0] == 'E' && line[1] == 'N' && line[2] == 'D' && (!line[3] || line[3] == ' ')) break; /* END command received*/ if (line[0] == 'C' && line[1] == 'A' && line[2] == 'N') { rc = _assuan_error (ASSUAN_Canceled); goto leave; } if (line[0] != 'D' || line[1] != ' ' || nodataexpected) { rc = _assuan_error (ASSUAN_Unexpected_Command); goto leave; } if (linelen < 3) continue; line += 2; linelen -= 2; p = line; while (linelen) { for (;linelen && *p != '%'; linelen--, p++) ; put_membuf (&mb, line, p-line); if (linelen > 2) { /* handle escaping */ unsigned char tmp[1]; p++; *tmp = xtoi_2 (p); p += 2; linelen -= 3; put_membuf (&mb, tmp, 1); } line = p; } if (mb.too_large) { rc = _assuan_error (ASSUAN_Too_Much_Data); goto leave; } } if (!nodataexpected) { *r_buffer = get_membuf (&mb, r_length); if (!*r_buffer) rc = _assuan_error (ASSUAN_Out_Of_Core); } leave: if (!nodataexpected) free_membuf (&mb); ctx->in_inquire = 0; return rc; }
gpg_error_t _assuan_inquire_ext_cb (assuan_context_t ctx) { gpg_error_t rc; unsigned char *line; int linelen; struct membuf *mb; unsigned char *p; line = (unsigned char *) ctx->inbound.line; linelen = ctx->inbound.linelen; mb = ctx->inquire_membuf; if (line[0] == 'C' && line[1] == 'A' && line[2] == 'N') { rc = _assuan_error (ctx, GPG_ERR_ASS_CANCELED); goto leave; } if (line[0] == 'E' && line[1] == 'N' && line[2] == 'D' && (!line[3] || line[3] == ' ')) { rc = 0; goto leave; } if (line[0] != 'D' || line[1] != ' ' || mb == NULL) { rc = _assuan_error (ctx, GPG_ERR_ASS_UNEXPECTED_CMD); goto leave; } if (linelen < 3) return 0; line += 2; linelen -= 2; p = line; while (linelen) { for (;linelen && *p != '%'; linelen--, p++) ; put_membuf (ctx, mb, line, p-line); if (linelen > 2) { /* handle escaping */ unsigned char tmp[1]; p++; *tmp = xtoi_2 (p); p += 2; linelen -= 3; put_membuf (ctx, mb, tmp, 1); } line = p; } if (mb->too_large) { rc = _assuan_error (ctx, GPG_ERR_ASS_TOO_MUCH_DATA); goto leave; } return 0; leave: { size_t buf_len = 0; unsigned char *buf = NULL; if (mb) { buf = get_membuf (ctx, mb, &buf_len); if (!buf) rc = _assuan_error (ctx, gpg_err_code_from_syserror ()); free_membuf (ctx, mb); free (mb); ctx->inquire_membuf = NULL; } ctx->in_inquire = 0; rc = (ctx->inquire_cb) (ctx->inquire_cb_data, rc, buf, buf_len); } return rc; }
/** * assuan_inquire: * @ctx: An assuan context * @keyword: The keyword used for the inquire * @r_buffer: Returns an allocated buffer * @r_length: Returns the length of this buffer * @maxlen: If not 0, the size limit of the inquired data. * * A Server may use this to Send an inquire. r_buffer, r_length and * maxlen may all be NULL/0 to indicate that no real data is expected. * * Return value: 0 on success or an ASSUAN error code **/ gpg_error_t assuan_inquire (assuan_context_t ctx, const char *keyword, unsigned char **r_buffer, size_t *r_length, size_t maxlen) { gpg_error_t rc; struct membuf mb; char cmdbuf[LINELENGTH-10]; /* (10 = strlen ("INQUIRE ")+CR,LF) */ unsigned char *line, *p; int linelen; int nodataexpected; if (!ctx || !keyword || (10 + strlen (keyword) >= sizeof (cmdbuf))) return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE); nodataexpected = !r_buffer && !r_length && !maxlen; if (!nodataexpected && (!r_buffer || !r_length)) return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE); if (!ctx->is_server) return _assuan_error (ctx, GPG_ERR_ASS_NOT_A_SERVER); if (ctx->in_inquire) return _assuan_error (ctx, GPG_ERR_ASS_NESTED_COMMANDS); ctx->in_inquire = 1; if (nodataexpected) memset (&mb, 0, sizeof mb); /* avoid compiler warnings */ else init_membuf (ctx, &mb, maxlen? maxlen:1024, maxlen); strcpy (stpcpy (cmdbuf, "INQUIRE "), keyword); rc = assuan_write_line (ctx, cmdbuf); if (rc) goto leave; for (;;) { do { do rc = _assuan_read_line (ctx); while (_assuan_error_is_eagain (ctx, rc)); if (rc) goto leave; line = (unsigned char *) ctx->inbound.line; linelen = ctx->inbound.linelen; } while (*line == '#' || !linelen); if (line[0] == 'E' && line[1] == 'N' && line[2] == 'D' && (!line[3] || line[3] == ' ')) break; /* END command received*/ if (line[0] == 'C' && line[1] == 'A' && line[2] == 'N') { rc = _assuan_error (ctx, GPG_ERR_ASS_CANCELED); goto leave; } if (line[0] != 'D' || line[1] != ' ' || nodataexpected) { rc = _assuan_error (ctx, GPG_ERR_ASS_UNEXPECTED_CMD); goto leave; } if (linelen < 3) continue; line += 2; linelen -= 2; p = line; while (linelen) { for (;linelen && *p != '%'; linelen--, p++) ; put_membuf (ctx, &mb, line, p-line); if (linelen > 2) { /* handle escaping */ unsigned char tmp[1]; p++; *tmp = xtoi_2 (p); p += 2; linelen -= 3; put_membuf (ctx, &mb, tmp, 1); } line = p; } if (mb.too_large) { rc = _assuan_error (ctx, GPG_ERR_ASS_TOO_MUCH_DATA); goto leave; } } if (!nodataexpected) { *r_buffer = get_membuf (ctx, &mb, r_length); if (!*r_buffer) rc = _assuan_error (ctx, gpg_err_code_from_syserror ()); } leave: if (!nodataexpected) free_membuf (ctx, &mb); ctx->in_inquire = 0; return rc; }
/* Read the first PEM certificate from the file FNAME. If fname is NULL the next certificate is read from stdin. The certificate is returned in an alloced buffer whose address will be returned in RBUF and its length in RBUFLEN. */ static gpg_error_t read_pem_certificate (const char *fname, unsigned char **rbuf, size_t *rbuflen) { FILE *fp; int c; int pos; int value; unsigned char *buf; size_t bufsize, buflen; enum { s_init, s_idle, s_lfseen, s_begin, s_b64_0, s_b64_1, s_b64_2, s_b64_3, s_waitend } state = s_init; fp = fname? fopen (fname, "r") : stdin; if (!fp) return gpg_error_from_errno (errno); pos = 0; value = 0; bufsize = 8192; buf = xmalloc (bufsize); buflen = 0; while ((c=getc (fp)) != EOF) { int escaped_c = 0; if (opt.escaped_pem) { if (c == '%') { char tmp[2]; if ((c = getc(fp)) == EOF) break; tmp[0] = c; if ((c = getc(fp)) == EOF) break; tmp[1] = c; if (!hexdigitp (tmp) || !hexdigitp (tmp+1)) { log_error ("invalid percent escape sequence\n"); state = s_idle; /* Force an error. */ /* Skip to end of line. */ while ( (c=getc (fp)) != EOF && c != '\n') ; goto ready; } c = xtoi_2 (tmp); escaped_c = 1; } else if (c == '\n') goto ready; /* Ready. */ } switch (state) { case s_idle: if (c == '\n') { state = s_lfseen; pos = 0; } break; case s_init: state = s_lfseen; case s_lfseen: if (c != "-----BEGIN "[pos]) state = s_idle; else if (pos == 10) state = s_begin; else pos++; break; case s_begin: if (c == '\n') state = s_b64_0; break; case s_b64_0: case s_b64_1: case s_b64_2: case s_b64_3: { if (buflen >= bufsize) { bufsize += 8192; buf = xrealloc (buf, bufsize); } if (c == '-') state = s_waitend; else if ((c = asctobin[c & 0xff]) == 255 ) ; /* Just skip invalid base64 characters. */ else if (state == s_b64_0) { value = c << 2; state = s_b64_1; } else if (state == s_b64_1) { value |= (c>>4)&3; buf[buflen++] = value; value = (c<<4)&0xf0; state = s_b64_2; } else if (state == s_b64_2) { value |= (c>>2)&15; buf[buflen++] = value; value = (c<<6)&0xc0; state = s_b64_3; } else {
/* Unquote STRING of LENGTH and store it into BUF. The surrounding quotes are must already be removed from STRING. We assume that the quoted string is syntacillay correct. */ static size_t unquote_string (const char *string, size_t length, unsigned char *buf) { int esc = 0; const unsigned char *s = (const unsigned char*)string; unsigned char *d = buf; size_t n = length; for (; n; n--, s++) { if (esc) { switch (*s) { case 'b': *d++ = '\b'; break; case 't': *d++ = '\t'; break; case 'v': *d++ = '\v'; break; case 'n': *d++ = '\n'; break; case 'f': *d++ = '\f'; break; case 'r': *d++ = '\r'; break; case '"': *d++ = '\"'; break; case '\'': *d++ = '\''; break; case '\\': *d++ = '\\'; break; case '\r': /* ignore CR[,LF] */ if (n>1 && s[1] == '\n') { s++; n--; } break; case '\n': /* ignore LF[,CR] */ if (n>1 && s[1] == '\r') { s++; n--; } break; case 'x': /* hex value */ if (n>2 && hexdigitp (s+1) && hexdigitp (s+2)) { s++; n--; *d++ = xtoi_2 (s); s++; n--; } break; default: if (n>2 && octdigitp (s) && octdigitp (s+1) && octdigitp (s+2)) { *d++ = (atoi_1 (s)*64) + (atoi_1 (s+1)*8) + atoi_1 (s+2); s += 2; n -= 2; } break; } esc = 0; } else if( *s == '\\' ) esc = 1; else *d++ = *s; } return d - buf; }
/* Parse a DN and return an array-ized one. This is not a validating parser and it does not support any old-stylish syntax; gpgme is expected to return only rfc2253 compatible strings. */ static const unsigned char * parse_dn_part (DnPair *array, const unsigned char *string) { const unsigned char *s, *s1; size_t n; char *p; /* parse attributeType */ for (s = string+1; *s && *s != '='; s++) ; if (!*s) return NULL; /* error */ n = s - string; if (!n) return NULL; /* empty key */ p = (char*)malloc (n+1); memcpy (p, string, n); p[n] = 0; trim_trailing_spaces ((char*)p); // map OIDs to their names: for ( unsigned int i = 0 ; i < numOidMaps ; ++i ) if ( !strcasecmp ((char*)p, oidmap[i].oid) ) { free( p ); p = strdup( oidmap[i].name ); break; } array->key = p; 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 = (char*)malloc (n+1); for (s1=string; n; s1 += 2, n--) *p++ = xtoi_2 (s1); *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 == '#' || *s == ';' ) break; else n++; } array->value = p = (char*)malloc (n+1); for (s=string; n; s++, n--) { if (*s == '\\') { s++; if (hexdigitp (s)) { *p++ = xtoi_2 (s); s++; } else *p++ = *s; } else *p++ = *s; } *p = 0; } return s; }
static gpg_error_t learn_status_cb (void *opaque, const char *line) { struct agent_card_info_s *parm = opaque; const char *keyword = line; int keywordlen; int i; for (keywordlen=0; *line && !spacep (line); line++, keywordlen++) ; while (spacep (line)) line++; if (keywordlen == 8 && !memcmp (keyword, "SERIALNO", keywordlen)) { xfree (parm->serialno); parm->serialno = store_serialno (line); parm->is_v2 = (strlen (parm->serialno) >= 16 && xtoi_2 (parm->serialno+12) >= 2 ); } else if (keywordlen == 7 && !memcmp (keyword, "APPTYPE", keywordlen)) { xfree (parm->apptype); parm->apptype = unescape_status_string (line); } else if (keywordlen == 9 && !memcmp (keyword, "DISP-NAME", keywordlen)) { xfree (parm->disp_name); parm->disp_name = unescape_status_string (line); } else if (keywordlen == 9 && !memcmp (keyword, "DISP-LANG", keywordlen)) { xfree (parm->disp_lang); parm->disp_lang = unescape_status_string (line); } else if (keywordlen == 8 && !memcmp (keyword, "DISP-SEX", keywordlen)) { parm->disp_sex = *line == '1'? 1 : *line == '2' ? 2: 0; } else if (keywordlen == 10 && !memcmp (keyword, "PUBKEY-URL", keywordlen)) { xfree (parm->pubkey_url); parm->pubkey_url = unescape_status_string (line); } else if (keywordlen == 10 && !memcmp (keyword, "LOGIN-DATA", keywordlen)) { xfree (parm->login_data); parm->login_data = unescape_status_string (line); } else if (keywordlen == 11 && !memcmp (keyword, "SIG-COUNTER", keywordlen)) { parm->sig_counter = strtoul (line, NULL, 0); } else if (keywordlen == 10 && !memcmp (keyword, "CHV-STATUS", keywordlen)) { char *p, *buf; buf = p = unescape_status_string (line); if (buf) { while (spacep (p)) p++; parm->chv1_cached = atoi (p); while (*p && !spacep (p)) p++; while (spacep (p)) p++; for (i=0; *p && i < 3; i++) { parm->chvmaxlen[i] = atoi (p); while (*p && !spacep (p)) p++; while (spacep (p)) p++; } for (i=0; *p && i < 3; i++) { parm->chvretry[i] = atoi (p); while (*p && !spacep (p)) p++; while (spacep (p)) p++; } xfree (buf); } } else if (keywordlen == 6 && !memcmp (keyword, "EXTCAP", keywordlen)) { char *p, *p2, *buf; int abool; buf = p = unescape_status_string (line); if (buf) { for (p = strtok (buf, " "); p; p = strtok (NULL, " ")) { p2 = strchr (p, '='); if (p2) { *p2++ = 0; abool = (*p2 == '1'); if (!strcmp (p, "ki")) parm->extcap.ki = abool; else if (!strcmp (p, "aac")) parm->extcap.aac = abool; } } xfree (buf); } } else if (keywordlen == 7 && !memcmp (keyword, "KEY-FPR", keywordlen)) { int no = atoi (line); while (*line && !spacep (line)) line++; while (spacep (line)) line++; if (no == 1) parm->fpr1valid = unhexify_fpr (line, parm->fpr1); else if (no == 2) parm->fpr2valid = unhexify_fpr (line, parm->fpr2); else if (no == 3) parm->fpr3valid = unhexify_fpr (line, parm->fpr3); } else if (keywordlen == 8 && !memcmp (keyword, "KEY-TIME", keywordlen)) { int no = atoi (line); while (* line && !spacep (line)) line++; while (spacep (line)) line++; if (no == 1) parm->fpr1time = strtoul (line, NULL, 10); else if (no == 2) parm->fpr2time = strtoul (line, NULL, 10); else if (no == 3) parm->fpr3time = strtoul (line, NULL, 10); } else if (keywordlen == 6 && !memcmp (keyword, "CA-FPR", keywordlen)) { int no = atoi (line); while (*line && !spacep (line)) line++; while (spacep (line)) line++; if (no == 1) parm->cafpr1valid = unhexify_fpr (line, parm->cafpr1); else if (no == 2) parm->cafpr2valid = unhexify_fpr (line, parm->cafpr2); else if (no == 3) parm->cafpr3valid = unhexify_fpr (line, parm->cafpr3); } else if (keywordlen == 8 && !memcmp (keyword, "KEY-ATTR", keywordlen)) { int keyno, algo, nbits; sscanf (line, "%d %d %d", &keyno, &algo, &nbits); keyno--; if (keyno >= 0 && keyno < DIM (parm->key_attr)) { parm->key_attr[keyno].algo = algo; parm->key_attr[keyno].nbits = nbits; } } return 0; }
/* Decode the C formatted string SRC and store the result in the buffer *DESTP which is LEN bytes long. If LEN is zero, then a large enough buffer is allocated with malloc and *DESTP is set to the result. Currently, LEN is only used to specify if allocation is desired or not, the caller is expected to make sure that *DESTP is large enough if LEN is not zero. */ static gpg_error_t decode_c_string (const char *src, char **destp, size_t len) { char *dest; /* Set up the destination buffer. */ if (len) { if (len < strlen (src) + 1) return gpg_error (GPG_ERR_INTERNAL); dest = *destp; } else { /* The converted string will never be larger than the original string. */ dest = malloc (strlen (src) + 1); if (!dest) return gpg_error_from_syserror (); *destp = dest; } /* Convert the string. */ while (*src) { if (*src != '\\') { *(dest++) = *(src++); continue; } switch (src[1]) { #define DECODE_ONE(match,result) \ case match: \ src += 2; \ *(dest++) = result; \ break; DECODE_ONE ('\'', '\''); DECODE_ONE ('\"', '\"'); DECODE_ONE ('\?', '\?'); DECODE_ONE ('\\', '\\'); DECODE_ONE ('a', '\a'); DECODE_ONE ('b', '\b'); DECODE_ONE ('f', '\f'); DECODE_ONE ('n', '\n'); DECODE_ONE ('r', '\r'); DECODE_ONE ('t', '\t'); DECODE_ONE ('v', '\v'); case 'x': { int val = xtoi_2 (&src[2]); if (val == -1) { /* Should not happen. */ *(dest++) = *(src++); *(dest++) = *(src++); if (*src) *(dest++) = *(src++); if (*src) *(dest++) = *(src++); } else { if (!val) { /* A binary zero is not representable in a C string. */ *(dest++) = '\\'; *(dest++) = '0'; } else *((unsigned char *) dest++) = val; src += 4; } } break; default: { /* Should not happen. */ *(dest++) = *(src++); *(dest++) = *(src++); } } } *(dest++) = 0; return 0; }