/* A simple callback used to return the serialnumber of a card. */ static gpg_error_t get_serialno_cb (void *opaque, const char *line) { char **serialno = opaque; const char *keyword = line; const char *s; int keywordlen, n; for (keywordlen=0; *line && !spacep (line); line++, keywordlen++) ; while (spacep (line)) line++; if (keywordlen == 8 && !memcmp (keyword, "SERIALNO", keywordlen)) { if (*serialno) return gpg_error (GPG_ERR_CONFLICT); /* Unexpected status line. */ for (n=0,s=line; hexdigitp (s); s++, n++) ; if (!n || (n&1)|| !(spacep (s) || !*s) ) return gpg_error (GPG_ERR_ASS_PARAMETER); *serialno = xtrymalloc (n+1); if (!*serialno) return out_of_core (); memcpy (*serialno, line, n); (*serialno)[n] = 0; } return 0; }
static gpg_error_t learn_status_cb (void *opaque, const char *line) { struct learn_parm_s *parm = opaque; const char *keyword = line; int keywordlen; for (keywordlen=0; *line && !spacep (line); line++, keywordlen++) ; while (spacep (line)) line++; if (keywordlen == 8 && !memcmp (keyword, "CERTINFO", keywordlen)) { parm->certinfo_cb (parm->certinfo_cb_arg, line); } else if (keywordlen == 11 && !memcmp (keyword, "KEYPAIRINFO", keywordlen)) { parm->kpinfo_cb (parm->kpinfo_cb_arg, line); } else if (keywordlen && *line) { parm->sinfo_cb (parm->sinfo_cb_arg, keyword, keywordlen, line); } return 0; }
static gpg_error_t pass_status_thru (void *opaque, const char *line) { assuan_context_t ctx = opaque; char keyword[200]; int i; if (line[0] == '#' && (!line[1] || spacep (line+1))) { /* We are called in convey comments mode. Now, if we see a comment marker as keyword we forward the line verbatim to the the caller. This way the comment lines from scdaemon won't appear as status lines with keyword '#'. */ assuan_write_line (ctx, line); } else { for (i=0; *line && !spacep (line) && i < DIM(keyword)-1; line++, i++) keyword[i] = *line; keyword[i] = 0; /* Truncate any remaining keyword stuff. */ for (; *line && !spacep (line); line++) ; while (spacep (line)) line++; assuan_write_status (ctx, keyword, line); } return 0; }
/* Callback function for agent_card_getattr. */ static gpg_error_t card_getattr_cb (void *opaque, const char *line) { struct card_getattr_parm_s *parm = opaque; const char *keyword = line; int keywordlen; if (parm->data) return 0; /* We want only the first occurrence. */ for (keywordlen=0; *line && !spacep (line); line++, keywordlen++) ; while (spacep (line)) line++; if (keywordlen == parm->keywordlen && !memcmp (keyword, parm->keyword, keywordlen)) { parm->data = percent_plus_unescape ((const unsigned char*)line, 0xff); if (!parm->data) parm->error = errno; } return 0; }
/* Status callback for the SCD GENKEY command. */ static gpg_error_t scd_genkey_cb (void *opaque, const char *line) { struct agent_card_genkey_s *parm = opaque; const char *keyword = line; int keywordlen; for (keywordlen=0; *line && !spacep (line); line++, keywordlen++) ; while (spacep (line)) line++; if (keywordlen == 7 && !memcmp (keyword, "KEY-FPR", keywordlen)) { parm->fprvalid = unhexify_fpr (line, parm->fpr); } else if (keywordlen == 14 && !memcmp (keyword,"KEY-CREATED-AT", keywordlen)) { parm->created_at = (u32)strtoul (line, NULL, 10); } else if (keywordlen == 8 && !memcmp (keyword, "PROGRESS", keywordlen)) { write_status_text (STATUS_PROGRESS, line); } return 0; }
/* Check whether the option NAME appears in LINE */ static int has_option (const char *line, const char *name) { const char *s; int n = strlen (name); s = strstr (line, name); if (s && s >= skip_options (line)) return 0; return (s && (s == line || spacep (s-1)) && (!s[n] || spacep (s+n))); }
/* Skip over options. Blanks after the options are also removed. */ static char * skip_options (const char *line) { while (spacep (line)) line++; while ( *line == '-' && line[1] == '-' ) { while (*line && !spacep (line)) line++; while (spacep (line)) line++; } return (char*)line; }
gpg_error_t assuan_command_parse_fd (assuan_context_t ctx, char *line, assuan_fd_t *rfd) { char *endp; if ((strncmp (line, "FD", 2) && strncmp (line, "fd", 2)) || (line[2] != '=' && line[2] != '\0' && !spacep(&line[2]))) return set_error (ctx, GPG_ERR_ASS_SYNTAX, "FD[=<n>] expected"); line += 2; if (*line == '=') { line ++; if (!digitp (*line)) return set_error (ctx, GPG_ERR_ASS_SYNTAX, "number required"); #ifdef HAVE_W32_SYSTEM /* Fixme: For a W32/64bit system we will need to change the cast and the conversion function. */ *rfd = (void*)strtoul (line, &endp, 10); #else *rfd = strtoul (line, &endp, 10); #endif /* Remove that argument so that a notify handler won't see it. */ memset (line, ' ', endp? (endp-line):strlen(line)); if (*rfd == ctx->inbound.fd) return set_error (ctx, GPG_ERR_ASS_PARAMETER, "fd same as inbound fd"); if (*rfd == ctx->outbound.fd) return set_error (ctx, GPG_ERR_ASS_PARAMETER, "fd same as outbound fd"); return 0; } else /* Our peer has sent the file descriptor. */ return assuan_receivefd (ctx, rfd); }
/* 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 a standard isotime or a human readable variant into an isotime structure. The allowed formats are those described by isotime_p and isotime_human_p. The function returns 0 on failure or the length of the scanned string on success. */ static int string2isotime (my_isotime_t atime, const char *string) { my_isotime_t dummyatime; if (!atime) atime = dummyatime; atime[0] = 0; if (isotime_p (string)) { memcpy (atime, string, 15); atime[15] = 0; return 15; } if (!isotime_human_p (string)) return 0; atime[0] = string[0]; atime[1] = string[1]; atime[2] = string[2]; atime[3] = string[3]; atime[4] = string[5]; atime[5] = string[6]; atime[6] = string[8]; atime[7] = string[9]; atime[8] = 'T'; memset (atime+9, '0', 6); atime[15] = 0; if (!spacep (string+10)) return 10; if (spacep (string+11)) return 11; /* As per def, second space stops scanning. */ atime[9] = string[11]; atime[10] = string[12]; if (string[13] != ':') return 13; atime[11] = string[14]; atime[12] = string[15]; if (string[16] != ':') return 16; atime[13] = string[17]; atime[14] = string[18]; return 19; }
/* Check the button_info line for a close action. */ static gpg_error_t close_button_status_cb (void *opaque, const char *line) { int *flag = opaque; const char *keyword = line; int keywordlen; for (keywordlen=0; *line && !spacep (line); line++, keywordlen++) ; while (spacep (line)) line++; if (keywordlen == 11 && !memcmp (keyword, "BUTTON_INFO", keywordlen)) { if ( !strcmp (line, "close") ) *flag = 1; } return 0; }
/* Return a pointer to the argument of the option with NAME. If such an option is not given, NULL is returned. */ char * option_value (const char *line, const char *name) { char *s; int n = strlen (name); s = strstr (line, name); if (s && s >= skip_options (line)) return NULL; if (s && (s == line || spacep (s-1)) && s[n] && (spacep (s+n) || s[n] == '=')) { s += n + 1; s += strspn (s, " "); if (*s && !spacep(s)) return s; } return NULL; }
static gpg_error_t std_handler_option (assuan_context_t ctx, char *line) { char *key, *value, *p; for (key=line; spacep (key); key++) ; if (!*key) return PROCESS_DONE (ctx, set_error (ctx, GPG_ERR_ASS_SYNTAX, "argument required")); if (*key == '=') return PROCESS_DONE (ctx, set_error (ctx, GPG_ERR_ASS_SYNTAX, "no option name given")); for (value=key; *value && !spacep (value) && *value != '='; value++) ; if (*value) { if (spacep (value)) *value++ = 0; /* terminate key */ for (; spacep (value); value++) ; if (*value == '=') { *value++ = 0; /* terminate key */ for (; spacep (value); value++) ; if (!*value) return PROCESS_DONE (ctx, set_error (ctx, GPG_ERR_ASS_SYNTAX, "option argument expected")); } if (*value) { for (p = value + strlen(value) - 1; p > value && spacep (p); p--) ; if (p > value) *++p = 0; /* strip trailing spaces */ } } if (*key == '-' && key[1] == '-' && key[2]) key += 2; /* the double dashes are optional */ if (*key == '-') return PROCESS_DONE (ctx, set_error (ctx, GPG_ERR_ASS_SYNTAX, "option should not begin with one dash")); if (ctx->option_handler_fnc) return PROCESS_DONE (ctx, ctx->option_handler_fnc (ctx, key, value)); return PROCESS_DONE (ctx, 0); }
/* Estimate the quality of the passphrase PW and return a value in the range 0..100. */ static int estimate_passphrase_quality (const char *pw) { int goodlength = opt.min_passphrase_len + opt.min_passphrase_len/3; int length; const char *s; if (goodlength < 1) return 0; for (length = 0, s = pw; *s; s++) if (!spacep (s)) length ++; if (length > goodlength) return 100; return ((length*10) / goodlength)*10; }
/* Helper to notify the client about Pinentry events. Because that might disturb some older clients, this is only done when enabled via an option. If it is not enabled we tell Windows to allow setting the foreground window right here. Returns an gpg error code. */ gpg_error_t gpg_proxy_pinentry_notify (ctrl_t ctrl, const unsigned char *line) { if (!ctrl || !ctrl->server_local || !ctrl->server_local->allow_pinentry_notify) { gnupg_allow_set_foregound_window ((pid_t)strtoul (line+17, NULL, 10)); /* Client might be interested in that event - send as status line. */ if (!strncmp (line, "PINENTRY_LAUNCHED", 17) && (line[17]==' '||!line[17])) { for (line += 17; *line && spacep (line); line++) ; write_status_text (STATUS_PINENTRY_LAUNCHED, line); } return 0; } return assuan_inquire (ctrl->server_local->assuan_ctx, line, NULL, NULL, 0); }
static gpg_error_t read_one_trustfile (const char *fname, int allow_include, trustitem_t **addr_of_table, size_t *addr_of_tablesize, int *addr_of_tableidx) { gpg_error_t err = 0; estream_t fp; int n, c; char *p, line[256]; trustitem_t *table, *ti; int tableidx; size_t tablesize; int lnr = 0; table = *addr_of_table; tablesize = *addr_of_tablesize; tableidx = *addr_of_tableidx; fp = es_fopen (fname, "r"); if (!fp) { err = gpg_error_from_syserror (); log_error (_("error opening `%s': %s\n"), fname, gpg_strerror (err)); goto leave; } while (es_fgets (line, DIM(line)-1, fp)) { lnr++; n = strlen (line); if (!n || line[n-1] != '\n') { /* Eat until end of line. */ while ( (c=es_getc (fp)) != EOF && c != '\n') ; err = gpg_error (*line? GPG_ERR_LINE_TOO_LONG : GPG_ERR_INCOMPLETE_LINE); log_error (_("file `%s', line %d: %s\n"), fname, lnr, gpg_strerror (err)); continue; } line[--n] = 0; /* Chop the LF. */ if (n && line[n-1] == '\r') line[--n] = 0; /* Chop an optional CR. */ /* Allow for empty lines and spaces */ for (p=line; spacep (p); p++) ; if (!*p || *p == '#') continue; if (!strncmp (p, "include-default", 15) && (!p[15] || spacep (p+15))) { char *etcname; gpg_error_t err2; if (!allow_include) { log_error (_("statement \"%s\" ignored in `%s', line %d\n"), "include-default", fname, lnr); continue; } /* fixme: Should check for trailing garbage. */ etcname = make_filename (gnupg_sysconfdir (), "trustlist.txt", NULL); if ( !strcmp (etcname, fname) ) /* Same file. */ log_info (_("statement \"%s\" ignored in `%s', line %d\n"), "include-default", fname, lnr); else if ( access (etcname, F_OK) && errno == ENOENT ) { /* A non existent system trustlist is not an error. Just print a note. */ log_info (_("system trustlist `%s' not available\n"), etcname); } else { err2 = read_one_trustfile (etcname, 0, &table, &tablesize, &tableidx); if (err2) err = err2; } xfree (etcname); continue; } if (tableidx == tablesize) /* Need more space. */ { trustitem_t *tmp; size_t tmplen; tmplen = tablesize + 20; tmp = xtryrealloc (table, tmplen * sizeof *table); if (!tmp) { err = gpg_error_from_syserror (); goto leave; } table = tmp; tablesize = tmplen; } ti = table + tableidx; memset (&ti->flags, 0, sizeof ti->flags); if (*p == '!') { ti->flags.disabled = 1; p++; while (spacep (p)) p++; } n = hexcolon2bin (p, ti->fpr, 20); if (n < 0) { log_error (_("bad fingerprint in `%s', line %d\n"), fname, lnr); err = gpg_error (GPG_ERR_BAD_DATA); continue; } p += n; for (; spacep (p); p++) ; /* Process the first flag which needs to be the first for backward compatibility. */ if (!*p || *p == '*' ) { ti->flags.for_smime = 1; ti->flags.for_pgp = 1; } else if ( *p == 'P' || *p == 'p') { ti->flags.for_pgp = 1; } else if ( *p == 'S' || *p == 's') { ti->flags.for_smime = 1; } else { log_error (_("invalid keyflag in `%s', line %d\n"), fname, lnr); err = gpg_error (GPG_ERR_BAD_DATA); continue; } p++; if ( *p && !spacep (p) ) { log_error (_("invalid keyflag in `%s', line %d\n"), fname, lnr); err = gpg_error (GPG_ERR_BAD_DATA); continue; } /* Now check for more key-value pairs of the form NAME[=VALUE]. */ while (*p) { for (; spacep (p); p++) ; if (!*p) break; n = strcspn (p, "= \t"); if (p[n] == '=') { log_error ("assigning a value to a flag is not yet supported; " "in `%s', line %d\n", fname, lnr); err = gpg_error (GPG_ERR_BAD_DATA); p++; } else if (n == 5 && !memcmp (p, "relax", 5)) ti->flags.relax = 1; else if (n == 2 && !memcmp (p, "cm", 2)) ti->flags.cm = 1; else log_error ("flag `%.*s' in `%s', line %d ignored\n", n, p, fname, lnr); p += n; } tableidx++; } if ( !err && !es_feof (fp) ) { err = gpg_error_from_syserror (); log_error (_("error reading `%s', line %d: %s\n"), fname, lnr, gpg_strerror (err)); } leave: es_fclose (fp); *addr_of_table = table; *addr_of_tablesize = tablesize; *addr_of_tableidx = tableidx; return err; }
/* Parse the /etc/gnupg/g13tab for user USERNAME. Return a table for the user on success. Return NULL on error and print diagnostics. */ static tab_item_t parse_g13tab (const char *username) { gpg_error_t err; int c, n; char line[512]; char *p; char *fname; estream_t fp; int lnr; char **words = NULL; tab_item_t table = NULL; tab_item_t *tabletail, ti; fname = make_filename (gnupg_sysconfdir (), G13_NAME"tab", NULL); fp = es_fopen (fname, "r"); if (!fp) { err = gpg_error_from_syserror (); log_error (_("error opening '%s': %s\n"), fname, gpg_strerror (err)); goto leave; } tabletail = &table; err = 0; lnr = 0; while (es_fgets (line, DIM(line)-1, fp)) { lnr++; n = strlen (line); if (!n || line[n-1] != '\n') { /* Eat until end of line. */ while ((c=es_getc (fp)) != EOF && c != '\n') ; err = gpg_error (*line? GPG_ERR_LINE_TOO_LONG : GPG_ERR_INCOMPLETE_LINE); log_error (_("file '%s', line %d: %s\n"), fname, lnr, gpg_strerror (err)); continue; } line[--n] = 0; /* Chop the LF. */ if (n && line[n-1] == '\r') line[--n] = 0; /* Chop an optional CR. */ /* Allow for empty lines and spaces */ for (p=line; spacep (p); p++) ; if (!*p || *p == '#') continue; /* Parse the line. The format is * <username> <blockdev> [<label>|"-" [<mountpoint>]] */ xfree (words); words = strtokenize (p, " \t"); if (!words) { err = gpg_error_from_syserror (); break; } if (!words[0] || !words[1]) { log_error (_("file '%s', line %d: %s\n"), fname, lnr, gpg_strerror (GPG_ERR_SYNTAX)); continue; } if (!(*words[1] == '/' || !strncmp (words[1], "PARTUUID=", 9) || !strncmp (words[1], "partuuid=", 9))) { log_error (_("file '%s', line %d: %s\n"), fname, lnr, "Invalid block device syntax"); continue; } if (words[2]) { if (strlen (words[2]) > 16 || strchr (words[2], '/')) { log_error (_("file '%s', line %d: %s\n"), fname, lnr, "Label too long or invalid syntax"); continue; } if (words[3] && *words[3] != '/') { log_error (_("file '%s', line %d: %s\n"), fname, lnr, "Invalid mountpoint syntax"); continue; } } if (strcmp (words[0], username)) continue; /* Skip entries for other usernames! */ ti = xtrymalloc (sizeof *ti + strlen (words[1])); if (!ti) { err = gpg_error_from_syserror (); break; } ti->next = NULL; ti->label = NULL; ti->mountpoint = NULL; strcpy (ti->blockdev, *words[1]=='/'? words[1] : words[1]+9); if (words[2]) { if (strcmp (words[2], "-") && !(ti->label = xtrystrdup (words[2]))) { err = gpg_error_from_syserror (); xfree (ti); break; } if (words[3] && !(ti->mountpoint = xtrystrdup (words[3]))) { err = gpg_error_from_syserror (); xfree (ti->label); xfree (ti); break; } } *tabletail = ti; tabletail = &ti->next; } if (!err && !es_feof (fp)) err = gpg_error_from_syserror (); if (err) log_error (_("error reading '%s', line %d: %s\n"), fname, lnr, gpg_strerror (err)); leave: xfree (words); es_fclose (fp); xfree (fname); if (err) { release_tab_items (table); return NULL; } return table; }
/* Parse the policy flags by reading them from STREAM and storing them * into FLAGS. If IGNORE_UNKNOWN is iset unknown keywords are * ignored. */ gpg_error_t wks_parse_policy (policy_flags_t flags, estream_t stream, int ignore_unknown) { enum tokens { TOK_SUBMISSION_ADDRESS, TOK_MAILBOX_ONLY, TOK_DANE_ONLY, TOK_AUTH_SUBMIT, TOK_MAX_PENDING, TOK_PROTOCOL_VERSION }; static struct { const char *name; enum tokens token; } keywords[] = { { "submission-address", TOK_SUBMISSION_ADDRESS }, { "mailbox-only", TOK_MAILBOX_ONLY }, { "dane-only", TOK_DANE_ONLY }, { "auth-submit", TOK_AUTH_SUBMIT }, { "max-pending", TOK_MAX_PENDING }, { "protocol-version", TOK_PROTOCOL_VERSION } }; gpg_error_t err = 0; int lnr = 0; char line[1024]; char *p, *keyword, *value; int i, n; memset (flags, 0, sizeof *flags); while (es_fgets (line, DIM(line)-1, stream) ) { lnr++; n = strlen (line); if (!n || line[n-1] != '\n') { err = gpg_error (*line? GPG_ERR_LINE_TOO_LONG : GPG_ERR_INCOMPLETE_LINE); break; } trim_trailing_spaces (line); /* Skip empty and comment lines. */ for (p=line; spacep (p); p++) ; if (!*p || *p == '#') continue; if (*p == ':') { err = gpg_error (GPG_ERR_SYNTAX); break; } keyword = p; value = NULL; if ((p = strchr (p, ':'))) { /* Colon found: Keyword with value. */ *p++ = 0; for (; spacep (p); p++) ; if (!*p) { err = gpg_error (GPG_ERR_MISSING_VALUE); break; } value = p; } for (i=0; i < DIM (keywords); i++) if (!ascii_strcasecmp (keywords[i].name, keyword)) break; if (!(i < DIM (keywords))) { if (ignore_unknown) continue; err = gpg_error (GPG_ERR_INV_NAME); break; } switch (keywords[i].token) { case TOK_SUBMISSION_ADDRESS: if (!value || !*value) { err = gpg_error (GPG_ERR_SYNTAX); goto leave; } xfree (flags->submission_address); flags->submission_address = xtrystrdup (value); if (!flags->submission_address) { err = gpg_error_from_syserror (); goto leave; } break; case TOK_MAILBOX_ONLY: flags->mailbox_only = 1; break; case TOK_DANE_ONLY: flags->dane_only = 1; break; case TOK_AUTH_SUBMIT: flags->auth_submit = 1; break; case TOK_MAX_PENDING: if (!value) { err = gpg_error (GPG_ERR_SYNTAX); goto leave; } /* FIXME: Define whether these are seconds, hours, or days * and decide whether to allow other units. */ flags->max_pending = atoi (value); break; case TOK_PROTOCOL_VERSION: if (!value) { err = gpg_error (GPG_ERR_SYNTAX); goto leave; } flags->protocol_version = atoi (value); break; } } if (!err && !es_feof (stream)) err = gpg_error_from_syserror (); leave: if (err) log_error ("error reading '%s', line %d: %s\n", es_fname_get (stream), lnr, gpg_strerror (err)); return err; }
/* Try to find KEY in the file FNAME. */ static char * findkey_fname (const char *key, const char *fname) { gpg_error_t err = 0; FILE *fp; int lnr = 0; int c; char *p, line[256]; int in_item = 0; membuf_t mb = MEMBUF_ZERO; fp = fopen (fname, "r"); if (!fp) { if (errno != ENOENT) { err = gpg_error_from_syserror (); log_error (_("can't open `%s': %s\n"), fname, gpg_strerror (err)); } return NULL; } while (fgets (line, DIM(line)-1, fp)) { lnr++; if (!*line || line[strlen(line)-1] != '\n') { /* Eat until end of line. */ while ( (c=getc (fp)) != EOF && c != '\n') ; err = gpg_error (*line? GPG_ERR_LINE_TOO_LONG : GPG_ERR_INCOMPLETE_LINE); log_error (_("file `%s', line %d: %s\n"), fname, lnr, gpg_strerror (err)); } else line[strlen(line)-1] = 0; /* Chop the LF. */ again: if (!in_item) { /* Allow for empty lines and spaces while not in an item. */ for (p=line; spacep (p); p++) ; if (!*p || *p == '#') continue; if (*line != '.' || spacep(line+1)) { log_info (_("file `%s', line %d: %s\n"), fname, lnr, _("ignoring garbage line")); continue; } trim_trailing_spaces (line); in_item = 1; if (!strcmp (line+1, key)) { /* Found. Start collecting. */ init_membuf (&mb, 1024); } continue; } /* If in an item only allow for comments in the first column and provide ". " as an escape sequence to allow for leading dots and hash marks in the actual text. */ if (*line == '#') continue; if (*line == '.') { if (spacep(line+1)) p = line + 2; else { trim_trailing_spaces (line); in_item = 0; if (is_membuf_ready (&mb)) break; /* Yep, found and collected the item. */ if (!line[1]) continue; /* Just an end of text dot. */ goto again; /* A new key line. */ } } else p = line; if (is_membuf_ready (&mb)) { put_membuf_str (&mb, p); put_membuf (&mb, "\n", 1); } } if ( !err && ferror (fp) ) { err = gpg_error_from_syserror (); log_error (_("error reading `%s', line %d: %s\n"), fname, lnr, gpg_strerror (err)); } fclose (fp); if (is_membuf_ready (&mb)) { /* We have collected something. */ if (err) { xfree (get_membuf (&mb, NULL)); return NULL; } else { put_membuf (&mb, "", 1); /* Terminate string. */ return get_membuf (&mb, NULL); } } else return NULL; }
/* Return a value describing the the class of PATTERN. The offset of the actual string to be used for the comparison is stored at R_OFFSET. The offset of the serialnumer is stored at R_SN_OFFSET. */ static enum pattern_class classify_pattern (const char *pattern, size_t *r_offset, size_t *r_sn_offset) { enum pattern_class result; const char *s; int hexprefix = 0; int hexlength; *r_offset = *r_sn_offset = 0; /* Skip leading spaces. */ for(s = pattern; *s && spacep (s); s++ ) ; switch (*s) { case 0: /* Empty string is an error. */ result = PATTERN_UNKNOWN; break; case '.': /* An email address, compare from end. */ result = PATTERN_UNKNOWN; /* Not implemented. */ break; case '<': /* An email address. */ result = PATTERN_EMAIL; s++; break; case '@': /* Part of an email address. */ result = PATTERN_EMAIL_SUBSTR; s++; break; case '=': /* Exact compare. */ result = PATTERN_UNKNOWN; /* Does not make sense for X.509. */ break; case '*': /* Case insensitive substring search. */ result = PATTERN_SUBSTR; s++; break; case '+': /* Compare individual words. */ result = PATTERN_UNKNOWN; /* Not implemented. */ break; case '/': /* Subject's DN. */ s++; if (!*s || spacep (s)) result = PATTERN_UNKNOWN; /* No DN or prefixed with a space. */ else result = PATTERN_SUBJECT; break; case '#': /* Serial number or issuer DN. */ { const char *si; s++; if ( *s == '/') { /* An issuer's DN is indicated by "#/" */ s++; if (!*s || spacep (s)) result = PATTERN_UNKNOWN; /* No DN or prefixed with a space. */ else result = PATTERN_ISSUER; } else { /* Serialnumber + optional issuer ID. */ for (si=s; *si && *si != '/'; si++) if (!strchr("01234567890abcdefABCDEF", *si)) break; if (*si && *si != '/') result = PATTERN_UNKNOWN; /* Invalid digit in serial number. */ else { *r_sn_offset = s - pattern; if (!*si) result = PATTERN_SERIALNO; else { s = si+1; if (!*s || spacep (s)) result = PATTERN_UNKNOWN; /* No DN or prefixed with a space. */ else result = PATTERN_SERIALNO_ISSUER; } } } } break; case ':': /* Unified fingerprint. */ { const char *se, *si; int i; se = strchr (++s, ':'); if (!se) result = PATTERN_UNKNOWN; else { for (i=0, si=s; si < se; si++, i++ ) if (!strchr("01234567890abcdefABCDEF", *si)) break; if ( si < se ) result = PATTERN_UNKNOWN; /* Invalid digit. */ else if (i == 32) result = PATTERN_FINGERPRINT16; else if (i == 40) result = PATTERN_FINGERPRINT20; else result = PATTERN_UNKNOWN; /* Invalid length for a fingerprint. */ } } break; case '&': /* Keygrip. */ result = PATTERN_UNKNOWN; /* Not implemented. */ break; default: if (s[0] == '0' && s[1] == 'x') { hexprefix = 1; s += 2; } hexlength = strspn(s, "0123456789abcdefABCDEF"); /* Check if a hexadecimal number is terminated by EOS or blank. */ if (hexlength && s[hexlength] && !spacep (s+hexlength)) { /* If the "0x" prefix is used a correct termination is required. */ if (hexprefix) { result = PATTERN_UNKNOWN; break; /* switch */ } hexlength = 0; /* Not a hex number. */ } if (hexlength == 8 || (!hexprefix && hexlength == 9 && *s == '0')) { if (hexlength == 9) s++; result = PATTERN_SHORT_KEYID; } else if (hexlength == 16 || (!hexprefix && hexlength == 17 && *s == '0')) { if (hexlength == 17) s++; result = PATTERN_LONG_KEYID; } else if (hexlength == 32 || (!hexprefix && hexlength == 33 && *s == '0')) { if (hexlength == 33) s++; result = PATTERN_FINGERPRINT16; } else if (hexlength == 40 || (!hexprefix && hexlength == 41 && *s == '0')) { if (hexlength == 41) s++; result = PATTERN_FINGERPRINT20; } else if (!hexprefix) { /* The fingerprints used with X.509 are often delimited by colons, so we try to single this case out. */ result = PATTERN_UNKNOWN; hexlength = strspn (s, ":0123456789abcdefABCDEF"); if (hexlength == 59 && (!s[hexlength] || spacep (s+hexlength))) { int i, c; for (i=0; i < 20; i++, s += 3) { c = hextobyte(s); if (c == -1 || (i < 19 && s[2] != ':')) break; } if (i == 20) result = PATTERN_FINGERPRINT20; } if (result == PATTERN_UNKNOWN) /* Default to substring match. */ { result = PATTERN_SUBSTR; } } else /* A hex number with a prefix but with a wrong length. */ result = PATTERN_UNKNOWN; } if (result != PATTERN_UNKNOWN) *r_offset = s - pattern; return result; }
int main (int argc, char **argv) { char *args; char *logfile; char *passphrasefile; char *passphrase; /* We get our options via PINENTRY_USER_DATA. */ (void) argc, (void) argv; setvbuf (stdin, NULL, _IOLBF, BUFSIZ); setvbuf (stdout, NULL, _IOLBF, BUFSIZ); args = getenv ("PINENTRY_USER_DATA"); if (! args) args = ""; logfile = option_value (args, "--logfile"); if (logfile) { char *p = logfile, more; while (*p && ! spacep (p)) p++; more = !! *p; *p = 0; args = more ? p+1 : p; log_stream = fopen (logfile, "a"); if (! log_stream) { perror (logfile); return 1; } } passphrasefile = option_value (args, "--passphrasefile"); if (passphrasefile) { char *p = passphrasefile, more; while (*p && ! spacep (p)) p++; more = !! *p; *p = 0; args = more ? p+1 : p; passphrase = get_passphrase (passphrasefile); if (! passphrase) { reply ("# Passphrasefile '%s' is empty. Terminating.\n", passphrasefile); return 1; } p = passphrase + strlen (passphrase) - 1; if (*p == '\n') *p = 0; } else passphrase = skip_options (args); reply ("# fake-pinentry started. Passphrase='%s'.\n", passphrase); reply ("OK - what's up?\n"); while (! feof (stdin)) { char buffer[1024]; if (fgets (buffer, sizeof buffer, stdin) == NULL) break; if (log_stream) fprintf (log_stream, "< %s", buffer); if (strncmp (buffer, "GETPIN", 6) == 0) reply ("D %s\n", passphrase); else if (strncmp (buffer, "BYE", 3) == 0) { reply ("OK\n"); break; } reply ("OK\n"); } reply ("# Connection terminated.\n"); if (log_stream) fclose (log_stream); return 0; }
static int classify_user_id (const char *name, KEYDB_SEARCH_DESC *desc, int *force_exact ) { const char *s; int hexprefix = 0; int hexlength; int mode = 0; /* clear the structure so that the mode field is set to zero unless * we set it to the correct value right at the end of this function */ memset (desc, 0, sizeof *desc); *force_exact = 0; /* Skip leading spaces. Fixme: what about trailing white space? */ for(s = name; *s && spacep (s); s++ ) ; switch (*s) { case 0: /* empty string is an error */ return 0; case '.': /* an email address, compare from end */ mode = KEYDB_SEARCH_MODE_MAILEND; s++; desc->u.name = s; break; case '<': /* an email address */ mode = KEYDB_SEARCH_MODE_MAIL; s++; desc->u.name = s; break; case '@': /* part of an email address */ mode = KEYDB_SEARCH_MODE_MAILSUB; s++; desc->u.name = s; break; case '=': /* exact compare */ mode = KEYDB_SEARCH_MODE_EXACT; s++; desc->u.name = s; break; case '*': /* case insensitive substring search */ mode = KEYDB_SEARCH_MODE_SUBSTR; s++; desc->u.name = s; break; case '+': /* compare individual words */ mode = KEYDB_SEARCH_MODE_WORDS; s++; desc->u.name = s; break; case '/': /* subject's DN */ s++; if (!*s || spacep (s)) return 0; /* no DN or prefixed with a space */ desc->u.name = s; mode = KEYDB_SEARCH_MODE_SUBJECT; break; case '#': { const char *si; s++; if ( *s == '/') { /* "#/" indicates an issuer's DN */ s++; if (!*s || spacep (s)) return 0; /* no DN or prefixed with a space */ desc->u.name = s; mode = KEYDB_SEARCH_MODE_ISSUER; } else { /* serialnumber + optional issuer ID */ for (si=s; *si && *si != '/'; si++) { if (!strchr("01234567890abcdefABCDEF", *si)) return 0; /* invalid digit in serial number*/ } desc->sn = (const unsigned char*)s; desc->snlen = -1; if (!*si) mode = KEYDB_SEARCH_MODE_SN; else { s = si+1; if (!*s || spacep (s)) return 0; /* no DN or prefixed with a space */ desc->u.name = s; mode = KEYDB_SEARCH_MODE_ISSUER_SN; } } } break; case ':': /*Unified fingerprint */ { const char *se, *si; int i; se = strchr (++s,':'); if (!se) return 0; for (i=0,si=s; si < se; si++, i++ ) { if (!strchr("01234567890abcdefABCDEF", *si)) return 0; /* invalid digit */ } if (i != 32 && i != 40) return 0; /* invalid length of fpr*/ for (i=0,si=s; si < se; i++, si +=2) desc->u.fpr[i] = hextobyte(si); for (; i < 20; i++) desc->u.fpr[i]= 0; s = se + 1; mode = KEYDB_SEARCH_MODE_FPR; } break; case '&': /* Keygrip*/ { if (hex2bin (s+1, desc->u.grip, 20) < 0) return 0; /* Invalid. */ mode = KEYDB_SEARCH_MODE_KEYGRIP; } break; default: if (s[0] == '0' && s[1] == 'x') { hexprefix = 1; s += 2; } hexlength = strspn(s, "0123456789abcdefABCDEF"); if (hexlength >= 8 && s[hexlength] =='!') { *force_exact = 1; hexlength++; /* just for the following check */ } /* check if a hexadecimal number is terminated by EOS or blank */ if (hexlength && s[hexlength] && !spacep (s+hexlength)) { if (hexprefix) /* a "0x" prefix without correct */ return 0; /* termination is an error */ /* The first chars looked like a hex number, but really is not */ hexlength = 0; } if (*force_exact) hexlength--; /* remove the bang */ if (hexlength == 8 || (!hexprefix && hexlength == 9 && *s == '0')) { /* short keyid */ unsigned long kid; if (hexlength == 9) s++; kid = strtoul( s, NULL, 16 ); desc->u.kid[4] = kid >> 24; desc->u.kid[5] = kid >> 16; desc->u.kid[6] = kid >> 8; desc->u.kid[7] = kid; mode = KEYDB_SEARCH_MODE_SHORT_KID; } else if (hexlength == 16
/* skip_spaces - skip leading spaces */ int skip_spaces() { while (spacep(*lptr)) lptr++; return (*lptr != EOS); }
/* Scan a string and return true if the string represents the human readable format of an ISO time. This format is: yyyy-mm-dd[ hh[:mm[:ss]]] Scanning stops at the second space or at a comma. */ static int isotime_human_p (const char *string) { const char *s; int i; if (!*string) return 0; for (s=string, i=0; i < 4; i++, s++) if (!digitp (s)) return 0; if (*s != '-') return 0; s++; if (!digitp (s) || !digitp (s+1) || s[2] != '-') return 0; i = atoi_2 (s); if (i < 1 || i > 12) return 0; s += 3; if (!digitp (s) || !digitp (s+1)) return 0; i = atoi_2 (s); if (i < 1 || i > 31) return 0; s += 2; if (!*s || *s == ',') return 1; /* Okay; only date given. */ if (!spacep (s)) return 0; s++; if (spacep (s)) return 1; /* Okay, second space stops scanning. */ if (!digitp (s) || !digitp (s+1)) return 0; i = atoi_2 (s); if (i < 0 || i > 23) return 0; s += 2; if (!*s || *s == ',') return 1; /* Okay; only date and hour given. */ if (*s != ':') return 0; s++; if (!digitp (s) || !digitp (s+1)) return 0; i = atoi_2 (s); if (i < 0 || i > 59) return 0; s += 2; if (!*s || *s == ',') return 1; /* Okay; only date, hour and minute given. */ if (*s != ':') return 0; s++; if (!digitp (s) || !digitp (s+1)) return 0; i = atoi_2 (s); if (i < 0 || i > 60) return 0; s += 2; if (!*s || *s == ',' || spacep (s)) return 1; /* Okay; date, hour and minute and second given. */ return 0; /* Unexpected delimiter. */ }
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; }
void read_exchange_rates (void) { gpg_error_t err = 0; estream_t fp; int lnr = 0; int n, c, idx; char line[256]; char *p, *pend; double rate; fp = es_fopen (euroxref_fname, "r"); if (!fp) { err = gpg_error_from_syserror (); log_error ("error opening '%s': %s\n", euroxref_fname, gpg_strerror (err)); return; } while (es_fgets (line, DIM(line)-1, fp)) { lnr++; n = strlen (line); if (!n || line[n-1] != '\n') { /* Eat until end of line. */ while ((c=es_getc (fp)) != EOF && c != '\n') ; err = gpg_error (*line? GPG_ERR_LINE_TOO_LONG : GPG_ERR_INCOMPLETE_LINE); log_error ("error reading '%s', line %d: %s\n", euroxref_fname, lnr, gpg_strerror (err)); continue; } line[--n] = 0; /* Chop the LF. */ if (n && line[n-1] == '\r') line[--n] = 0; /* Chop an optional CR. */ /* Allow leading spaces and skip empty and comment lines. */ for (p=line; spacep (p); p++) ; if (!*p || *p == '#') continue; /* Parse the currency name. */ pend = strchr (p, '='); if (!pend) { log_error ("error parsing '%s', line %d: %s\n", euroxref_fname, lnr, "missing '='"); continue; } *pend++ = 0; trim_spaces (p); if (!*p) { log_error ("error parsing '%s', line %d: %s\n", euroxref_fname, lnr, "currency name missing"); continue; } /* Note that we start at 1 to skip the first entry which is EUR. */ for (idx=1; idx < DIM(currency_table); idx++) if (!strcasecmp (currency_table[idx].name, p)) break; if (!(idx < DIM(currency_table))) continue; /* Currency not supported. */ /* Parse the rate. */ p = pend; errno = 0; rate = strtod (p, &pend); if ((!rate && p == pend) || errno || rate <= 0.0 || rate > 10000.0) { log_error ("error parsing '%s', line %d: %s\n", euroxref_fname, lnr, "invalid exchange rate"); continue; } p = pend; trim_spaces (p); if (*p) { log_error ("error parsing '%s', line %d: %s\n", euroxref_fname, lnr, "garbage after exchange rate"); continue; } /* Update the tbale. */ if (currency_table[idx].rate != rate) { if (!currency_table[idx].rate) log_info ("setting exchange rate for %s to %.4f\n", currency_table[idx].name, rate); else log_info ("changing exchange rate for %s from %.4f to %.4f\n", currency_table[idx].name, currency_table[idx].rate, rate); currency_table[idx].rate = rate; jrnl_store_exchange_rate_record (currency_table[idx].name, rate); } } es_fclose (fp); }
/* Handle one line of the encfs tool's output. This function is allowed to modify the content of BUFFER. */ static gpg_error_t handle_status_line (runner_t runner, const char *line, enum encfs_cmds cmd, tupledesc_t tuples) { gpg_error_t err; /* Check that encfs understands our new options. */ if (!strncmp (line, "$STATUS$", 8)) { for (line +=8; *line && spacep (line); line++) ; log_info ("got status '%s'\n", line); if (!strcmp (line, "fuse_main_start")) { /* Send a special error code back to let the caller know that everything has been setup by encfs. */ err = gpg_error (GPG_ERR_UNFINISHED); } else err = 0; } else if (!strncmp (line, "$PROMPT$", 8)) { for (line +=8; *line && spacep (line); line++) ; log_info ("got prompt '%s'\n", line); if (!strcmp (line, "create_root_dir")) err = send_cmd (runner, cmd == ENCFS_CMD_CREATE? "y":"n"); else if (!strcmp (line, "create_mount_point")) err = send_cmd (runner, "y"); else if (!strcmp (line, "passwd") || !strcmp (line, "new_passwd")) { if (tuples) { size_t n; const void *value; value = find_tuple (tuples, KEYBLOB_TAG_ENCKEY, &n); if (!value) err = gpg_error (GPG_ERR_INV_SESSION_KEY); else if ((err = send_cmd_bin (runner, value, n))) { if (gpg_err_code (err) == GPG_ERR_BUG && gpg_err_source (err) == GPG_ERR_SOURCE_DEFAULT) err = gpg_error (GPG_ERR_INV_SESSION_KEY); } } else err = gpg_error (GPG_ERR_NO_DATA); } else err = send_cmd (runner, ""); /* Default to send an empty line. */ } else if (strstr (line, "encfs: unrecognized option '")) err = gpg_error (GPG_ERR_INV_ENGINE); else err = 0; return err; }