/* 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; }
/* Read from FP and return a newly allocated buffer in R_BUFFER with the entire data read from FP. */ static gpg_error_t read_response (estream_t fp, unsigned char **r_buffer, size_t *r_buflen) { gpg_error_t err; unsigned char *buffer; size_t bufsize, nbytes; *r_buffer = NULL; *r_buflen = 0; bufsize = 4096; buffer = xtrymalloc (bufsize); if (!buffer) return gpg_error_from_errno (errno); nbytes = 0; for (;;) { unsigned char *tmp; size_t nread = 0; assert (nbytes < bufsize); nread = es_fread (buffer+nbytes, 1, bufsize-nbytes, fp); if (nread < bufsize-nbytes && es_ferror (fp)) { err = gpg_error_from_errno (errno); log_error (_("error reading from responder: %s\n"), strerror (errno)); xfree (buffer); return err; } if ( !(nread == bufsize-nbytes && !es_feof (fp))) { /* Response succesfully received. */ nbytes += nread; *r_buffer = buffer; *r_buflen = nbytes; return 0; } nbytes += nread; /* Need to enlarge the buffer. */ if (bufsize >= MAX_RESPONSE_SIZE) { log_error (_("response from server too large; limit is %d bytes\n"), MAX_RESPONSE_SIZE); xfree (buffer); return gpg_error (GPG_ERR_TOO_LARGE); } bufsize += 4096; tmp = xtryrealloc (buffer, bufsize); if (!tmp) { err = gpg_error_from_errno (errno); xfree (buffer); return err; } buffer = tmp; } }
/* 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; }
static void test_poll (void) { int ret; gpgrt_poll_t fds[3]; char buffer[16]; size_t used, nwritten; int c; memset (fds, 0, sizeof fds); fds[0].stream = test_stdin; fds[0].want_read = 1; fds[1].stream = test_stdout; fds[1].want_write = 1; /* FIXME: We don't use the next stream at all. */ fds[2].stream = test_stderr; fds[2].want_write = 1; fds[2].ignore = 1; used = 0; while (used || !fds[0].ignore) { ret = gpgrt_poll (fds, DIM(fds), -1); if (ret == -1) { fail ("gpgrt_poll failed: %s\n", strerror (errno)); continue; } if (!ret) { fail ("gpgrt_poll unexpectedly timed out\n"); continue; } show ("gpgrt_poll detected %d events\n", ret); if (fds[0].got_read) { /* Read from the producer. */ for (;;) { c = es_fgetc (fds[0].stream); if (c == EOF) { if (es_feof (fds[0].stream)) { show ("reading '%s': EOF\n", peer_stdin.name); fds[0].ignore = 1; /* Not anymore needed. */ peer_stdin.stop_me = 1; /* Tell the thread to stop. */ } else if (es_ferror (fds[0].stream)) { fail ("error reading '%s': %s\n", peer_stdin.name, strerror (errno)); fds[0].ignore = 1; /* Disable. */ peer_stdin.stop_me = 1; /* Tell the thread to stop. */ } else show ("reading '%s': EAGAIN\n", peer_stdin.name); break; } else { if (used <= sizeof buffer -1) buffer[used++] = c; if (used == sizeof buffer) { show ("throttling reading from '%s'\n", peer_stdin.name); fds[0].ignore = 1; break; } } } show ("read from '%s': %zu bytes\n", peer_stdin.name, used); if (used) fds[1].ignore = 0; /* Data to send. */ } if (fds[1].got_write) { if (used) { ret = es_write (fds[1].stream, buffer, used, &nwritten); show ("result for writing to '%s': ret=%d, n=%zu, nwritten=%zu\n", peer_stdout.name, ret, used, nwritten); if (!ret) { assert (nwritten <= used); /* Move the remaining data to the front of buffer. */ memmove (buffer, buffer + nwritten, sizeof buffer - nwritten); used -= nwritten; } ret = es_fflush (fds[1].stream); if (ret) fail ("Flushing for '%s' failed: %s\n", peer_stdout.name, strerror (errno)); } if (!used) fds[1].ignore = 1; /* No need to send data. */ } if (used < sizeof buffer / 2 && !peer_stdin.stop_me && fds[0].ignore) { show ("accelerate reading from '%s'\n", peer_stdin.name); fds[0].ignore = 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; }