summaryrefslogtreecommitdiff
path: root/usr/src/lib/libpkg/common/pkgweb.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/lib/libpkg/common/pkgweb.c')
-rw-r--r--usr/src/lib/libpkg/common/pkgweb.c3240
1 files changed, 0 insertions, 3240 deletions
diff --git a/usr/src/lib/libpkg/common/pkgweb.c b/usr/src/lib/libpkg/common/pkgweb.c
deleted file mode 100644
index bcda3e53f2..0000000000
--- a/usr/src/lib/libpkg/common/pkgweb.c
+++ /dev/null
@@ -1,3240 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License (the "License").
- * You may not use this file except in compliance with the License.
- *
- * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
- * or http://www.opensolaris.org/os/licensing.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
- * If applicable, add the following below this CDDL HEADER, with the
- * fields enclosed by brackets "[]" replaced with your own identifying
- * information: Portions Copyright [yyyy] [name of copyright owner]
- *
- * CDDL HEADER END
- */
-
-/*
- * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
- * Use is subject to license terms.
- */
-
-/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
-/* All Rights Reserved */
-
-
-#include <stdio.h>
-#include <limits.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-#include <pkglocs.h>
-#include <locale.h>
-#include <libintl.h>
-#include <libgen.h>
-#include <signal.h>
-#include <sys/stat.h>
-#include <sys/statvfs.h>
-#include <sys/types.h>
-#include <fcntl.h>
-#include <dirent.h>
-#include <boot_http.h>
-#include <errno.h>
-#include <ctype.h>
-#include <openssl/pkcs7.h>
-#include <openssl/ocsp.h>
-#include <openssl/pkcs12.h>
-#include <openssl/err.h>
-#include <openssl/x509.h>
-#include <openssl/pem.h>
-#include <openssl/evp.h>
-#include <openssl/rand.h>
-#include <openssl/x509v3.h>
-#include "pkglib.h"
-#include "pkglibmsgs.h"
-#include "pkglocale.h"
-#include "keystore.h"
-#include "pkgweb.h"
-#include "pkgerr.h"
-#include "p12lib.h"
-
-/* fixed format when making an OCSP request */
-#define OCSP_REQUEST_FORMAT \
- "POST %s HTTP/1.0\r\n" \
- "Content-Type: application/ocsp-request\r\n" \
- "Content-Length: %d\r\n\r\n"
-
-/*
- * no security is afforded by using this phrase to "encrypt" CA certificates,
- * but it might aid in debugging and has to be non-null
- */
-#define WEB_CA_PHRASE "schizophrenic"
-
-/* This one needs the ': ' at the end */
-#define CONTENT_TYPE_HDR "Content-Type"
-#define CONTENT_DISPOSITION_HDR "Content-Disposition"
-#define CONTENT_OCSP_RESP "application/ocsp-response"
-#define CONTENT_LENGTH_HDR "Content-Length"
-#define LAST_MODIFIED_HDR "Last-Modified"
-#define OCSP_BUFSIZ 1024
-
-/*
- * default amount of time that is allowed for error when checking
- * OCSP response validity.
- * For example, if this is set to 5 minutes, then if a response
- * is issued that is valid from 12:00 to 1:00, then we will
- * accept it if the local time is between 11:55 and 1:05.
- * This takes care of not-quite-synchronized server and client clocks.
- */
-#define OCSP_VALIDITY_PERIOD (5 * 60)
-
-/* this value is defined by getpassphrase(3c) manpage */
-#define MAX_PHRASELEN 257
-
-/* Max length of "enter password again" prompt message */
-#define MAX_VERIFY_MSGLEN 1024
-
-/* local prototypes */
-static boolean_t remove_dwnld_file(char *);
-static boolean_t get_ENV_proxyport(PKG_ERR *, ushort_t *);
-static boolean_t make_link(char *, char *);
-static WebStatus web_send_request(PKG_ERR *, int, int, int);
-static boolean_t web_eval_headers(PKG_ERR *);
-static WebStatus web_get_file(PKG_ERR *, char *, int, char **);
-static boolean_t ck_dwnld_dir_space(PKG_ERR *, char *, ulong_t);
-static WebStatus web_connect(PKG_ERR *);
-static boolean_t web_setup(PKG_ERR *);
-static boolean_t check_dwnld_dir(PKG_ERR *, char *);
-static boolean_t parse_url_proxy(PKG_ERR *, char *, char *, ushort_t);
-static boolean_t web_disconnect(void);
-static char *get_unique_filename(char *, char *);
-static boolean_t get_ENV_proxy(PKG_ERR *, char **);
-static char *condense_lastmodified(char *);
-static int web_verify(int, X509_STORE_CTX *);
-static int get_issuer(X509 **issuer, X509_STORE_CTX *ctx, X509 *x);
-static boolean_t get_ocsp_uri(X509 *, char **);
-static OCSPStatus ocsp_verify(PKG_ERR *, X509 *, X509 *, char *, url_hport_t *,
- STACK_OF(X509) *);
-static char *get_time_string(ASN1_GENERALIZEDTIME *);
-static char *write_ca_file(PKG_ERR *, char *, STACK_OF(X509) *, char *);
-static boolean_t _get_random_info(void *, int);
-static boolean_t init_session(void);
-static void progress_setup(int, ulong_t);
-static void progress_report(int, ulong_t);
-static void progress_finish(int);
-static char *replace_token(char *, char, char);
-static void dequote(char *);
-static void trim(char *);
-
-
-/*
- * structure used to hold data passed back to the
- * X509 verify callback routine in validate_signature()
- */
-typedef struct {
- url_hport_t *proxy;
- PKG_ERR *err;
- STACK_OF(X509) *cas;
-} verify_cb_data_t;
-
-/* Progress bar variables */
-static ulong_t const_increment, const_divider, completed, const_completed;
-
-/* current network backoff wait period */
-static int cur_backoff = 0;
-
-/* download session context handle */
-static WEB_SESSION *ps;
-
-static int webpkg_install = 0;
-static char *prompt = NULL;
-static char *passarg = NULL;
-
-
-/* ~~~~~~~~~~~~~~ Public Functions ~~~~~~~~~~~~~~~~~~~ */
-
-/*
- * Name: set_prompt
- * Description: Specifies the prompt to use with the pkglib
- * passphrase callback routine.
- *
- * Arguments: newprompt - The prompt to display
- *
- * Returns : NONE
- */
-void
-set_passphrase_prompt(char *newprompt)
-{
- prompt = newprompt;
-}
-
-/*
- * Name: set_passarg
- * Description: Specifies the passphrase retrieval method
- * to use with the pkglib
- * passphrase callback routine.
- *
- * Arguments: newpassarg - The new password retrieval arg
- *
- * Returns : NONE
- */
-void
-set_passphrase_passarg(char *newpassarg)
-{
- passarg = newpassarg;
-}
-
-/*
- * Name: get_proxy_port
- * Description: Resolves proxy specification
- *
- * Arguments: err - where to record any errors.
- * proxy - Location to store result - if *proxy is not
- * null, then it will be validated, but not changed
- *
- * Returns : B_TRUE - success, B_FALSE otherwise
- * on success, *proxy and *port are set to either
- * the user-supplied proxy and port, or the
- * ones found in the environment variables
- * HTTPPROXY and/or HTTPROXYPORT
- */
-boolean_t
-get_proxy_port(PKG_ERR *err, char **proxy, ushort_t *port)
-{
- if (*proxy != NULL) {
- if (!path_valid(*proxy)) {
- /* bad proxy supplied */
- pkgerr_add(err, PKGERR_WEB,
- gettext(ERR_BAD_PROXY), *proxy);
- return (B_FALSE);
- }
- if (!get_ENV_proxyport(err, port)) {
- /* env set, but bad */
- return (B_FALSE);
- }
- } else {
- if (!get_ENV_proxy(err, proxy)) {
- /* environment variable set, but bad */
- return (B_FALSE);
- }
- if ((*proxy != NULL) && !path_valid(*proxy)) {
- /* env variable set, but bad */
- pkgerr_add(err, PKGERR_WEB,
- gettext(ERR_BAD_PROXY), *proxy);
- return (B_FALSE);
- }
- if (!get_ENV_proxyport(err, port)) {
- /* env variable set, but bad */
- return (B_FALSE);
- }
- }
- return (B_TRUE);
-}
-
-/*
- * Name: path_valid
- * Description: Checks a string for being a valid path
- *
- * Arguments: path - path to validate
- *
- * Returns : B_TRUE - success, B_FALSE otherwise.
- * B_FALSE means path was null, too long (>PATH_MAX),
- * or too short (<1)
- */
-boolean_t
-path_valid(char *path)
-{
- if (path == NULL) {
- return (B_FALSE);
- } else if (strlen(path) > PATH_MAX) {
- return (B_FALSE);
- } else if (strlen(path) >= 1) {
- return (B_TRUE);
- } else {
- /* path < 1 */
- return (B_FALSE);
- }
-}
-
-/*
- * Name: web_cleanup
- * Description: Deletes temp files, closes, frees memory taken
- * by 'ps' static structure
- *
- * Arguments: none
- *
- * Returns : none
- */
-void
-web_cleanup(void)
-{
- PKG_ERR *err;
-
- if (ps == NULL)
- return;
-
- err = pkgerr_new();
-
- if (ps->keystore) {
- (void) close_keystore(err, ps->keystore, NULL);
- }
-
- ps->keystore = NULL;
-
- pkgerr_free(err);
-
- if (ps->uniqfile) {
- (void) remove_dwnld_file(ps->uniqfile);
- free(ps->uniqfile);
- ps->uniqfile = NULL;
- }
- if (ps->link) {
- (void) remove_dwnld_file(ps->link);
- free(ps->link);
- ps->link = NULL;
- }
- if (ps->dwnld_dir) {
- (void) rmdir(ps->dwnld_dir);
- ps->dwnld_dir = NULL;
- }
- if (ps->errstr) {
- free(ps->errstr);
- ps->errstr = NULL;
- }
-
- if (ps->content) {
- free(ps->content);
- ps->content = NULL;
- }
-
- if (ps->resp) {
- http_free_respinfo(ps->resp);
- ps->resp = NULL;
- }
-
- if (ps) {
- free(ps);
- ps = NULL;
- }
-}
-
-/*
- * Name: web_session_control
- * Description: Downloads an arbitrary URL and saves to disk.
- *
- * Arguments: err - where to record any errors.
- * url - URL pointing to content to download - can be
- * http:// or https://
- * dwnld_dir - Directory to download into
- * keystore - keystore to use for accessing trusted
- * certs when downloading using SSL
- * proxy - HTTP proxy to use, or NULL for no proxy
- * proxy_port - HTTP proxy port to use, ignored
- * if proxy is NULL
- * passarg - method to retrieve password
- * retries - # of times to retry download before
- * giving up
- * timeout - how long to wait before retrying,
- * when download is interrupted
- * nointeract - if non-zero, do not output
- * download progress to screen
- *
- * Returns : B_TRUE - success, B_FALSE otherwise
- */
-boolean_t
-web_session_control(PKG_ERR *err, char *url, char *dwnld_dir,
- keystore_handle_t keystore, char *proxy, ushort_t proxy_port,
- int retries, int timeout, int nointeract, char **fname)
-{
- int i;
- boolean_t ret = B_TRUE;
- boolean_t retrieved = B_FALSE;
-
- if (!init_session()) {
- ret = B_FALSE;
- goto cleanup;
- }
-
- if (!parse_url_proxy(err, url, proxy, proxy_port)) {
- ret = B_FALSE;
- goto cleanup;
- }
-
- ps->timeout = timeout;
-
- if (keystore != NULL)
- ps->keystore = keystore;
-
- if (dwnld_dir != NULL)
- ps->dwnld_dir = xstrdup(dwnld_dir);
- else {
- pkgerr_add(err, PKGERR_WEB, gettext(ERR_NO_DWNLD_DIR));
- ret = B_FALSE;
- goto cleanup;
- }
-
- if (!check_dwnld_dir(err, dwnld_dir)) {
- ret = B_FALSE;
- goto cleanup;
- }
-
- for (i = 0; i < retries && !retrieved; i++) {
- if (!web_setup(err)) {
- ret = B_FALSE;
- goto cleanup;
- }
-
- switch (web_connect(err)) {
- /* time out and wait a little bit for these failures */
- case WEB_OK:
- /* were able to connect */
- reset_backoff();
- break;
- case WEB_TIMEOUT:
- echo_out(nointeract, gettext(MSG_DWNLD_TIMEOUT));
- (void) web_disconnect();
- backoff();
- continue;
-
- case WEB_CONNREFUSED:
- echo_out(nointeract, gettext(MSG_DWNLD_CONNREF),
- ps->url.hport.hostname);
- (void) web_disconnect();
- backoff();
- continue;
- case WEB_HOSTDOWN:
- echo_out(nointeract, gettext(MSG_DWNLD_HOSTDWN),
- ps->url.hport.hostname);
- (void) web_disconnect();
- backoff();
- continue;
-
- default:
- /* every other failure is a hard failure, so bail */
- ret = B_FALSE;
- goto cleanup;
- }
-
- switch (web_send_request(err, HTTP_REQ_TYPE_HEAD,
- ps->data.cur_pos, ps->data.content_length)) {
- case WEB_OK:
- /* were able to connect */
- reset_backoff();
- break;
- case WEB_TIMEOUT:
- echo_out(nointeract, gettext(MSG_DWNLD_TIMEOUT));
- (void) web_disconnect();
- backoff();
- continue;
-
- case WEB_CONNREFUSED:
- echo_out(nointeract, gettext(MSG_DWNLD_CONNREF),
- ps->url.hport.hostname);
- (void) web_disconnect();
- backoff();
- continue;
- case WEB_HOSTDOWN:
- echo_out(nointeract, gettext(MSG_DWNLD_HOSTDWN),
- ps->url.hport.hostname);
- (void) web_disconnect();
- backoff();
- continue;
- default:
- /* every other case is failure, so bail */
- ret = B_FALSE;
- goto cleanup;
- }
-
- if (!web_eval_headers(err)) {
- ret = B_FALSE;
- goto cleanup;
- }
-
- switch (web_get_file(err, dwnld_dir, nointeract, fname)) {
- case WEB_OK:
- /* were able to retrieve file */
- retrieved = B_TRUE;
- reset_backoff();
- break;
-
- case WEB_TIMEOUT:
- echo_out(nointeract, gettext(MSG_DWNLD_TIMEOUT));
- (void) web_disconnect();
- backoff();
- continue;
-
- case WEB_CONNREFUSED:
- echo_out(nointeract, gettext(MSG_DWNLD_CONNREF),
- ps->url.hport.hostname);
- (void) web_disconnect();
- backoff();
- continue;
- case WEB_HOSTDOWN:
- echo_out(nointeract, gettext(MSG_DWNLD_HOSTDWN),
- ps->url.hport.hostname);
- (void) web_disconnect();
- backoff();
- continue;
- default:
- /* every other failure is a hard failure, so bail */
- ret = B_FALSE;
- goto cleanup;
- }
- }
-
- if (!retrieved) {
- /* max retries attempted */
- pkgerr_add(err, PKGERR_WEB,
- gettext(ERR_DWNLD_FAILED), retries);
- ret = B_FALSE;
- }
-cleanup:
- (void) web_disconnect();
- if (!ret) {
- pkgerr_add(err, PKGERR_WEB, gettext(ERR_DWNLD), url);
- }
- return (ret);
-}
-
-/*
- * Name: get_signature
- * Description: retrieves signature from signed package.
- *
- * Arguments: err - where to record any errors.
- * ids_name - name of package stream, for error reporting
- * devp - Device on which package resides that we
- * result - where to store resulting PKCS7 signature
- *
- * Returns : B_TRUE - package is signed and signature returned OR
- * package is not signed, in which case result is NULL
- *
- * B_FALSE - there were problems accessing signature,
- * and it is unknown whether it is signed or not. Errors
- * recorded in 'err'.
- */
-boolean_t
-get_signature(PKG_ERR *err, char *ids_name, struct pkgdev *devp, PKCS7 **result)
-{
- char path[PATH_MAX];
- int len, fd = -1;
- struct stat buf;
- FILE *fp = NULL;
- boolean_t ret = B_TRUE;
- BIO *sig_in = NULL;
- PKCS7 *p7 = NULL;
-
- /*
- * look for signature. If one was in the stream,
- * it is now extracted
- */
- if (((len = snprintf(path, PATH_MAX, "%s/%s", devp->dirname,
- SIGNATURE_FILENAME)) >= PATH_MAX) || (len < 0)) {
- pkgerr_add(err, PKGERR_WEB, gettext(ERR_LEN), ids_name);
- ret = B_FALSE;
- goto cleanup;
- }
-
- if ((fd = open(path, O_RDONLY|O_NONBLOCK)) == -1) {
- /*
- * only if the signature is non-existant
- * do we "pass"
- */
- if (errno != ENOENT) {
- pkgerr_add(err, PKGERR_WEB, gettext(ERR_OPENSIG),
- strerror(errno));
- ret = B_FALSE;
- goto cleanup;
- }
- } else {
- /* found sig file. parse it. */
- if (fstat(fd, &buf) == -1) {
- pkgerr_add(err, PKGERR_WEB,
- gettext(ERR_OPENSIG), strerror(errno));
- ret = B_FALSE;
- goto cleanup;
- }
-
- if (!S_ISREG(buf.st_mode)) {
- pkgerr_add(err, PKGERR_WEB, gettext(ERR_OPENSIG),
- (gettext(ERR_NOT_REG)));
- ret = B_FALSE;
- goto cleanup;
- }
-
- if ((fp = fdopen(fd, "r")) == NULL) {
- pkgerr_add(err, PKGERR_WEB,
- gettext(ERR_OPENSIG), strerror(errno));
- ret = B_FALSE;
- goto cleanup;
- }
-
- /*
- * read in signature. If it's invalid, we
- * punt, unless we're ignoring it
- */
- if ((sig_in = BIO_new_fp(fp, BIO_NOCLOSE)) == NULL) {
- pkgerr_add(err, PKGERR_WEB,
- gettext(ERR_OPENSIG), strerror(errno));
- goto cleanup;
- }
-
- if ((p7 = PEM_read_bio_PKCS7(sig_in,
- NULL, NULL, NULL)) == NULL) {
- pkgerr_add(err, PKGERR_WEB, gettext(ERR_CORRUPTSIG),
- ids_name);
- ret = B_FALSE;
- goto cleanup;
- }
- *result = p7;
- p7 = NULL;
- }
-
-cleanup:
- if (sig_in)
- (void) BIO_free(sig_in);
- if (fp)
- (void) fclose(fp);
- if (fd != -1)
- (void) close(fd);
- if (p7)
- (void) PKCS7_free(p7);
-
- return (ret);
-}
-
-/*
- * Name: echo_out
- * Description: Conditionally output a message to stdout
- *
- * Arguments: nointeract - if non-zero, do not output anything
- * fmt - print format
- * ... - print arguments
- *
- * Returns : none
- */
-/*PRINTFLIKE2*/
-void
-echo_out(int nointeract, char *fmt, ...)
-{
- va_list ap;
-
- va_start(ap, fmt);
-
- if (nointeract)
- return;
-
- (void) vfprintf(stdout, fmt, ap);
-
- va_end(ap);
-
- (void) putc('\n', stdout);
-}
-
-/*
- * Name: strip_port
- * Description: Returns "port" portion of a "hostname:port" string
- *
- * Arguments: proxy - full "hostname:port" string pointer
- *
- * Returns : the "port" portion of a "hostname:port" string,
- * converted to a decimal integer, or (int)0
- * if string contains no :port suffix.
- */
-ushort_t
-strip_port(char *proxy)
-{
- char *tmp_port;
-
- if ((tmp_port = strpbrk(proxy, ":")) != NULL)
- return (atoi(tmp_port));
- else
- return (0);
-}
-
-/*
- * Name: set_web_install
- * Description: Sets flag indicating we are doing a web-based install
- *
- * Arguments: none
- *
- * Returns : none
- */
-void
-set_web_install(void)
-{
- webpkg_install++;
-}
-
-/*
- * Name: is_web_install
- * Description: Determines whether we are doing a web-based install
- *
- * Arguments: none
- *
- * Returns : non-zero if we are doing a web-based install, 0 otherwise
- */
-int
-is_web_install(void)
-{
- return (webpkg_install);
-}
-
-/* ~~~~~~~~~~~~~~ Private Functions ~~~~~~~~~~~~~~~~~~~ */
-
-/*
- * Name: web_disconnect
- * Description: Disconnects connection to web server
- *
- * Arguments: none
- *
- * Returns : B_TRUE - successful disconnect, B_FALSE otherwise
- * Temp certificiate files are deleted,
- * if one was used to initiate the connection
- * (such as when using SSL)
- */
-static boolean_t
-web_disconnect(void)
-{
- if (ps->certfile) {
- (void) unlink(ps->certfile);
- }
- if (http_srv_disconnect(ps->hps) == 0)
- if (http_srv_close(ps->hps) == 0)
- return (B_TRUE);
-
- return (B_FALSE);
-}
-
-/*
- * Name: check_dwnld_dir
- * Description: Creates temp download directory
- *
- * Arguments: err - where to record any errors.
- * dwnld_dir - name of directory to create
- *
- * Returns : B_TRUE - success, B_FALSE otherwise
- * on success, directory is created with
- * safe permissions
- */
-static boolean_t
-check_dwnld_dir(PKG_ERR *err, char *dwnld_dir)
-{
- DIR *dirp;
-
- /*
- * Check the directory passed in. If it doesn't exist, create it
- * with strict permissions
- */
- if ((dirp = opendir(dwnld_dir)) == NULL) {
- if (mkdir(dwnld_dir, 0744) == -1) {
- pkgerr_add(err, PKGERR_WEB, gettext(MSG_NOTEMP),
- dwnld_dir);
- return (B_FALSE);
- }
- }
- if (dirp) {
- (void) closedir(dirp);
- }
- return (B_TRUE);
-}
-
-/*
- * Name: ds_validate_signature
- * Description: Validates signature found in a package datastream
- *
- * Arguments: err - where to record any errors.
- * pkgdev - Package context handle of package to verify
- * pkgs - Null-terminated List of package name to verify
- * ids_name - Pathname to stream to validate
- * p7 - PKCS7 signature decoded from stream header
- * cas - List of trusted CA certificates
- * proxy - Proxy to use when doing online validation (OCSP)
- * nointeract - if non-zero, do not output to screen
- *
- * Returns : B_TRUE - success, B_FALSE otherwise
- * success means signature was completely validated,
- * and contents of stream checked against signature.
- */
-boolean_t
-ds_validate_signature(PKG_ERR *err, struct pkgdev *pkgdev, char **pkgs,
- char *ids_name, PKCS7 *p7, STACK_OF(X509) *cas,
- url_hport_t *proxy, int nointeract)
-{
- BIO *p7_bio;
- boolean_t ret = B_TRUE;
-
- /* make sure it's a Signed PKCS7 message */
- if (!PKCS7_type_is_signed(p7)) {
- pkgerr_add(err, PKGERR_WEB, gettext(ERR_CORRUPTSIG_TYPE),
- ids_name);
- ret = B_FALSE;
- goto cleanup;
- }
-
- /* initialize PKCS7 object to be filled in */
- if (!PKCS7_get_detached(p7)) {
- pkgerr_add(err, PKGERR_WEB, gettext(ERR_CORRUPTSIG_DT),
- ids_name);
- ret = B_FALSE;
- goto cleanup;
- }
-
- /* dump header and packages into BIO to calculate the message digest */
- if ((p7_bio = PKCS7_dataInit(p7, NULL)) == NULL) {
- pkgerr_add(err, PKGERR_WEB, gettext(ERR_CORRUPTSIG),
- ids_name);
- ret = B_FALSE;
- goto cleanup;
- }
-
- if ((BIO_ds_dump_header(err, p7_bio) != 0) ||
- (BIO_ds_dump(err, ids_name, p7_bio) != 0)) {
- ret = B_FALSE;
- goto cleanup;
- }
- (void) BIO_flush(p7_bio);
-
- /* validate the stream and its signature */
- if (!validate_signature(err, ids_name, p7_bio, p7, cas,
- proxy, nointeract)) {
- ret = B_FALSE;
- goto cleanup;
- }
-
- /* reset device stream (really bad performance for tapes) */
- (void) ds_close(1);
- (void) ds_init(ids_name, pkgs, pkgdev->norewind);
-
-cleanup:
- return (ret);
-}
-
-
-/*
- * Name: validate_signature
- * Description: Validates signature of an arbitrary stream of bits
- *
- * Arguments: err - where to record any errors.
- * name - Descriptive name of object being validated,
- * for good error reporting messages
- * indata - BIO object to read stream bits from
- * p7 - PKCS7 signature of stream
- * cas - List of trusted CA certificates
- * proxy - Proxy to use when doing online validation (OCSP)
- * nointeract - if non-zero, do not output to screen
- *
- * Returns : B_TRUE - success, B_FALSE otherwise
- * success means signature was completely validated,
- * and contents of stream checked against signature.
- */
-boolean_t
-validate_signature(PKG_ERR *err, char *name, BIO *indata, PKCS7 *p7,
- STACK_OF(X509) *cas, url_hport_t *proxy, int nointeract)
-{
- STACK_OF(PKCS7_SIGNER_INFO) *sec_sinfos = NULL;
-
- PKCS7_SIGNER_INFO *signer = NULL;
- X509_STORE *sec_truststore = NULL;
- X509_STORE_CTX *ctx = NULL;
- X509 *signer_cert = NULL, *issuer = NULL;
- STACK_OF(X509) *chaincerts = NULL;
- int i, k;
- unsigned long errcode;
- const char *err_data = NULL;
- const char *err_reason = NULL;
- char *err_string;
- int err_flags;
- verify_cb_data_t verify_data;
- char *signer_sname;
- char *signer_iname;
- PKCS7_ISSUER_AND_SERIAL *ias;
- boolean_t ret = B_TRUE;
-
- /* only support signed PKCS7 signatures */
- if (!PKCS7_type_is_signed(p7)) {
- PKCS7err(PKCS7_F_PKCS7_DATAVERIFY, PKCS7_R_WRONG_PKCS7_TYPE);
- ret = B_FALSE;
- goto cleanup;
- }
-
- /* initialize temporary internal trust store used for verification */
- sec_truststore = X509_STORE_new();
-
- for (i = 0; i < sk_X509_num(cas); i++) {
- if (X509_STORE_add_cert(sec_truststore,
- sk_X509_value(cas, i)) == 0) {
- pkgerr_add(err, PKGERR_VERIFY, gettext(ERR_MEM));
- ret = B_FALSE;
- goto cleanup;
- }
- }
-
- /* get signers from the signature */
- if ((sec_sinfos = PKCS7_get_signer_info(p7)) == NULL) {
- pkgerr_add(err, PKGERR_WEB, gettext(ERR_CORRUPTSIG), name);
- ret = B_FALSE;
- goto cleanup;
- }
-
- /* verify each signer found in the PKCS7 signature */
- for (k = 0; k < sk_PKCS7_SIGNER_INFO_num(sec_sinfos); k++) {
- signer = sk_PKCS7_SIGNER_INFO_value(sec_sinfos, k);
- signer_cert = PKCS7_cert_from_signer_info(p7, signer);
- signer_sname = get_subject_display_name(signer_cert);
- signer_iname = get_issuer_display_name(signer_cert);
-
- echo_out(nointeract, gettext(MSG_VERIFY), signer_sname);
-
- /* find the issuer of the current cert */
- chaincerts = p7->d.sign->cert;
- ias = signer->issuer_and_serial;
- issuer = X509_find_by_issuer_and_serial(chaincerts,
- ias->issuer, ias->serial);
-
- /* were we not able to find the issuer cert */
- if (issuer == NULL) {
- pkgerr_add(err, PKGERR_WEB,
- gettext(ERR_VERIFY_ISSUER),
- signer_iname, signer_sname);
- ret = B_FALSE;
- goto cleanup;
- }
-
- /* Lets verify */
- if ((ctx = X509_STORE_CTX_new()) == NULL) {
- pkgerr_add(err, PKGERR_VERIFY, gettext(ERR_MEM));
- ret = B_FALSE;
- goto cleanup;
- }
- (void) X509_STORE_CTX_init(ctx, sec_truststore,
- issuer, chaincerts);
- (void) X509_STORE_CTX_set_purpose(ctx,
- X509_PURPOSE_ANY);
-
- /* callback will perform OCSP on certificates with OCSP data */
- X509_STORE_CTX_set_verify_cb(ctx, web_verify);
-
- /* pass needed data into callback through the app_data handle */
- verify_data.proxy = proxy;
- verify_data.cas = cas;
- verify_data.err = err;
- (void) X509_STORE_CTX_set_app_data(ctx, &verify_data);
-
- /* first verify the certificate chain */
- i = X509_verify_cert(ctx);
- if (i <= 0 && ctx->error != X509_V_ERR_CERT_HAS_EXPIRED) {
- signer_sname =
- get_subject_display_name(ctx->current_cert);
- signer_iname =
- get_issuer_display_name(ctx->current_cert);
- /* if the verify context holds an error, print it */
- if (ctx->error != X509_V_OK) {
- pkgerr_add(err, PKGERR_VERIFY,
- gettext(ERR_VERIFY_SIG), signer_sname,
- signer_iname,
- (char *)X509_verify_cert_error_string(ctx->error));
- } else {
- /* some other error. print them all. */
- while ((errcode = ERR_get_error_line_data(NULL,
- NULL, &err_data, &err_flags)) != 0) {
- size_t errsz;
- err_reason =
- ERR_reason_error_string(errcode);
- if (err_reason == NULL) {
- err_reason =
- gettext(ERR_SIG_INT);
- }
-
- if (!(err_flags & ERR_TXT_STRING)) {
- err_data =
- gettext(ERR_SIG_INT);
- }
- errsz = strlen(err_reason) +
- strlen(err_data) + 3;
- err_string = xmalloc(errsz);
- (void) snprintf(err_string, errsz,
- "%s: %s", err_reason, err_data);
- pkgerr_add(err, PKGERR_VERIFY,
- gettext(ERR_VERIFY_SIG),
- signer_sname, signer_iname,
- err_string);
- free(err_string);
- }
- }
- ret = B_FALSE;
- goto cleanup;
- }
-
- /* now verify the signature */
- i = PKCS7_signatureVerify(indata, p7, signer, issuer);
-
- if (i <= 0) {
- /* print out any OpenSSL-specific errors */
- signer_sname =
- get_subject_display_name(ctx->current_cert);
- signer_iname =
- get_subject_display_name(ctx->current_cert);
- while ((errcode = ERR_get_error_line_data(NULL,
- NULL, &err_data, &err_flags)) != 0) {
- err_reason =
- ERR_reason_error_string(errcode);
- if (err_reason == NULL) {
- err_reason =
- gettext(ERR_SIG_INT);
- }
-
- if (!(err_flags & ERR_TXT_STRING)) {
- err_data =
- gettext(ERR_SIG_INT);
- }
- pkgerr_add(err, PKGERR_VERIFY,
- gettext(ERR_VERIFY_SIG), signer_sname,
- signer_iname, err_reason);
- pkgerr_add(err, PKGERR_VERIFY,
- gettext(ERR_VERIFY_SIG), signer_sname,
- signer_iname, err_data);
- }
- ret = B_FALSE;
- goto cleanup;
- }
-
- echo_out(nointeract, gettext(MSG_VERIFY_OK), signer_sname);
- }
-
- /* signature(s) verified successfully */
-cleanup:
- if (ctx)
- X509_STORE_CTX_cleanup(ctx);
- return (ret);
-}
-
-/*
- * Name: web_verify
- * Description: Callback used by PKCS7_dataVerify when
- * verifying a certificate chain.
- *
- * Arguments: err - where to record any errors.
- * ctx - The context handle of the current verification operation
- *
- * Returns : B_TRUE - success, B_FALSE otherwise
- * if it's '0' (not OK) we simply return it, since the
- * verification operation has already determined that the
- * cert is invalid. if 'ok' is non-zero, then we do our
- * checks, and return 0 or 1 based on if the cert is
- * invalid or valid.
- */
-static int
-web_verify(int ok, X509_STORE_CTX *ctx)
-{
- X509 *curr_cert;
- X509 *curr_issuer;
- char *uri;
- url_hport_t *proxy;
- PKG_ERR *err = NULL;
- STACK_OF(X509) *cas;
- if (!ok) {
- /* don't override a verify failure */
- return (ok);
- }
-
-
- /* get app data supplied through callback context */
- err = ((verify_cb_data_t *)X509_STORE_CTX_get_app_data(ctx))->err;
- proxy = ((verify_cb_data_t *)X509_STORE_CTX_get_app_data(ctx))->proxy;
- cas = ((verify_cb_data_t *)X509_STORE_CTX_get_app_data(ctx))->cas;
-
- /* Check revocation status */
- curr_cert = X509_STORE_CTX_get_current_cert(ctx);
-
- /* this shouldn't happen */
- if (curr_cert == NULL) {
- pkgerr_add(err, PKGERR_INTERNAL, gettext(ERR_PKG_INTERNAL),
- __FILE__, __LINE__);
- return (0);
- }
-
- /* don't perform OCSP unless cert has required OCSP extensions */
- if (get_ocsp_uri(curr_cert, &uri)) {
- if (get_issuer(&curr_issuer, ctx, curr_cert) <= 0) {
- /* no issuer! */
- pkgerr_add(err, PKGERR_INTERNAL,
- gettext(ERR_PKG_INTERNAL),
- __FILE__, __LINE__);
- return (0);
- }
-
- /*
- * ok we have the current cert
- * and its issuer. Do the OCSP check
- */
-
- /*
- * OCSP extensions are, by, RFC 2459, never critical
- * extensions, therefore, we only fail if we were able
- * to explicitly contact an OCSP responder, and that
- * responder did not indicate the cert was valid. We
- * also fail if user-supplied data could not be parsed
- * or we run out of memory. We succeeed for "soft"
- * failures, such as not being able to connect to the
- * OCSP responder, or trying to use if the OCSP URI
- * indicates SSL must be used (which we do not
- * support)
- */
- switch (ocsp_verify(err, curr_cert, curr_issuer,
- uri, proxy, cas)) {
- case OCSPMem: /* Ran out of memory */
- case OCSPInternal: /* Some internal error */
- case OCSPVerify: /* OCSP responder indicated fail */
- return (0);
- }
- /* all other cases are success, or soft failures */
- pkgerr_clear(err);
- }
-
- return (ok);
-}
-
-/*
- * Name: get_time_string
- * Description: Generates a human-readable string from an ASN1_GENERALIZED_TIME
- *
- * Arguments: intime - The time to convert
- *
- * Returns : A pointer to a static string representing the passed-in time.
- */
-static char
-*get_time_string(ASN1_GENERALIZEDTIME *intime)
-{
-
- static char time[ATTR_MAX];
- BIO *mem;
- char *p;
-
- if (intime == NULL) {
- return (NULL);
- }
- if ((mem = BIO_new(BIO_s_mem())) == NULL) {
- return (NULL);
- }
-
- if (ASN1_GENERALIZEDTIME_print(mem, intime) == 0) {
- (void) BIO_free(mem);
- return (NULL);
- }
-
- if (BIO_gets(mem, time, ATTR_MAX) <= 0) {
- (void) BIO_free(mem);
- return (NULL);
- }
-
- (void) BIO_free(mem);
-
- /* trim the end of the string */
- for (p = time + strlen(time) - 1; isspace(*p); p--) {
- *p = '\0';
- }
-
- return (time);
-}
-
-/*
- * Name: get_ocsp_uri
- * Description: Examines an X509 certificate and retrieves the embedded
- * OCSP Responder URI if one exists.
- *
- * Arguments: cert - The cert to inspect
- * uri - pointer where the newly-allocated URI is placed, if found
- *
- * Returns : Success if the URI was found. Appropriate status otherwise.
- */
-static boolean_t
-get_ocsp_uri(X509 *cert, char **uri)
-{
- AUTHORITY_INFO_ACCESS *aia;
- ACCESS_DESCRIPTION *ad;
- int i;
-
- if (getenv("PKGWEB_TEST_OCSP")) {
- *uri = xstrdup(getenv("PKGWEB_TEST_OCSP"));
- return (B_TRUE);
- }
-
- /* get the X509v3 extension holding the OCSP URI */
- if ((aia = X509_get_ext_d2i(cert, NID_info_access,
- NULL, NULL)) != NULL) {
- for (i = 0; i < sk_ACCESS_DESCRIPTION_num(aia); i++) {
- ad = sk_ACCESS_DESCRIPTION_value(aia, i);
- if (OBJ_obj2nid(ad->method) == NID_ad_OCSP) {
- if (ad->location->type == GEN_URI) {
- *uri =
- xstrdup((char *)ASN1_STRING_data(ad->location->d.ia5));
- return (B_TRUE);
- }
- }
- }
- }
-
- /* no URI was found */
- return (B_FALSE);
-}
-
-/*
- * Name: ocsp_verify
- * Description: Attempts to contact an OCSP Responder and ascertain the validity
- * of an X509 certificate.
- *
- * Arguments: err - Error object to add error messages to
- * cert - The cert to validate
- * issuer - The certificate of the issuer of 'cert'
- * uri - The OCSP Responder URI
- * cas - The trusted CA certificates used to verify the
- * signed OCSP response
- * Returns : Success - The OCSP Responder reported a 'good'
- * status for the cert otherwise, appropriate
- * error is returned.
- */
-static OCSPStatus
-ocsp_verify(PKG_ERR *err, X509 *cert, X509 *issuer,
- char *uri, url_hport_t *proxy, STACK_OF(X509) *cas)
-{
- OCSP_CERTID *id;
- OCSP_REQUEST *req;
- OCSP_RESPONSE *resp;
- OCSP_BASICRESP *bs;
- BIO *cbio, *mem;
- char ocspbuf[OCSP_BUFSIZ];
- char *host = NULL, *portstr = NULL, *path = "/", *p, *q, *r;
- int port, status, reason;
- int len, retval, respcode, use_ssl = 0;
- ASN1_GENERALIZEDTIME *rev, *thisupd, *nextupd;
- char *subjname;
- time_t currtime;
- char currtimestr[ATTR_MAX];
- unsigned long errcode;
- const char *err_reason;
-
- subjname = get_subject_display_name(cert);
-
- /* parse the URI into its constituent parts */
- if (OCSP_parse_url(uri, &host, &portstr, &path, &use_ssl) == NULL) {
- pkgerr_add(err, PKGERR_PARSE, gettext(ERR_OCSP_PARSE), uri);
- return (OCSPParse);
- }
-
- /* we don't currently support SSL-based OCSP Responders */
- if (use_ssl) {
- pkgerr_add(err, PKGERR_PARSE, gettext(ERR_OCSP_UNSUP), uri);
- return (OCSPUnsupported);
- }
-
- /* default port if none specified */
- if (portstr == NULL) {
- port = (int)URL_DFLT_SRVR_PORT;
- } else {
- port = (int)strtoul(portstr, &r, 10);
- if (*r != '\0') {
- pkgerr_add(err, PKGERR_PARSE,
- gettext(ERR_OCSP_PARSE), uri);
- return (OCSPParse);
- }
- }
-
- /* allocate new request structure */
- if ((req = OCSP_REQUEST_new()) == NULL) {
- pkgerr_add(err, PKGERR_PARSE, gettext(ERR_MEM));
- return (OCSPMem);
- }
-
- /* convert cert and issuer fields into OCSP request data */
- if ((id = OCSP_cert_to_id(NULL, cert, issuer)) == NULL) {
- pkgerr_add(err, PKGERR_PARSE, gettext(ERR_PKG_INTERNAL),
- __FILE__, __LINE__);
- return (OCSPInternal);
- }
-
- /* fill out request structure with request data */
- if ((OCSP_request_add0_id(req, id)) == NULL) {
- pkgerr_add(err, PKGERR_PARSE, gettext(ERR_PKG_INTERNAL),
- __FILE__, __LINE__);
- return (OCSPInternal);
- }
-
- /* add nonce */
- (void) OCSP_request_add1_nonce(req, NULL, -1);
-
- /* connect to host, or proxy */
- if (proxy != NULL) {
- if ((cbio = BIO_new_connect(proxy->hostname)) == NULL) {
- pkgerr_add(err, PKGERR_PARSE, gettext(ERR_MEM));
- return (OCSPMem);
- }
-
- /*
- * BIO_set_conn_int_port takes an int *, so let's give it one
- * rather than an ushort_t *
- */
- port = proxy->port;
- (void) BIO_set_conn_int_port(cbio, &port);
- if (BIO_do_connect(cbio) <= 0) {
- pkgerr_add(err, PKGERR_PARSE,
- gettext(ERR_OCSP_CONNECT),
- proxy->hostname, port);
- return (OCSPConnect);
- }
- } else {
- if ((cbio = BIO_new_connect(host)) == NULL) {
- pkgerr_add(err, PKGERR_PARSE, gettext(ERR_MEM));
- return (OCSPMem);
- }
-
- (void) BIO_set_conn_int_port(cbio, &port);
- if (BIO_do_connect(cbio) <= 0) {
- pkgerr_add(err, PKGERR_PARSE,
- gettext(ERR_OCSP_CONNECT),
- host, port);
- return (OCSPConnect);
- }
- }
-
- /* calculate length of binary request data */
- len = i2d_OCSP_REQUEST(req, NULL);
-
- /* send the request headers */
- if (proxy != NULL) {
- retval = BIO_printf(cbio, OCSP_REQUEST_FORMAT, uri, len);
- } else {
- retval = BIO_printf(cbio, OCSP_REQUEST_FORMAT, path, len);
- }
-
- if (retval <= 0) {
- pkgerr_add(err, PKGERR_PARSE, gettext(ERR_OCSP_SEND), host);
- return (OCSPRequest);
- }
-
- /* send the request binary data */
- if (i2d_OCSP_REQUEST_bio(cbio, req) <= 0) {
- pkgerr_add(err, PKGERR_PARSE, gettext(ERR_OCSP_SEND), host);
- return (OCSPRequest);
- }
-
- /*
- * read the response into a memory BIO, so we can 'gets'
- * (socket bio's don't support BIO_gets)
- */
- if ((mem = BIO_new(BIO_s_mem())) == NULL) {
- pkgerr_add(err, PKGERR_PARSE, gettext(ERR_MEM));
- return (OCSPMem);
- }
-
- while ((len = BIO_read(cbio, ocspbuf, OCSP_BUFSIZ))) {
- if (len < 0) {
- pkgerr_add(err, PKGERR_PARSE,
- gettext(ERR_OCSP_READ), host);
- return (OCSPRequest);
- }
- if (BIO_write(mem, ocspbuf, len) != len) {
- pkgerr_add(err, PKGERR_PARSE, gettext(ERR_MEM));
- return (OCSPMem);
- }
- }
-
- /* now get the first line of the response */
- if (BIO_gets(mem, ocspbuf, OCSP_BUFSIZ) <= 0) {
- pkgerr_add(err, PKGERR_PARSE, gettext(ERR_OCSP_RESP_PARSE));
- return (OCSPRequest);
- }
-
- /* parse the header response */
- /* it should look like "HTTP/x.x 200 OK" */
-
- /* skip past the protocol info */
- for (p = ocspbuf; (*p != '\0') && !isspace(*p); p++)
- continue;
-
- /* skip past whitespace betwen protocol and start of response code */
- while ((*p != '\0') && isspace(*p)) {
- p++;
- }
-
- if (*p == '\0') {
- /* premature end */
- pkgerr_add(err, PKGERR_PARSE,
- gettext(ERR_OCSP_RESP_PARSE), ocspbuf);
- return (OCSPRequest);
- }
-
- /* find end of response code */
- for (q = p; (*q != NULL) && !isspace(*q); q++)
- continue;
-
- /* mark end of response code */
- *q++ = '\0';
-
- /* parse response code */
- respcode = strtoul(p, &r, 10);
- if (*r != '\0') {
- pkgerr_add(err, PKGERR_PARSE,
- gettext(ERR_OCSP_RESP_PARSE), ocspbuf);
- return (OCSPRequest);
- }
-
- /* now find beginning of the response string */
- while ((*q != NULL) && isspace(*q)) {
- q++;
- }
-
- /* trim whitespace from end of message */
- for (r = (q + strlen(q) - 1); isspace(*r); r--) {
- *r = '\0';
- }
-
- /* response must be OK */
- if (respcode != 200) {
- pkgerr_add(err, PKGERR_PARSE,
- gettext(ERR_OCSP_RESP_NOTOK), 200,
- respcode, q);
- return (OCSPRequest);
- }
-
- /* read headers, looking for content-type or a blank line */
- while (BIO_gets(mem, ocspbuf, OCSP_BUFSIZ) > 0) {
-
- /* if we get a content type, make sure it's the right type */
- if (ci_strneq(ocspbuf, CONTENT_TYPE_HDR,
- strlen(CONTENT_TYPE_HDR))) {
-
- /* look for the delimiting : */
- p = strchr(ocspbuf + strlen(CONTENT_TYPE_HDR), ':');
-
- if (p == NULL) {
- pkgerr_add(err, PKGERR_PARSE,
- gettext(ERR_OCSP_RESP_PARSE), ocspbuf);
- return (OCSPResponder);
- }
-
- /* skip over ':' */
- p++;
-
- /* find beginning of the content type */
- while ((*p != NULL) && isspace(*p)) {
- p++;
- }
-
- if (!ci_strneq(p, CONTENT_OCSP_RESP,
- strlen(CONTENT_OCSP_RESP))) {
- /* response is not right type */
- pkgerr_add(err, PKGERR_PARSE,
- gettext(ERR_OCSP_RESP_TYPE),
- p, CONTENT_OCSP_RESP);
- return (OCSPResponder);
- }
-
- /* continue with next header line */
- continue;
- }
-
- /* scan looking for a character */
- for (p = ocspbuf; (*p != '\0') && isspace(*p); p++) {
- continue;
- }
- /*
- * if we got to the end of the line with
- * no chars, then this is a blank line
- */
- if (*p == '\0') {
- break;
- }
- }
-
-
- if (*p != '\0') {
- /* last line was not blank */
- pkgerr_add(err, PKGERR_PARSE,
- gettext(ERR_OCSP_RESP_PARSE), ocspbuf);
- return (OCSPResponder);
- }
-
- /* now read in the binary response */
- if ((resp = d2i_OCSP_RESPONSE_bio(mem, NULL)) == NULL) {
- pkgerr_add(err, PKGERR_PARSE, gettext(ERR_OCSP_READ), host);
- return (OCSPResponder);
- }
-
- /* free temp BIOs */
- (void) BIO_free(mem);
- (void) BIO_free_all(cbio);
- cbio = NULL;
-
- /* make sure request was successful */
- if (OCSP_response_status(resp) != OCSP_RESPONSE_STATUS_SUCCESSFUL) {
- pkgerr_add(err, PKGERR_PARSE, gettext(ERR_OCSP_RESP_NOTOK),
- OCSP_RESPONSE_STATUS_SUCCESSFUL,
- OCSP_response_status(resp),
- OCSP_response_status_str(OCSP_response_status(resp)));
- return (OCSPResponder);
- }
-
- /* parse binary response into internal structure */
- if ((bs = OCSP_response_get1_basic(resp)) == NULL) {
- pkgerr_add(err, PKGERR_PARSE, gettext(ERR_OCSP_READ), host);
- return (OCSPParse);
- }
-
- /*
- * From here to the end of the code, the return values
- * should be hard failures
- */
-
- /* verify the response, warn if no nonce */
- if (OCSP_check_nonce(req, bs) <= 0) {
- logerr(pkg_gt(WRN_OCSP_RESP_NONCE));
- }
-
- if (OCSP_basic_verify(bs, cas, NULL, OCSP_TRUSTOTHER) <= 0) {
- while ((errcode = ERR_get_error()) != NULL) {
- err_reason = ERR_reason_error_string(errcode);
- if (err_reason == NULL) {
- err_reason =
- gettext(ERR_SIG_INT);
- }
- pkgerr_add(err, PKGERR_PARSE, (char *)err_reason);
- }
- pkgerr_add(err, PKGERR_PARSE, gettext(ERR_OCSP_VERIFY_FAIL),
- uri);
- return (OCSPVerify);
- }
-
- /* check the validity of our certificate */
- if (OCSP_resp_find_status(bs, id, &status, &reason,
- &rev, &thisupd, &nextupd) == NULL) {
- pkgerr_add(err, PKGERR_PARSE,
- gettext(ERR_OCSP_VERIFY_NO_STATUS), subjname);
- return (OCSPVerify);
- }
-
- if ((currtime = time(NULL)) == (time_t)-1) {
- pkgerr_add(err, PKGERR_PARSE,
- gettext(ERR_OCSP_VERIFY_NOTIME));
- return (OCSPVerify);
- }
-
- (void) strlcpy(currtimestr, ctime(&currtime), ATTR_MAX);
-
- /* trim end */
- for (r = currtimestr + strlen(currtimestr) - 1;
- isspace(*r); r--) {
- *r = '\0';
- }
-
- if (!OCSP_check_validity(thisupd, nextupd,
- OCSP_VALIDITY_PERIOD, -1)) {
- if (nextupd != NULL) {
- pkgerr_add(err, PKGERR_PARSE,
- gettext(ERR_OCSP_VERIFY_VALIDITY),
- get_time_string(thisupd), get_time_string(nextupd),
- currtimestr);
- } else {
- pkgerr_add(err, PKGERR_PARSE,
- gettext(ERR_OCSP_VERIFY_VALIDITY),
- get_time_string(thisupd),
- currtimestr);
- }
- return (OCSPVerify);
- }
-
- if (status != V_OCSP_CERTSTATUS_GOOD) {
- pkgerr_add(err, PKGERR_PARSE,
- gettext(ERR_OCSP_VERIFY_STATUS), subjname,
- OCSP_cert_status_str(status));
- return (OCSPVerify);
- }
-
- /* everythign checks out */
- return (OCSPSuccess);
-}
-
-/*
- * Name: get_issuer
- * Description: Attempts to find the issuing certificate for a given certificate
- * This will look in both the list of trusted certificates found in
- * the X509_STORE_CTX structure, as well as the list of untrusted
- * chain certificates found in the X509_STORE_CTX structure.
- * Arguments:
- * issuer - The resulting issuer cert is placed here, if found
- * ctx - The current verification context
- * x - The certificate whose issuer we are looking for
- * Returns : Success - The issuer cert was found and placed in *issuer.
- * otherwise, appropriate error is returned.
- */
-static int
-get_issuer(X509 **issuer, X509_STORE_CTX *ctx, X509 *x)
-{
- int i, ok;
-
- /*
- * first look in the list of trusted
- * certs, using the context's method to do so
- */
- if ((ok = ctx->get_issuer(issuer, ctx, x)) > 0) {
- return (ok);
- }
-
- if (ctx->untrusted != NULL) {
- /* didn't find it in trusted certs, look through untrusted */
- for (i = 0; i < sk_X509_num(ctx->untrusted); i++) {
- if (X509_check_issued(sk_X509_value(ctx->untrusted, i),
- x) == X509_V_OK) {
- *issuer = sk_X509_value(ctx->untrusted, i);
- return (1);
- }
- }
- }
- *issuer = NULL;
- return (0);
-}
-
-/*
- * Name: parse_url_proxy
- * Description: Parses URL and optional proxy specification, populates static
- * 'ps' structure
- *
- * Arguments: err - where to record any errors.
- * url - URL to parse
- * proxy - proxy to parse, or NULL for no proxy
- * proxy_port - Default proxy port to use if no proxy
- * port specified in 'proxy'
- *
- * Returns : B_TRUE - success, B_FALSE otherwise
- * on success, 'ps->url' and 'ps->proxy' are populated
- * with parsed data.
- */
-static boolean_t
-parse_url_proxy(PKG_ERR *err, char *url, char *proxy, ushort_t proxy_port)
-{
- boolean_t ret = B_TRUE;
- if (!path_valid(url)) {
- ret = B_FALSE;
- goto cleanup;
- }
-
- if (url_parse(url, &ps->url) != URL_PARSE_SUCCESS) {
- pkgerr_add(err, PKGERR_WEB, gettext(ERR_PARSE_URL), url);
- ret = B_FALSE;
- goto cleanup;
- }
-
- if (proxy != NULL) {
- if (url_parse_hostport(proxy, &ps->proxy, proxy_port)
- != URL_PARSE_SUCCESS) {
- pkgerr_add(err, PKGERR_WEB,
- gettext(ERR_BAD_PROXY), proxy);
- ret = B_FALSE;
- goto cleanup;
- }
- }
-
-cleanup:
- return (ret);
-}
-
-/*
- * Name: web_setup
- * Description: Initializes http library settings
- *
- * Arguments: err - where to record any errors.
- *
- * Returns : B_TRUE - success, B_FALSE otherwise
- */
-static boolean_t
-web_setup(PKG_ERR *err)
-{
- boolean_t ret = B_TRUE;
- static boolean_t keepalive = B_TRUE;
-
- if ((ps->hps = http_srv_init(&ps->url)) == NULL) {
- pkgerr_add(err, PKGERR_WEB, gettext(ERR_INIT_SESS), ps->url);
- ret = B_FALSE;
- goto cleanup;
- }
-
- if (getenv("WEBPKG_DEBUG") != NULL) {
- http_set_verbose(B_TRUE);
- }
-
- if (ps->proxy.hostname[0] != '\0' &&
- http_set_proxy(ps->hps, &ps->proxy) != 0) {
- pkgerr_add(err, PKGERR_WEB, gettext(ERR_INIT_SESS), ps->url);
- ret = B_FALSE;
- goto cleanup;
- }
- if (http_set_keepalive(ps->hps, keepalive) != 0) {
- pkgerr_add(err, PKGERR_WEB, gettext(ERR_INIT_SESS), ps->url);
- ret = B_FALSE;
- goto cleanup;
- }
- if (http_set_socket_read_timeout(ps->hps, ps->timeout) != 0) {
- pkgerr_add(err, PKGERR_WEB, gettext(ERR_INIT_SESS), ps->url);
- ret = B_FALSE;
- goto cleanup;
- }
- if (http_set_random_file(ps->hps, RANDOM) != 0) {
- pkgerr_add(err, PKGERR_WEB, gettext(ERR_INIT_SESS), ps->url);
- ret = B_FALSE;
- goto cleanup;
- }
-
- (void) http_set_p12_format(B_TRUE);
-
-cleanup:
- return (ret);
-}
-
-/*
- * Name: web_connect
- * Description: Makes connection with URL stored in static 'ps' structure.
- *
- * Arguments: err - where to record any errors.
- *
- * Returns : WEB_OK - connection successful
- * WEB_VERIFY_SETUP - Unable to complete necessary
- * SSL setup
- * WEB_CONNREFUSED - Connection was refused to web site
- * WEB_HOSTDOWN - Host was not responding to request
- * WEB_NOCONNECT - Some other connection failure
- */
-static WebStatus
-web_connect(PKG_ERR *err)
-{
- STACK_OF(X509) *sec_cas = NULL;
- char *path;
- WebStatus ret = WEB_OK;
- ulong_t errcode;
- uint_t errsrc;
- int my_errno = 0;
- const char *libhttperr = NULL;
-
- if (ps->url.https == B_TRUE) {
- /* get CA certificates */
- if (find_ca_certs(err, ps->keystore, &sec_cas) != 0) {
- ret = WEB_VERIFY_SETUP;
- goto cleanup;
- }
-
- if (sk_X509_num(sec_cas) < 1) {
- /* no trusted websites */
- pkgerr_add(err, PKGERR_WEB,
- gettext(ERR_KEYSTORE_NOTRUST));
- ret = WEB_VERIFY_SETUP;
- goto cleanup;
- }
-
- /*
- * write out all CA certs to temp file. libwanboot should
- * have an interface for giving it a list of trusted certs
- * through an in-memory structure, but currently that does
- * not exist
- */
- if ((path = write_ca_file(err, ps->dwnld_dir, sec_cas,
- WEB_CA_PHRASE)) == NULL) {
- ret = WEB_VERIFY_SETUP;
- goto cleanup;
- }
-
- ps->certfile = path;
- if (http_set_password(ps->hps, WEB_CA_PHRASE) != 0) {
- pkgerr_add(err, PKGERR_WEB,
- gettext(ERR_HTTPS_PASSWD));
- ret = WEB_VERIFY_SETUP;
- goto cleanup;
- }
-
- if (http_set_certificate_authority_file(path) != 0) {
- pkgerr_add(err, PKGERR_WEB,
- gettext(ERR_HTTPS_CA));
- ret = WEB_VERIFY_SETUP;
- goto cleanup;
- }
- }
-
- if (http_srv_connect(ps->hps) != 0) {
- while ((errcode = http_get_lasterr(ps->hps, &errsrc)) != 0) {
- /* Have an error - is it EINTR? */
- if (errsrc == ERRSRC_SYSTEM) {
- my_errno = errcode;
- break;
- } else if (libhttperr == NULL) {
- /* save the first non-system error message */
- libhttperr = http_errorstr(errsrc, errcode);
- }
- }
- switch (my_errno) {
- case EINTR:
- case ETIMEDOUT:
- /* Timed out. Try, try again */
- ret = WEB_TIMEOUT;
- break;
- case ECONNREFUSED:
- ret = WEB_CONNREFUSED;
- break;
- case EHOSTDOWN:
- ret = WEB_HOSTDOWN;
- break;
- default:
- /* some other fatal error */
- ret = WEB_NOCONNECT;
- if (libhttperr == NULL) {
- pkgerr_add(err, PKGERR_WEB,
- gettext(ERR_INIT_CONN),
- ps->url.hport.hostname);
- } else {
- pkgerr_add(err, PKGERR_WEB,
- gettext(ERR_HTTP), libhttperr);
- }
- break;
- }
- }
-cleanup:
- return (ret);
-}
-
-/*
- * Name: write_ca_file
- * Description: Writes out a PKCS12 file containing all trusted certs
- * found in keystore recorded in static 'ps' structure
- *
- * This routine is used because the libwanboot library's
- * HTTPS routines cannot accept trusted certificates
- * through an in-memory structure, when initiating an
- * SSL connection. They must be in a PKCS12, which is
- * admittedly a poor interface.
- *
- * Arguments: err - where to record any errors.
- * tmpdir - Directory to write certificate file in
- * cacerts - Certs to write out
- * passwd - password used to encrypt certs
- *
- * Returns : path to resulting file, if successfullly written,
- * otherwise NULL.
- */
-static char
-*write_ca_file(PKG_ERR *err, char *tmpdir, STACK_OF(X509) *cacerts,
- char *passwd)
-{
- int fd, len;
- FILE *fp;
- PKCS12 *p12 = NULL;
- char *ret = NULL;
- static char tmp_file[PATH_MAX] = "";
- struct stat buf;
-
- if (!path_valid(tmpdir)) {
- pkgerr_add(err, PKGERR_WEB, gettext(MSG_NOTEMP), tmpdir);
- goto cleanup;
- }
-
- /* mkstemp replaces XXXXXX with a unique string */
- if (((len = snprintf(tmp_file, PATH_MAX, "%s/%sXXXXXX", tmpdir,
- "cert")) < 0) ||
- (len >= PATH_MAX)) {
- pkgerr_add(err, PKGERR_WEB, gettext(MSG_NOTEMP), tmpdir);
- goto cleanup;
- }
-
- if ((fd = mkstemp(tmp_file)) == -1) {
- pkgerr_add(err, PKGERR_WEB, gettext(MSG_NOTMPFIL), tmp_file);
- goto cleanup;
- }
-
- if (fstat(fd, &buf) == -1) {
- pkgerr_add(err, PKGERR_WEB, gettext(MSG_NOTMPFIL), tmp_file);
- goto cleanup;
- }
-
- if (!S_ISREG(buf.st_mode)) {
- pkgerr_add(err, PKGERR_WEB, gettext(MSG_NOTMPFIL), tmp_file);
- goto cleanup;
- }
-
- if ((fp = fdopen(fd, "w")) == NULL) {
- pkgerr_add(err, PKGERR_WEB, gettext(MSG_NOTMPFIL), tmp_file);
- goto cleanup;
- }
-
- if ((p12 = sunw_PKCS12_create(passwd, NULL, NULL, cacerts)) == NULL) {
- pkgerr_add(err, PKGERR_WEB,
- gettext(ERR_KEYSTORE_FORM), tmp_file);
- goto cleanup;
- }
-
- if (i2d_PKCS12_fp(fp, p12) == 0) {
- pkgerr_add(err, PKGERR_WEB,
- gettext(ERR_KEYSTORE_FORM), tmp_file);
- goto cleanup;
- }
-
- (void) fflush(fp);
- (void) fclose(fp);
- (void) close(fd);
- fp = NULL;
- fd = -1;
- ret = tmp_file;
-
-cleanup:
- if (p12 != NULL)
- PKCS12_free(p12);
- if (fp != NULL)
- (void) fclose(fp);
- if (fd != -1) {
- (void) close(fd);
- (void) unlink(tmp_file);
- }
-
- return (ret);
-}
-
-/*
- * Name: web_send_request
- * Description: Sends an HTTP request for a file to the
- * web server being communicated with in the static
- * 'ps' structure
- *
- * Arguments: err - where to record any errors.
- * request_type - HTTP_REQ_TYPE_HEAD to send an HTTP HEAD request,
- * or HTTP_REQ_TYPE_GET to send an HTTP GET request
- * cp -
- * Returns : WEB_OK - request sent successfully
- * WEB_CONNREFUSED - Connection was refused to web site
- * WEB_HOSTDOWN - Host was not responding to request
- * WEB_NOCONNECT - Some other connection failure
- */
-static WebStatus
-web_send_request(PKG_ERR *err, int request_type, int cp, int ep)
-{
- WebStatus ret = WEB_OK;
- ulong_t errcode;
- uint_t errsrc;
- int my_errno = 0;
- const char *libhttperr = NULL;
- switch (request_type) {
- case HTTP_REQ_TYPE_HEAD:
- if ((http_head_request(ps->hps, ps->url.abspath)) != 0) {
- while ((errcode = http_get_lasterr(ps->hps,
- &errsrc)) != 0) {
- /* Have an error - is it EINTR? */
- if (errsrc == ERRSRC_SYSTEM) {
- my_errno = errcode;
- break;
- } else if (libhttperr == NULL) {
- /* save first non-system error message */
- libhttperr =
- http_errorstr(errsrc, errcode);
- }
- }
- switch (my_errno) {
- case EINTR:
- case ETIMEDOUT:
- /* Timed out. Try, try again */
- ret = WEB_TIMEOUT;
- break;
- case ECONNREFUSED:
- ret = WEB_CONNREFUSED;
- break;
- case EHOSTDOWN:
- ret = WEB_HOSTDOWN;
- break;
- default:
- /* some other fatal error */
- ret = WEB_NOCONNECT;
- if (libhttperr == NULL) {
- pkgerr_add(err, PKGERR_WEB,
- gettext(ERR_INIT_CONN),
- ps->url.hport.hostname);
- } else {
- pkgerr_add(err, PKGERR_WEB,
- gettext(ERR_HTTP), libhttperr);
- }
- break;
- }
- goto cleanup;
- }
- break;
-
- case HTTP_REQ_TYPE_GET:
- if (cp && ep) {
- if (http_get_range_request(ps->hps, ps->url.abspath,
- cp, ep - cp) != 0) {
- while ((errcode = http_get_lasterr(ps->hps,
- &errsrc)) != 0) {
- /* Have an error - is it EINTR? */
- if (errsrc == ERRSRC_SYSTEM) {
- my_errno = errcode;
- break;
- } else {
- /*
- * save first non-system
- * error message
- */
- libhttperr =
- http_errorstr(errsrc,
- errcode);
- }
- }
- switch (my_errno) {
- case EINTR:
- case ETIMEDOUT:
- /* Timed out. Try, try again */
- ret = WEB_TIMEOUT;
- break;
- case ECONNREFUSED:
- ret = WEB_CONNREFUSED;
- break;
- case EHOSTDOWN:
- ret = WEB_HOSTDOWN;
- break;
- default:
- /* some other fatal error */
- ret = WEB_NOCONNECT;
- if (libhttperr == NULL) {
- pkgerr_add(err, PKGERR_WEB,
- gettext(ERR_INIT_CONN),
- ps->url.hport.hostname);
- } else {
- pkgerr_add(err, PKGERR_WEB,
- gettext(ERR_HTTP),
- libhttperr);
- }
- break;
- }
- goto cleanup;
- }
-
- if (!web_eval_headers(err)) {
- ret = WEB_NOCONNECT;
- goto cleanup;
- }
- } else {
- if ((http_get_request(ps->hps, ps->url.abspath))
- != 0) {
- while ((errcode = http_get_lasterr(ps->hps,
- &errsrc)) != 0) {
- /* Have an error - is it EINTR? */
- if (errsrc == ERRSRC_SYSTEM) {
- my_errno = errcode;
- break;
- } else {
- /*
- * save the first non-system
- * error message
- */
- libhttperr =
- http_errorstr(errsrc,
- errcode);
- }
- }
- switch (my_errno) {
- case EINTR:
- case ETIMEDOUT:
- /* Timed out. Try, try again */
- ret = WEB_TIMEOUT;
- break;
- case ECONNREFUSED:
- ret = WEB_CONNREFUSED;
- break;
- case EHOSTDOWN:
- ret = WEB_HOSTDOWN;
- break;
- default:
- /* some other fatal error */
- ret = WEB_NOCONNECT;
- if (libhttperr == NULL) {
- pkgerr_add(err, PKGERR_WEB,
- gettext(ERR_INIT_CONN),
- ps->url.hport.hostname);
- } else {
- pkgerr_add(err, PKGERR_WEB,
- gettext(ERR_HTTP),
- libhttperr);
- }
- break;
- }
- goto cleanup;
- }
-
- if (!web_eval_headers(err)) {
- ret = WEB_NOCONNECT;
- goto cleanup;
- }
- }
- break;
- default:
- pkgerr_add(err, PKGERR_INTERNAL, gettext(ERR_PKG_INTERNAL),
- __FILE__, __LINE__);
- }
-
-cleanup:
- return (ret);
-}
-
-/*
- * Name: web_eval_headers
- * Description: Evaluates HTTP headers returned during an HTTP request.
- * This must be called before calling
- * http_get_header_value().
- *
- * Arguments: err - where to record any errors.
- *
- * Returns : B_TRUE - success, B_FALSE otherwise
- */
-static boolean_t
-web_eval_headers(PKG_ERR *err)
-{
- const char *http_err;
- ulong_t herr;
- uint_t errsrc;
-
- if (http_process_headers(ps->hps, &ps->resp) != 0) {
- if ((ps->resp != NULL) && (ps->resp->statusmsg != NULL)) {
- pkgerr_add(err, PKGERR_WEB, gettext(ERR_HTTP),
- ps->resp->statusmsg);
- }
-
- herr = http_get_lasterr(ps->hps, &errsrc);
- http_err = http_errorstr(errsrc, herr);
- pkgerr_add(err, PKGERR_WEB, gettext(ERR_HTTP),
- http_err);
- return (B_FALSE);
- }
- return (B_TRUE);
-}
-
-/*
- * Name: web_get_file
- * Description: Downloads the file URL from the website, all of
- * which are recorded in the static 'ps' struct
- *
- * Arguments: err - where to record any errors.
- * dwnld_dir - Directory to download file into
- * device - Where to store path to resulting
- * file
- * nointeract - if non-zero, do not output
- * progress
- * fname - name of downloaded file link in the dwnld_dir
- *
- * Returns : WEB_OK - download successful
- * WEB_CONNREFUSED - Connection was refused to web site
- * WEB_HOSTDOWN - Host was not responding to request
- * WEB_GET_FAIL - Unable to initialize download
- * state (temp file creation, header parsing, etc)
- * WEB_NOCONNECT - Some other connection failure
- */
-static WebStatus
-web_get_file(PKG_ERR *err, char *dwnld_dir, int nointeract, char **fname)
-{
- int i, fd;
- int n = 0;
- ulong_t abs_pos = 0;
- char *head_val = NULL;
- char *lastmod_val = NULL;
- char *bname = NULL;
- struct stat status;
- WebStatus ret = WEB_OK;
- WebStatus req_ret;
- ulong_t errcode;
- uint_t errsrc;
- int my_errno = 0;
- const char *libhttperr = NULL;
- char *disp;
- char tmp_file[PATH_MAX];
- int len;
-
- ps->data.prev_cont_length =
- ps->data.content_length =
- ps->data.cur_pos = 0;
-
- if ((head_val = http_get_header_value(ps->hps,
- CONTENT_LENGTH_HDR)) != NULL) {
- ps->data.content_length = atol(head_val);
- } else {
- pkgerr_add(err, PKGERR_WEB, gettext(ERR_NO_HEAD_VAL),
- CONTENT_LENGTH_HDR);
- ret = WEB_GET_FAIL;
- goto cleanup;
- }
-
- free(head_val);
- head_val = NULL;
-
- if ((head_val = http_get_header_value(ps->hps,
- CONTENT_DISPOSITION_HDR)) != NULL) {
- /* "inline; parm=val; parm=val */
- if ((disp = strtok(head_val, "; \t\n\f\r")) != NULL) {
- /* disp = "inline" */
- while ((disp = strtok(NULL, "; \t\n\f\r")) != NULL) {
- /* disp = "parm=val" */
- if (ci_strneq(disp, "filename=", 9)) {
- bname = xstrdup(basename(disp + 9));
- trim(bname);
- dequote(bname);
- }
- }
- }
- free(head_val);
- head_val = NULL;
- }
-
- if (bname == NULL) {
- /*
- * couldn't determine filename from header value,
- * so take basename of URL
- */
- if ((bname = get_endof_string(ps->url.abspath, '/')) == NULL) {
- /* URL is bad */
- pkgerr_add(err, PKGERR_PARSE,
- gettext(ERR_PARSE_URL), ps->url.abspath);
- ret = WEB_GET_FAIL;
- goto cleanup;
- }
- }
-
- *fname = bname;
-
- if ((head_val = http_get_header_value(ps->hps, LAST_MODIFIED_HDR))
- != NULL) {
-
- if ((lastmod_val = condense_lastmodified(head_val)) == NULL) {
- pkgerr_add(err, PKGERR_WEB, gettext(ERR_BAD_HEAD_VAL),
- LAST_MODIFIED_HDR, head_val);
- ret = WEB_GET_FAIL;
- goto cleanup;
- }
- free(head_val);
- head_val = NULL;
-
- if ((ps->uniqfile = get_unique_filename(dwnld_dir,
- lastmod_val)) == NULL) {
- pkgerr_add(err, PKGERR_WEB, gettext(ERR_OPEN_TMP));
- ret = WEB_GET_FAIL;
- goto cleanup;
- }
-
- free(lastmod_val);
- lastmod_val = NULL;
-
- if ((fd = open(ps->uniqfile,
- O_NONBLOCK|O_RDWR|O_APPEND|O_CREAT|O_EXCL,
- 640)) == -1) {
-
- /*
- * A partial downloaded file
- * already exists, so open it.
- */
- if ((fd = open(ps->uniqfile,
- O_NONBLOCK|O_RDWR|O_APPEND)) != -1) {
- if (fstat(fd, &status) == -1 ||
- !S_ISREG(status.st_mode)) {
- pkgerr_add(err, PKGERR_WEB,
- gettext(ERR_DWNLD_NO_CONT),
- ps->uniqfile);
- ret = WEB_GET_FAIL;
- goto cleanup;
- } else {
- echo_out(nointeract,
- gettext(MSG_DWNLD_PART),
- ps->uniqfile,
- status.st_size);
- ps->data.prev_cont_length =
- status.st_size;
- }
- } else {
- /* unable to open partial file */
- pkgerr_add(err, PKGERR_WEB,
- gettext(ERR_DWNLD_NO_CONT),
- ps->uniqfile);
- ret = WEB_GET_FAIL;
- goto cleanup;
- }
- }
- } else {
- /*
- * no "Last-Modified" header, so this file is not eligible for
- * spooling and "resuming last download" operations
- */
- ps->spool = B_FALSE;
-
- /* mkstemp replaces XXXXXX with a unique string */
- if (((len = snprintf(tmp_file, PATH_MAX,
- "%s/%sXXXXXX", dwnld_dir, "stream")) < 0) ||
- (len >= PATH_MAX)) {
- pkgerr_add(err, PKGERR_WEB,
- gettext(MSG_NOTEMP), dwnld_dir);
- ret = WEB_GET_FAIL;
- goto cleanup;
- }
-
- if ((fd = mkstemp(tmp_file)) == -1) {
- pkgerr_add(err, PKGERR_WEB,
- gettext(MSG_NOTMPFIL), tmp_file);
- ret = WEB_GET_FAIL;
- goto cleanup;
- }
-
- if (fstat(fd, &status) == -1 ||
- !S_ISREG(status.st_mode)) {
- pkgerr_add(err, PKGERR_WEB,
- gettext(ERR_DWNLD_NO_CONT),
- ps->uniqfile);
- ret = WEB_GET_FAIL;
- goto cleanup;
- }
-
- ps->data.prev_cont_length = 0;
- ps->uniqfile = xstrdup(tmp_file);
- }
-
- /* File has already been completely downloaded */
- if (ps->data.prev_cont_length == ps->data.content_length) {
- echo_out(nointeract, gettext(MSG_DWNLD_PREV), ps->uniqfile);
- ps->data.cur_pos = ps->data.prev_cont_length;
- if (!make_link(dwnld_dir, bname)) {
- pkgerr_add(err, PKGERR_WEB, gettext(MSG_NOTEMP),
- dwnld_dir);
- ret = WEB_GET_FAIL;
- goto cleanup;
- }
- /* we're done, so cleanup and return success */
- goto cleanup;
- } else if (ps->data.prev_cont_length != 0) {
- ps->data.cur_pos = ps->data.prev_cont_length;
- }
-
- if (!ck_dwnld_dir_space(err, dwnld_dir,
- (ps->data.prev_cont_length != 0) ?
- (ps->data.content_length - ps->data.cur_pos) :
- ps->data.content_length)) {
- ret = WEB_GET_FAIL;
- goto cleanup;
- }
-
- if ((req_ret = web_send_request(err, HTTP_REQ_TYPE_GET,
- ps->data.cur_pos, ps->data.content_length)) != WEB_OK) {
- ret = req_ret;
- goto cleanup;
- }
-
- if (ps->data.prev_cont_length != 0)
- echo_out(nointeract, gettext(MSG_DWNLD_CONT));
- else
- echo_out(nointeract, gettext(MSG_DWNLD));
-
- progress_setup(nointeract, ps->data.content_length);
-
- /* Download the file a BLOCK at a time */
- while (ps->data.cur_pos < ps->data.content_length) {
- progress_report(nointeract, abs_pos);
- i = ((ps->data.content_length - ps->data.cur_pos) < BLOCK) ?
- (ps->data.content_length - ps->data.cur_pos)
- : BLOCK;
- if ((n = http_read_body(ps->hps, ps->content, i)) <= 0) {
- while ((errcode = http_get_lasterr(ps->hps,
- &errsrc)) != 0) {
- /* Have an error - is it EINTR? */
- if (errsrc == ERRSRC_SYSTEM) {
- my_errno = errcode;
- break;
- } else {
- /*
- * save first non-system
- * error message
- */
- libhttperr =
- http_errorstr(errsrc, errcode);
- }
- }
- switch (my_errno) {
- case EINTR:
- case ETIMEDOUT:
- /* Timed out. Try, try again */
- ret = WEB_TIMEOUT;
- break;
- case ECONNREFUSED:
- ret = WEB_CONNREFUSED;
- break;
- case EHOSTDOWN:
- ret = WEB_HOSTDOWN;
- break;
- default:
- /* some other fatal error */
- ret = WEB_NOCONNECT;
- if (libhttperr == NULL) {
- pkgerr_add(err, PKGERR_WEB,
- gettext(ERR_INIT_CONN),
- ps->url.hport.hostname);
- } else {
- pkgerr_add(err, PKGERR_WEB,
- gettext(ERR_HTTP), libhttperr);
- }
- break;
- }
- goto cleanup;
- }
- if ((n = write(fd, ps->content, n)) == 0) {
- pkgerr_add(err, PKGERR_WEB, gettext(ERR_WRITE),
- ps->uniqfile, strerror(errno));
- ret = WEB_GET_FAIL;
- goto cleanup;
- }
- ps->data.cur_pos += n;
- abs_pos += n;
- }
-
- progress_finish(nointeract);
- echo_out(nointeract, gettext(MSG_DWNLD_COMPLETE));
-
- if (!make_link(dwnld_dir, bname)) {
- pkgerr_add(err, PKGERR_WEB, gettext(MSG_NOTEMP),
- dwnld_dir);
- ret = WEB_GET_FAIL;
- goto cleanup;
- }
-
-cleanup:
- sync();
- if (fd != -1) {
- (void) close(fd);
- }
-
- if (head_val != NULL)
- free(head_val);
-
- if (lastmod_val != NULL)
- free(lastmod_val);
-
- return (ret);
-}
-
-/*
- * Name: make_link
- * Description: Create new link to file being downloaded
- *
- * Arguments: dwnld_dir - directory in which downloaded file exists
- * bname - name of link
- *
- * Returns : B_TRUE - success, B_FALSE otherwise
- */
-static boolean_t
-make_link(char *dwnld_dir, char *bname)
-{
- int len;
-
- if ((ps->link = (char *)xmalloc(PATH_MAX)) == NULL)
- return (B_FALSE);
- if (((len = snprintf(ps->link, PATH_MAX, "%s/%s",
- dwnld_dir, bname)) < 0) ||
- len >= PATH_MAX)
- return (B_FALSE);
-
- (void) link(ps->uniqfile, ps->link);
-
- return (B_TRUE);
-}
-
-/*
- * Name: get_startof_string
- * Description: searches string for token, returns a newly-allocated
- * substring of the given string up to, but not
- * including, token. for example
- * get_startof_string("abcd", 'c') will return "ab"
- *
- * Arguments: path - path to split
- * token - character to split on
- *
- * Returns : substring of 'path', up to, but not including,
- * token, if token appears in path. Otherwise,
- * returns NULL.
- */
-char *
-get_startof_string(char *path, char token)
-{
- char *p, *p2;
-
- if (path == NULL)
- return (NULL);
-
- p = xstrdup(path);
-
- p2 = strchr(p, token);
- if (p2 == NULL) {
- free(p);
- return (NULL);
- } else {
- *p2 = '\0';
- return (p);
- }
-}
-
-/*
- * Name: get_endof_string
- * Description: searches string for token, returns a
- * newly-allocated substring of the given string,
- * starting at character following token, to end of
- * string.
- *
- * for example get_end_string("abcd", 'c')
- * will return "d"
- *
- * Arguments: path - path to split
- * token - character to split on
- *
- * Returns : substring of 'path', beginning at character
- * following token, to end of string, if
- * token appears in path. Otherwise,
- * returns NULL.
- */
-char *
-get_endof_string(char *path, char token)
-{
- char *p, *p2;
-
- if (path == NULL)
- return (NULL);
-
- p = xstrdup(path);
-
- if ((p2 = strrchr(p, token)) == NULL) {
- return (NULL);
- }
-
- return (p2 + 1);
-}
-
-/*
- * Name: progress_setup
- * Description: Initialize session for reporting progress
- *
- * Arguments: nointeract - if non-zero, do not do anything
- * ulong_t - size of job to report progress for
- *
- * Returns : none
- */
-static void
-progress_setup(int nointeract, ulong_t size_of_load)
-{
- ulong_t divisor;
- ulong_t term_width = TERM_WIDTH;
-
- if (nointeract)
- return;
-
- if (size_of_load > MED_DWNLD && size_of_load < LARGE_DWNLD)
- divisor = MED_DIVISOR;
- else if (size_of_load > LARGE_DWNLD) {
- term_width = TERM_WIDTH - 8;
- divisor = LARGE_DIVISOR;
- } else
- divisor = SMALL_DIVISOR;
-
- const_increment = size_of_load / term_width;
- const_divider = size_of_load / divisor;
- const_completed = 100 / divisor;
-}
-
-/*
- * Name: progress_report
- * Description: Report progress for current progress context,
- * to stderr
- *
- * Arguments: nointeract - if non-zero, do not do anything
- * position - how far along in the job to report.
- * This should be <= size used during progress_setup
- *
- * Returns : none
- */
-static void
-progress_report(int nointeract, ulong_t position)
-{
- static ulong_t increment;
- static ulong_t divider;
-
- if (nointeract)
- return;
-
- if (position == 0) {
- increment = const_increment;
- divider = const_divider;
- }
- if (position > increment && position < divider) {
- (void) putc('.', stderr);
- increment += const_increment;
- } else if (position > divider) {
- completed += const_completed;
- (void) fprintf(stderr, "%ld%c", completed, '%');
- increment += const_increment;
- divider += const_divider;
- }
-}
-
-/*
- * Name: progress_finish
- * Description: Finalize session for reporting progress.
- * "100%" is reported to screen
- *
- * Arguments: nointeract - if non-zero, do not do anything
- *
- * Returns : none
- */
-static void
-progress_finish(int nointeract)
-{
- if (nointeract)
- return;
-
- (void) fprintf(stderr, "%d%c\n", 100, '%');
-}
-
-/*
- * Name: init_session
- * Description: Initializes static 'ps' structure with default
- * values
- *
- * Arguments: none
- *
- * Returns : B_TRUE - success, B_FALSE otherwise
- */
-static boolean_t
-init_session(void)
-{
- if ((ps = (WEB_SESSION *)
- xmalloc(sizeof (WEB_SESSION))) == NULL) {
- return (B_FALSE);
- }
- (void) memset(ps, 0, sizeof (*ps));
-
- if ((ps->content = (char *)xmalloc(BLOCK)) == NULL) {
- return (B_FALSE);
- }
-
- (void) memset(ps->content, 0, BLOCK);
-
- ps->data.cur_pos = 0UL;
- ps->data.content_length = 0UL;
- ps->url.https = B_FALSE;
- ps->uniqfile = NULL;
- ps->link = NULL;
- ps->dwnld_dir = NULL;
- ps->spool = B_TRUE;
- ps->errstr = NULL;
- ps->keystore = NULL;
-
- return (B_TRUE);
-}
-
-/*
- * Name: ck_downld_dir_space
- * Description: Verify enough space exists in directory to hold file
- *
- * Arguments: err - where to record any errors.
- * dwnld_dir - Directory to check available space in
- * bytes_needed - How many bytes are need
- *
- * Returns : B_TRUE - enough space exists in dwnld_dir to hold
- * bytes_needed bytes, otherwise B_FALSE
- */
-static boolean_t
-ck_dwnld_dir_space(PKG_ERR *err, char *dwnld_dir, ulong_t bytes_needed)
-{
- u_longlong_t bytes_avail;
- u_longlong_t block_pad;
- struct statvfs64 status;
-
- if (statvfs64(dwnld_dir, &status)) {
- pkgerr_add(err, PKGERR_WEB, gettext(ERR_TMPDIR), dwnld_dir);
- return (B_FALSE);
- }
-
- block_pad = (status.f_frsize ? status.f_frsize : status.f_bsize);
- bytes_avail = status.f_bavail * block_pad;
-
- if ((((u_longlong_t)bytes_needed) + block_pad) > bytes_avail) {
- pkgerr_add(err, PKGERR_WEB, gettext(ERR_DISK_SPACE),
- dwnld_dir,
- (((u_longlong_t)bytes_needed) + block_pad) / 1024ULL,
- bytes_avail / 1024ULL);
- return (B_FALSE);
- }
-
- return (B_TRUE);
-}
-
-/*
- * Description:
- * This function returns a unique file name based on the parts of the
- * URI. This is done to enable partially downloaded files to be resumed.
- * Arguments:
- * dir - The directory that should contain the filename.
- * last_modified - A string representing the date of last modification,
- * used as part of generating unique name
- * Returns:
- * A valid filename or NULL.
- */
-
-static char *
-get_unique_filename(char *dir, char *last_modified)
-{
- char *buf, *buf2, *beg_str;
- int len;
-
- if ((buf = (char *)xmalloc(PATH_MAX)) == NULL) {
- return (NULL);
- }
- if ((buf2 = (char *)xmalloc(PATH_MAX)) == NULL) {
- return (NULL);
- }
-
- /* prepare strings for being cat'ed onto */
- buf[0] = buf2[0] = '\0';
- /*
- * No validation of the path is done here. We just construct the path
- * and it must be validated later
- */
-
- if (dir) {
- if (((len = snprintf(buf2, PATH_MAX, "%s/", dir)) < 0) ||
- (len >= PATH_MAX))
- return (NULL);
- } else {
- return (NULL);
- }
-
- if (ps->url.abspath)
- if (strlcat(buf, ps->url.abspath, PATH_MAX) >= PATH_MAX)
- return (NULL);
- if (ps->url.hport.hostname)
- if (isdigit((int)ps->url.hport.hostname[0])) {
- if (strlcat(buf, ps->url.hport.hostname, PATH_MAX)
- >= PATH_MAX)
- return (NULL);
- } else {
- if ((beg_str =
- get_startof_string(ps->url.hport.hostname, '.'))
- != NULL)
- if (strlcat(buf, beg_str, PATH_MAX) >= PATH_MAX)
- return (NULL);
- }
- if (last_modified != NULL)
- if (strlcat(buf, last_modified, PATH_MAX) >= PATH_MAX)
- return (NULL);
-
- if ((buf = replace_token(buf, '/', '_')) != NULL) {
- if (strlcat(buf2, buf, PATH_MAX) >= PATH_MAX) {
- return (NULL);
- } else {
- if (buf) free(buf);
- return (buf2);
- }
- } else {
- if (buf) free(buf);
- if (buf2) free(buf2);
- return (NULL);
- }
-}
-
-/*
- * Description:
- * Removes token(s) consisting of one character from any path.
- * Arguments:
- * path - The path to search for the token in.
- * token - The token to search for
- * Returns:
- * The path with all tokens removed or NULL.
- */
-static char *
-replace_token(char *path, char oldtoken, char newtoken)
-{
- char *newpath, *p;
-
- if ((path == NULL) || (oldtoken == '\0') || (newtoken == '\0')) {
- return (NULL);
- }
-
- newpath = xstrdup(path);
-
- for (p = newpath; *p != '\0'; p++) {
- if (*p == oldtoken) {
- *p = newtoken;
- }
- }
-
- return (newpath);
-}
-
-/*
- * Name: trim
- * Description: Trims whitespace from a string
- * has been registered)
- * Scope: private
- * Arguments: string - string to trim. It is assumed
- * this string is writable up to it's entire
- * length.
- * Returns: none
- */
-static void
-trim(char *str)
-{
- int len, i;
- if (str == NULL) {
- return;
- }
-
- len = strlen(str);
- /* strip from front */
- while (isspace(*str)) {
- for (i = 0; i < len; i++) {
- str[i] = str[i+1];
- }
- }
-
- /* strip from back */
- len = strlen(str);
- while (isspace(str[len-1])) {
- len--;
- }
- str[len] = '\0';
-}
-
-/*
- * Description:
- * Resolves double quotes
- * Arguments:
- * str - The string to resolve
- * Returns:
- * None
- */
-static void
-dequote(char *str)
-{
- char *cp;
-
- if ((str == NULL) || (str[0] != '"')) {
- /* no quotes */
- return;
- }
-
- /* remove first quote */
- (void) memmove(str, str + 1, strlen(str) - 1);
-
- /*
- * scan string looking for ending quote.
- * escaped quotes like \" don't count
- */
- cp = str;
-
- while (*cp != '\0') {
- switch (*cp) {
- case '\\':
- /* found an escaped character */
- /* make sure end of string is not '\' */
- if (*++cp != '\0') {
- cp++;
- }
- break;
-
- case '"':
- *cp = '\0';
- break;
- default:
- cp++;
- }
- }
-}
-
-/*
- * Name: get_ENV_proxy
- * Description: Retrieves setting of proxy env variable
- *
- * Arguments: err - where to record any errors.
- * proxy - where to store proxy
- *
- * Returns : B_TRUE - http proxy was found and valid, stored in proxy
- * B_FALSE - error, errors recorded in err
- */
-static boolean_t
-get_ENV_proxy(PKG_ERR *err, char **proxy)
-{
- char *buf;
-
- if ((buf = getenv("HTTPPROXY")) != NULL) {
- if (!path_valid(buf)) {
- pkgerr_add(err, PKGERR_WEB,
- gettext(ERR_ILL_ENV), "HTTPPROXY", buf);
- return (B_FALSE);
- } else {
- *proxy = buf;
- return (B_TRUE);
- }
- } else {
- /* try the other env variable */
- if ((buf = getenv("http_proxy")) != NULL) {
- if (!path_valid(buf)) {
- pkgerr_add(err, PKGERR_WEB,
- gettext(ERR_ILL_ENV), "http_proxy", buf);
- return (B_FALSE);
- }
- if (!strneq(buf, "http://", 7)) {
- pkgerr_add(err, PKGERR_WEB,
- gettext(ERR_ILL_ENV), "http_proxy", buf);
- return (B_FALSE);
- }
-
- /* skip over the http:// part of the proxy "url" */
- *proxy = buf + 7;
- return (B_TRUE);
- }
- }
-
- /* either the env variable(s) were set and valid, or not set */
- return (B_TRUE);
-}
-
-/*
- * Name: get_ENV_proxyport
- * Description: Retrieves setting of PROXYPORT env variable
- *
- * Arguments: err - where to record any errors.
- * port - where to store resulting port
- *
- * Returns : B_TRUE - string found in PROXYPORT variable, converted
- * to decimal integer, if it exists
- * and is valid. Or, PROXYPORT not set, port set to 1.
- * B_FALSE - env variable set, but invalid
- * (not a number for example)
- */
-static boolean_t
-get_ENV_proxyport(PKG_ERR *err, ushort_t *port)
-{
- char *buf;
- ushort_t newport;
- buf = getenv("HTTPPROXYPORT");
- if (buf != NULL) {
- if (!path_valid(buf)) {
- pkgerr_add(err, PKGERR_WEB,
- gettext(ERR_ILL_ENV), "HTTPPROXYPORT", buf);
- return (B_FALSE);
- }
- if ((newport = atoi(buf)) == 0) {
- pkgerr_add(err, PKGERR_WEB,
- gettext(ERR_ILL_ENV), "HTTPPROXYPORT", buf);
- return (B_FALSE);
- }
- *port = newport;
- return (B_TRUE);
- } else {
- *port = 1;
- return (B_TRUE);
- }
-}
-
-/*
- * Name: remove_dwnld_file
- * Description: Removes newly-downloaded file if completely downloaded.
- *
- * Arguments: path - path to file to remove
- *
- * Returns : B_TRUE - success, B_FALSE otherwise
- * if it's '0' (not OK) we simply return it, since the
- * verification operation has already determined that the
- * cert is invalid. if 'ok' is non-zero, then we do our
- * checks, and return 0 or 1 based on if the cert is
- * invalid or valid.
- */
-static boolean_t
-remove_dwnld_file(char *path)
-{
- if (path && path != NULL) {
- /*
- * Only remove the downloaded file if it has been completely
- * downloaded, or is not eligible for spooling
- */
- if ((!ps->spool) ||
- (ps->data.cur_pos >= ps->data.content_length)) {
- (void) unlink(path);
- }
- } else {
- return (B_FALSE);
- }
- return (B_TRUE);
-}
-
-/*
- * Name: condense_lastmodifided
- * Description: generates a substring of a last-modified string,
- * and removes colons.
- *
- * Arguments: last_modified - string of the form
- * "Wed, 23 Oct 2002 21:59:45 GMT"
- *
- * Returns :
- * new string, consisting of hours/minutes/seconds only,
- * sans any colons.
- */
-char *
-condense_lastmodified(char *last_modified)
-{
- char *p, *p2;
-
- /*
- * Last-Modified: Wed, 23 Oct 2002 21:59:45 GMT
- * Strip the hours, minutes and seconds, without the ':'s, from
- * the above string, void of the ':".
- */
-
- if (last_modified == NULL)
- return (NULL);
-
- if ((p = xstrdup(last_modified)) == NULL)
- return (NULL);
- p2 = (strstr(p, ":") - 2);
- p2[8] = '\0';
- return (replace_token(p2, ':', '_'));
-}
-
-/*
- * Name: backoff
- * Description: sleeps for a certain # of seconds after a network
- * failure.
- * Scope: public
- * Arguments: none
- * Returns: none
- */
-void
-backoff()
-{
- static boolean_t initted = B_FALSE;
- int backoff;
- long seed;
-
- if (!initted) {
- /* seed the rng */
- (void) _get_random_info(&seed, sizeof (seed));
- srand48(seed);
- initted = B_TRUE;
- }
-
- backoff = (int)(drand48() * (double)cur_backoff);
- (void) sleep(backoff);
- if (cur_backoff < MAX_BACKOFF) {
- /*
- * increase maximum time we might wait
- * next time so as to fall off over
- * time.
- */
- cur_backoff *= BACKOFF_FACTOR;
- }
-}
-
-/*
- * Name: reset_backoff
- * Description: notifies the backoff service that whatever was
- * being backoff succeeded.
- * Scope: public
- * Arguments: none
- * Returns: none
- */
-void
-reset_backoff()
-{
- cur_backoff = MIN_BACKOFF;
-}
-
-/*
- * Name: _get_random_info
- * Description: generate an amount of random bits. Currently
- * only a small amount (a long long) can be
- * generated at one time.
- * Scope: private
- * Arguments: buf - [RO, *RW] (char *)
- * Buffer to copy bits into
- * size - amount to copy
- * Returns: B_TRUE on success, B_FALSE otherwise. The buffer is filled
- * with the amount of bytes of random data specified.
- */
-static boolean_t
-_get_random_info(void *buf, int size)
-{
- struct timeval tv;
- typedef struct {
- long low_time;
- long hostid;
- } randomness;
- randomness r;
-
- /* if the RANDOM file exists, use it */
- if (access(RANDOM, R_OK) == 0) {
- if ((RAND_load_file(RANDOM, 1024 * 1024)) > 0) {
- if (RAND_bytes((uchar_t *)buf, size) == 1) {
- /* success */
- return (B_TRUE);
- }
- }
- }
-
- /* couldn't use RANDOM file, so fallback to time of day and hostid */
- (void) gettimeofday(&tv, (struct timezone *)0);
-
- /* Wouldn't it be nice if we could hash these */
- r.low_time = tv.tv_usec;
- r.hostid = gethostid();
-
- if (sizeof (r) < size) {
- /*
- * Can't copy correctly
- */
- return (B_FALSE);
- }
- (void) memcpy(buf, &r, size);
- return (B_TRUE);
-}
-
-/*
- * Name: pkg_passphrase_cb
- * Description: Default callback that applications can use when
- * a passphrase is needed. This routine collects
- * a passphrase from the user using the given
- * passphrase retrieval method set with
- * set_passphrase_passarg(). If the method
- * indicates an interactive prompt, then the
- * prompt set with set_passphrase_prompt()
- * is displayed.
- *
- * Arguments: buf - Buffer to copy passphrase into
- * size - Max amount to copy to buf
- * rw - Whether this passphrase is needed
- * to read something off disk, or
- * write something to disk. Applications
- * typically want to ask twice when getting
- * a passphrase for writing something.
- * data - application-specific data. In this
- * callback, data is a pointer to
- * a keystore_passphrase_data structure.
- *
- * Returns: Length of passphrase collected, or -1 on error.
- * Errors recorded in 'err' object in the *data.
- */
-int
-pkg_passphrase_cb(char *buf, int size, int rw, void *data)
-{
- BIO *pwdbio = NULL;
- char passphrase_copy[MAX_PHRASELEN + 1];
- PKG_ERR *err;
- int passlen;
- char *ws;
- char prompt_copy[MAX_VERIFY_MSGLEN];
- char *passphrase;
- char *arg;
-
- err = ((keystore_passphrase_data *)data)->err;
-
- if (passarg == NULL) {
- arg = "console";
- } else {
- arg = passarg;
- }
-
- /* default method of collecting password is by prompting */
- if (ci_streq(arg, "console")) {
- if ((passphrase = getpassphrase(prompt)) == NULL) {
- pkgerr_add(err, PKGERR_BADPASS,
- gettext(MSG_NOPASS), arg);
- return (-1);
- }
-
- if (rw) {
- /*
- * if the password is being supplied for
- * writing something to disk, verify it first
- */
-
- /* make a copy (getpassphrase overwrites) */
- (void) strlcpy(passphrase_copy, passphrase,
- MAX_PHRASELEN + 1);
-
- if (((passlen = snprintf(prompt_copy,
- MAX_VERIFY_MSGLEN, "%s: %s",
- gettext(MSG_PASSWD_AGAIN),
- prompt)) < 0) ||
- (passlen >= (MAX_PHRASELEN + 1))) {
- pkgerr_add(err, PKGERR_BADPASS,
- gettext(MSG_NOPASS), arg);
- return (-1);
- }
-
- if ((passphrase =
- getpassphrase(prompt_copy)) == NULL) {
- pkgerr_add(err, PKGERR_BADPASS,
- gettext(MSG_NOPASS), arg);
- return (-1);
- }
-
- if (!streq(passphrase_copy, passphrase)) {
- pkgerr_add(err, PKGERR_READ,
- gettext(MSG_PASSWD_NOMATCH));
- return (-1);
- }
- }
- } else if (ci_strneq(arg, "pass:", 5)) {
- passphrase = arg + 5;
- } else if (ci_strneq(arg, "env:", 4)) {
- passphrase = getenv(arg + 4);
- } else if (ci_strneq(arg, "file:", 5)) {
-
- /* open file for reading */
- if ((pwdbio = BIO_new_file(arg + 5, "r")) == NULL) {
- pkgerr_add(err, PKGERR_EXIST,
- gettext(MSG_PASSWD_FILE), arg + 5);
- return (-1);
- }
-
- /* read first line */
- if (((passlen = BIO_gets(pwdbio, buf, size)) < 1) ||
- (passlen > size)) {
- pkgerr_add(err, PKGERR_READ, gettext(MSG_PASSWD_FILE),
- arg + 5);
- return (-1);
- }
- BIO_free_all(pwdbio);
- pwdbio = NULL;
-
- if (passlen == size) {
- /*
- * password was maximum length, so there is
- * no null terminator. null-terminate it
- */
- buf[size - 1] = '\0';
- }
-
- /* first newline found is end of passwd, so nuke it */
- if ((ws = strchr(buf, '\n')) != NULL) {
- *ws = '\0';
- }
- return (strlen(buf));
- } else {
- /* unrecognized passphrase */
- pkgerr_add(err, PKGERR_BADPASS,
- gettext(MSG_BADPASSARG), arg);
- return (-1);
- }
-
- if (passphrase == NULL) {
- /* unable to collect passwd from given source */
- pkgerr_add(err, PKGERR_BADPASS,
- gettext(MSG_NOPASS), arg);
- return (-1);
- }
-
- (void) strlcpy(buf, passphrase, size);
- return (strlen(buf));
-}