static void init_membuf (assuan_context_t ctx, struct membuf *mb, int initiallen, size_t maxlen) { mb->len = 0; mb->size = initiallen; mb->out_of_core = 0; mb->too_large = 0; mb->maxlen = maxlen; /* we need to allocate one byte more for get_membuf */ mb->buf = _assuan_malloc (ctx, initiallen + 1); if (!mb->buf) mb->out_of_core = 1; }
/* Build a command line for use with W32's CreateProcess. On success CMDLINE gets the address of a newly allocated string. */ static int build_w32_commandline (assuan_context_t ctx, const char * const *argv, char **cmdline) { int i, n; const char *s; char *buf, *p; *cmdline = NULL; n = 0; 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 = _assuan_malloc (ctx, n); if (! buf) return -1; for (i = 0; argv[i]; i++) { if (i) p = stpcpy (p, " "); if (! *argv[i]) /* Empty string. */ p = stpcpy (p, "\"\""); else if (strpbrk (argv[i], " \t\n\v\f\"")) { p = stpcpy (p, "\""); for (s = argv[i]; *s; s++) { *p++ = *s; if (*s == '\"') *p++ = *s; } *p++ = '\"'; *p = 0; } else p = stpcpy (p, argv[i]); } *cmdline= buf; return 0; }
void * _assuan_calloc(size_t n, size_t m) { void *p; size_t nbytes; nbytes = n * m; if(m && nbytes / m != n) { errno = ENOMEM; return NULL; } p = _assuan_malloc(nbytes); if(p) memset(p, 0, nbytes); return p; }
gpg_error_t assuan_write_status (assuan_context_t ctx, const char *keyword, const char *text) { char buffer[256]; char *helpbuf; size_t n; gpg_error_t ae; if ( !ctx || !keyword) return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE); if (!text) text = ""; n = 2 + strlen (keyword) + 1 + strlen (text) + 1; if (n < sizeof (buffer)) { strcpy (buffer, "S "); strcat (buffer, keyword); if (*text) { strcat (buffer, " "); strcat (buffer, text); } ae = assuan_write_line (ctx, buffer); } else if ( (helpbuf = _assuan_malloc (ctx, n)) ) { strcpy (helpbuf, "S "); strcat (helpbuf, keyword); if (*text) { strcat (helpbuf, " "); strcat (helpbuf, text); } ae = assuan_write_line (ctx, helpbuf); _assuan_free (ctx, helpbuf); } else ae = 0; return ae; }
/* Set the text used for the next OK reponse. This string is automatically reset to NULL after the next command. */ gpg_error_t assuan_set_okay_line (assuan_context_t ctx, const char *line) { if (!ctx) return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE); if (!line) { _assuan_free (ctx, ctx->okay_line); ctx->okay_line = NULL; } else { /* FIXME: we need to use gcry_is_secure() to test whether we should allocate the entire line in secure memory */ char *buf = _assuan_malloc (ctx, 3 + strlen(line) + 1); if (!buf) return _assuan_error (ctx, gpg_err_code_from_syserror ()); strcpy (buf, "OK "); strcpy (buf+3, line); _assuan_free (ctx, ctx->okay_line); ctx->okay_line = buf; } return 0; }
/* Make a connection to the Unix domain socket NAME and return a new Assuan context in CTX. SERVER_PID is currently not used but may become handy in the future. Defined flag bits are: ASSUAN_SOCKET_CONNECT_FDPASSING sendmsg and recvmsg are used. NAME must either start with a slash and optional with a drive prefix ("c:") or use one of these URL schemata: file://<fname> This is the same as the default just with an explicit schemata. assuan://<ipaddr>:<port> assuan://[<ip6addr>]:<port> Connect using TCP to PORT of the server with the numerical IPADDR. Note that '[' and ']' are literal characters. */ gpg_error_t assuan_socket_connect (assuan_context_t ctx, const char *name, pid_t server_pid, unsigned int flags) { gpg_error_t err = 0; assuan_fd_t fd; #ifdef WITH_IPV6 struct sockaddr_in6 srvr_addr_in6; #endif struct sockaddr_un srvr_addr_un; struct sockaddr_in srvr_addr_in; struct sockaddr *srvr_addr = NULL; uint16_t port = 0; size_t len = 0; const char *s; int af = AF_LOCAL; int pf = PF_LOCAL; TRACE2 (ctx, ASSUAN_LOG_CTX, "assuan_socket_connect", ctx, "name=%s, flags=0x%x", name ? name : "(null)", flags); if (!ctx || !name) return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE); if (!strncmp (name, "file://", 7) && name[7]) name += 7; else if (!strncmp (name, "assuan://", 9) && name[9]) { name += 9; af = AF_INET; pf = PF_INET; } else /* Default. */ { /* We require that the name starts with a slash if no URL schemata is used. To make things easier we allow an optional drive prefix. */ s = name; if (*s && s[1] == ':') s += 2; if (*s != DIRSEP_C && *s != '/') return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE); } if (af == AF_LOCAL) { if (strlen (name)+1 >= sizeof srvr_addr_un.sun_path) return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE); memset (&srvr_addr_un, 0, sizeof srvr_addr_un); srvr_addr_un.sun_family = AF_LOCAL; strncpy (srvr_addr_un.sun_path, name, sizeof (srvr_addr_un.sun_path) - 1); srvr_addr_un.sun_path[sizeof (srvr_addr_un.sun_path) - 1] = 0; len = SUN_LEN (&srvr_addr_un); srvr_addr = (struct sockaddr *)&srvr_addr_un; } else { char *addrstr, *p; #ifdef HAVE_INET_PTON void *addrbuf = NULL; #endif addrstr = _assuan_malloc (ctx, strlen (name) + 1); if (!addrstr) return _assuan_error (ctx, gpg_err_code_from_syserror ()); if (*name == '[') { strcpy (addrstr, name+1); p = strchr (addrstr, ']'); if (!p || p[1] != ':' || !parse_portno (p+2, &port)) err = _assuan_error (ctx, GPG_ERR_BAD_URI); else { *p = 0; #ifdef WITH_IPV6 af = AF_INET6; pf = PF_INET6; memset (&srvr_addr_in6, 0, sizeof srvr_addr_in6); srvr_addr_in6.sin6_family = af; srvr_addr_in6.sin6_port = htons (port); #ifdef HAVE_INET_PTON addrbuf = &srvr_addr_in6.sin6_addr; #endif srvr_addr = (struct sockaddr *)&srvr_addr_in6; len = sizeof srvr_addr_in6; #else err = _assuan_error (ctx, GPG_ERR_EAFNOSUPPORT); #endif } } else { strcpy (addrstr, name); p = strchr (addrstr, ':'); if (!p || !parse_portno (p+1, &port)) err = _assuan_error (ctx, GPG_ERR_BAD_URI); else { *p = 0; memset (&srvr_addr_in, 0, sizeof srvr_addr_in); srvr_addr_in.sin_family = af; srvr_addr_in.sin_port = htons (port); #ifdef HAVE_INET_PTON addrbuf = &srvr_addr_in.sin_addr; #endif srvr_addr = (struct sockaddr *)&srvr_addr_in; len = sizeof srvr_addr_in; } } if (!err) { #ifdef HAVE_INET_PTON switch (inet_pton (af, addrstr, addrbuf)) { case 1: break; case 0: err = _assuan_error (ctx, GPG_ERR_BAD_URI); break; default: err = _assuan_error (ctx, gpg_err_code_from_syserror ()); } #else /*!HAVE_INET_PTON*/ /* We need to use the old function. If we are here v6 support isn't enabled anyway and thus we can do fine without. Note that Windows as a compatible inet_pton function named inetPton, but only since Vista. */ srvr_addr_in.sin_addr.s_addr = inet_addr (addrstr); if (srvr_addr_in.sin_addr.s_addr == INADDR_NONE) err = _assuan_error (ctx, GPG_ERR_BAD_URI); #endif /*!HAVE_INET_PTON*/ } _assuan_free (ctx, addrstr); if (err) return err; } fd = _assuan_sock_new (ctx, pf, SOCK_STREAM, 0); if (fd == ASSUAN_INVALID_FD) { err = _assuan_error (ctx, gpg_err_code_from_syserror ()); TRACE1 (ctx, ASSUAN_LOG_SYSIO, "assuan_socket_connect", ctx, "can't create socket: %s", strerror (errno)); return err; } if (_assuan_sock_connect (ctx, fd, srvr_addr, len) == -1) { TRACE2 (ctx, ASSUAN_LOG_SYSIO, "assuan_socket_connect", ctx, "can't connect to `%s': %s\n", name, strerror (errno)); _assuan_close (ctx, fd); return _assuan_error (ctx, GPG_ERR_ASS_CONNECT_FAILED); } err = _assuan_connect_finalize (ctx, fd, flags); if (err) _assuan_reset (ctx); return err; }
/* This function is similar to pipe_connect but uses a socketpair and sets the I/O up to use sendmsg/recvmsg. */ static gpg_error_t socketpair_connect (assuan_context_t ctx, const char *name, const char **argv, assuan_fd_t *fd_child_list, void (*atfork) (void *opaque, int reserved), void *atforkvalue) { gpg_error_t err; int idx; int fds[2]; char mypidstr[50]; pid_t pid; int *child_fds = NULL; int child_fds_cnt = 0; struct at_socketpair_fork atp; int rc; TRACE_BEG3 (ctx, ASSUAN_LOG_CTX, "socketpair_connect", ctx, "name=%s,atfork=%p,atforkvalue=%p", name ? name : "(null)", atfork, atforkvalue); atp.user_atfork = atfork; atp.user_atforkvalue = atforkvalue; atp.parent_pid = getpid (); if (!ctx || (name && (!argv || !argv[0])) || (!name && !argv)) return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE); if (! ctx->flags.no_fixsignals) fix_signals (); sprintf (mypidstr, "%lu", (unsigned long)getpid ()); if (fd_child_list) while (fd_child_list[child_fds_cnt] != ASSUAN_INVALID_FD) child_fds_cnt++; child_fds = _assuan_malloc (ctx, (child_fds_cnt + 2) * sizeof (int)); if (! child_fds) return TRACE_ERR (gpg_err_code_from_syserror ()); child_fds[1] = ASSUAN_INVALID_FD; if (fd_child_list) memcpy (&child_fds[1], fd_child_list, (child_fds_cnt + 1) * sizeof (int)); if (_assuan_socketpair (ctx, AF_LOCAL, SOCK_STREAM, 0, fds)) { TRACE_LOG1 ("socketpair failed: %s", strerror (errno)); _assuan_free (ctx, child_fds); return TRACE_ERR (GPG_ERR_ASS_GENERAL); } atp.peer_fd = fds[1]; child_fds[0] = fds[1]; rc = _assuan_spawn (ctx, &pid, name, argv, ASSUAN_INVALID_FD, ASSUAN_INVALID_FD, child_fds, at_socketpair_fork_cb, &atp, 0); if (rc < 0) { err = gpg_err_code_from_syserror (); _assuan_close (ctx, fds[0]); _assuan_close (ctx, fds[1]); _assuan_free (ctx, child_fds); return TRACE_ERR (err); } /* For W32, the user needs to know the server-local names of the inherited handles. Return them here. Note that the translation of the peer socketpair fd (fd_child_list[0]) must be done by the wrapper program based on the environment variable _assuan_connection_fd. */ if (fd_child_list) { for (idx = 0; fd_child_list[idx] != -1; idx++) /* We add 1 to skip over the socketpair end. */ fd_child_list[idx] = child_fds[idx + 1]; } _assuan_free (ctx, child_fds); /* If this is the server child process, exit early. */ if (! name && (*argv)[0] == 's') { _assuan_close (ctx, fds[0]); return 0; } _assuan_close (ctx, fds[1]); ctx->engine.release = _assuan_client_release; ctx->finish_handler = _assuan_client_finish; ctx->max_accepts = 1; ctx->inbound.fd = fds[0]; ctx->outbound.fd = fds[0]; _assuan_init_uds_io (ctx); err = initial_handshake (ctx); if (err) _assuan_reset (ctx); return err; }
/* Build a command line for use with W32's CreateProcess. On success CMDLINE gets the address of a newly allocated string. */ static int build_w32_commandline (assuan_context_t ctx, const char * const *argv, assuan_fd_t fd0, assuan_fd_t fd1, assuan_fd_t fd2, int fd2_isnull, char **cmdline) { int i, n; const char *s; char *buf, *p; char fdbuf[3*30]; p = fdbuf; *p = 0; if (fd0 != ASSUAN_INVALID_FD) { snprintf (p, 25, "-&S0=%d ", (int)fd0); p += strlen (p); } if (fd1 != ASSUAN_INVALID_FD) { snprintf (p, 25, "-&S1=%d ", (int)fd1); p += strlen (p); } if (fd2 != ASSUAN_INVALID_FD) { if (fd2_isnull) strcpy (p, "-&S2=null "); else snprintf (p, 25, "-&S2=%d ", (int)fd2); p += strlen (p); } *cmdline = NULL; n = strlen (fdbuf); for (i=0; (s = argv[i]); i++) { if (!i) continue; /* Ignore argv[0]. */ n += strlen (s) + 1 + 2; /* (1 space, 2 quoting) */ for (; *s; s++) if (*s == '\"') n++; /* Need to double inner quotes. */ } n++; buf = p = _assuan_malloc (ctx, n); if (! buf) return -1; p = stpcpy (p, fdbuf); for (i = 0; argv[i]; i++) { if (!i) continue; /* Ignore argv[0]. */ if (i > 1) p = stpcpy (p, " "); if (! *argv[i]) /* Empty string. */ p = stpcpy (p, "\"\""); else if (strpbrk (argv[i], " \t\n\v\f\"")) { p = stpcpy (p, "\""); for (s = argv[i]; *s; s++) { *p++ = *s; if (*s == '\"') *p++ = *s; } *p++ = '\"'; *p = 0; } else p = stpcpy (p, argv[i]); } *cmdline = buf; return 0; }