Home | History | Annotate | Line # | Download | only in libarchive
      1 /*-
      2  * Copyright (c) 2014 Michihiro NAKAJIMA
      3  * All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions
      7  * are met:
      8  * 1. Redistributions of source code must retain the above copyright
      9  *    notice, this list of conditions and the following disclaimer.
     10  * 2. Redistributions in binary form must reproduce the above copyright
     11  *    notice, this list of conditions and the following disclaimer in the
     12  *    documentation and/or other materials provided with the distribution.
     13  *
     14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
     15  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     16  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     17  * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
     18  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     19  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     20  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     21  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     23  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     24  */
     25 
     26 #include "archive_platform.h"
     27 
     28 #ifdef HAVE_ERRNO_H
     29 #include <errno.h>
     30 #endif
     31 #include "archive_read_private.h"
     32 
     33 static void
     34 add_passphrase_to_tail(struct archive_read *a,
     35     struct archive_read_passphrase *p)
     36 {
     37 	*a->passphrases.last = p;
     38 	a->passphrases.last = &p->next;
     39 	p->next = NULL;
     40 }
     41 
     42 static struct archive_read_passphrase *
     43 remove_passphrases_from_head(struct archive_read *a)
     44 {
     45 	struct archive_read_passphrase *p;
     46 
     47 	p = a->passphrases.first;
     48 	if (p != NULL)
     49 		a->passphrases.first = p->next;
     50 	return (p);
     51 }
     52 
     53 static void
     54 insert_passphrase_to_head(struct archive_read *a,
     55     struct archive_read_passphrase *p)
     56 {
     57 	p->next = a->passphrases.first;
     58 	a->passphrases.first = p;
     59 	if (&a->passphrases.first == a->passphrases.last) {
     60 		a->passphrases.last = &p->next;
     61 		p->next = NULL;
     62 	}
     63 }
     64 
     65 static struct archive_read_passphrase *
     66 new_read_passphrase(struct archive_read *a, const char *passphrase)
     67 {
     68 	struct archive_read_passphrase *p;
     69 
     70 	p = malloc(sizeof(*p));
     71 	if (p == NULL) {
     72 		archive_set_error(&a->archive, ENOMEM,
     73 		    "Can't allocate memory");
     74 		return (NULL);
     75 	}
     76 	p->passphrase = strdup(passphrase);
     77 	if (p->passphrase == NULL) {
     78 		free(p);
     79 		archive_set_error(&a->archive, ENOMEM,
     80 		    "Can't allocate memory");
     81 		return (NULL);
     82 	}
     83 	return (p);
     84 }
     85 
     86 int
     87 archive_read_add_passphrase(struct archive *_a, const char *passphrase)
     88 {
     89 	struct archive_read *a = (struct archive_read *)_a;
     90 	struct archive_read_passphrase *p;
     91 
     92 	archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,
     93 		"archive_read_add_passphrase");
     94 
     95 	if (passphrase == NULL || passphrase[0] == '\0') {
     96 		archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
     97 		    "Empty passphrase is unacceptable");
     98 		return (ARCHIVE_FAILED);
     99 	}
    100 
    101 	p = new_read_passphrase(a, passphrase);
    102 	if (p == NULL)
    103 		return (ARCHIVE_FATAL);
    104 	add_passphrase_to_tail(a, p);
    105 
    106 	return (ARCHIVE_OK);
    107 }
    108 
    109 int
    110 archive_read_set_passphrase_callback(struct archive *_a, void *client_data,
    111     archive_passphrase_callback *cb)
    112 {
    113 	struct archive_read *a = (struct archive_read *)_a;
    114 
    115 	archive_check_magic(_a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_NEW,
    116 		"archive_read_set_passphrase_callback");
    117 
    118 	a->passphrases.callback = cb;
    119 	a->passphrases.client_data = client_data;
    120 	return (ARCHIVE_OK);
    121 }
    122 
    123 /*
    124  * Call this in advance when you start to get a passphrase for decryption
    125  * for a entry.
    126  */
    127 void
    128 __archive_read_reset_passphrase(struct archive_read *a)
    129 {
    130 
    131 	a->passphrases.candidate = -1;
    132 }
    133 
    134 /*
    135  * Get a passphrase for decryption.
    136  */
    137 const char *
    138 __archive_read_next_passphrase(struct archive_read *a)
    139 {
    140 	struct archive_read_passphrase *p;
    141 	const char *passphrase;
    142 
    143 	if (a->passphrases.candidate < 0) {
    144 		/* Count out how many passphrases we have. */
    145 		int cnt = 0;
    146 
    147 		for (p = a->passphrases.first; p != NULL; p = p->next)
    148 			cnt++;
    149 		a->passphrases.candidate = cnt;
    150 		p = a->passphrases.first;
    151 	} else if (a->passphrases.candidate > 1) {
    152 		/* Rotate a passphrase list. */
    153 		a->passphrases.candidate--;
    154 		p = remove_passphrases_from_head(a);
    155 		add_passphrase_to_tail(a, p);
    156 		/* Pick a new passphrase candidate up. */
    157 		p = a->passphrases.first;
    158 	} else if (a->passphrases.candidate == 1) {
    159 		/* This case is that all candidates failed to decrypt. */
    160 		a->passphrases.candidate = 0;
    161 		if (a->passphrases.first->next != NULL) {
    162 			/* Rotate a passphrase list. */
    163 			p = remove_passphrases_from_head(a);
    164 			add_passphrase_to_tail(a, p);
    165 		}
    166 		p = NULL;
    167 	} else  /* There is no passphrase candidate. */
    168 		p = NULL;
    169 
    170 	if (p != NULL)
    171 		passphrase = p->passphrase;
    172 	else if (a->passphrases.callback != NULL) {
    173 		/* Get a passphrase through a call-back function
    174 		 * since we tried all passphrases out or we don't
    175 		 * have it. */
    176 		passphrase = a->passphrases.callback(&a->archive,
    177 		    a->passphrases.client_data);
    178 		if (passphrase != NULL) {
    179 			p = new_read_passphrase(a, passphrase);
    180 			if (p == NULL)
    181 				return (NULL);
    182 			insert_passphrase_to_head(a, p);
    183 			a->passphrases.candidate = 1;
    184 		}
    185 	} else
    186 		passphrase = NULL;
    187 
    188 	return (passphrase);
    189 }
    190