pass

simple password manager
git clone git://git.2f30.org/pass.git
Log | Files | Refs | LICENSE

pass.c (7568B)


      1 /* See LICENSE file for copyright and license details. */		
      2 #include <errno.h>
      3 #include <fcntl.h>
      4 #include <gpgme.h>
      5 #include <getopt.h>
      6 #include <limits.h>
      7 #include <libgen.h>
      8 #include <locale.h>
      9 #include <pwd.h>
     10 #include <stdio.h>
     11 #include <string.h>
     12 #include <stdlib.h>
     13 #include <sys/stat.h>
     14 #include <unistd.h>
     15 
     16 #include "pass.h"
     17 #include "readpassphrase.h"
     18 
     19 #define MAX_BUFSIZ 512
     20 static char file[PATH_MAX];
     21 const char *home;
     22 
     23 void
     24 initgpgme(void)
     25 {
     26 	const char *reqlibver = "1.6.0";
     27 	const char *reqgpgver = "2.1.0";
     28 
     29 	setlocale(LC_ALL, "en_US.UTF-8");
     30 	gpgme_set_locale(NULL, LC_ALL, "en_US.UTF-8");
     31 	gpgme_set_global_flag("require-gnupg", reqgpgver);
     32 	gpgme_check_version(reqlibver);
     33 }
     34 
     35 void
     36 gethomedir(void)
     37 {
     38 	if (!(home = getenv("HOME")))
     39 		fatalx("$HOME not set, cannot determine password-store location");
     40 }
     41 
     42 
     43 void
     44 delete(char *item)
     45 { 
     46 	char *l;
     47 	int r;
     48 	
     49 	gethomedir();
     50 	snprintf(file, sizeof(file), "%s/.password-store/%s.gpg", home, item);
     51 	printf("Are you sure you would like to delete %s? [y/N] ", item);
     52 	if ((r = getchar()) == 'N' || r == 'n')
     53 		exit(0);
     54 	else if ((r == 'y' || r == 'Y') && !remove(file)) {
     55 		printf("removed '%s'\n", file);
     56 		if ((l = strrchr(file, '/')))
     57 			*l = '\0';
     58 		rmdir(file);
     59 	}
     60 }
     61 
     62 void
     63 usage(void)
     64 {
     65 	fprintf(stderr,
     66 		"usage:\tpass init gpg-id\n"
     67 		"\tpass insert pass-name\n"
     68 		"\tpass pass-name\n"
     69 		"\tpass rm pass-name\n"
     70 	       );
     71 	exit(1);
     72 }
     73 
     74 void
     75 decrypt(char *buf)
     76 {
     77 	gpgme_data_t in, out;
     78 	gpgme_error_t gpgerr;
     79 	gpgme_ctx_t ctx;
     80 	gpgme_protocol_t proto; 
     81 	int a, ret;
     82 
     83 	initgpgme(); 
     84 	proto = GPGME_PROTOCOL_OpenPGP;
     85 
     86 	gpgerr = gpgme_new(&ctx);
     87 	if (gpgme_err_code(gpgerr) != GPG_ERR_NO_ERROR)
     88 		fatalgpg(gpgerr, "Error: gpgme_new: %s");
     89 	gpgerr = gpgme_set_protocol(ctx, proto); 
     90 	if (gpgme_err_code(gpgerr) == GPG_ERR_INV_VALUE)
     91 		fatalgpg(gpgerr, "Error: gpgme_set_protocol");
     92 	gpgerr = gpgme_data_new_from_file(&in, file, 1); 
     93 	if (gpgme_err_code(gpgerr) != GPG_ERR_NO_ERROR)
     94 		fatalgpg(gpgerr, "Error: gpgme_data_new_from_file");
     95 	gpgerr = gpgme_data_new(&out);
     96 	if (gpgme_err_code(gpgerr) != GPG_ERR_NO_ERROR)
     97 		fatalgpg(gpgerr, "Error: gpgme_data_new");
     98 	gpgerr = gpgme_op_decrypt(ctx, in, out);
     99 	if (gpgme_err_code(gpgerr) != GPG_ERR_NO_ERROR)
    100 		fatalgpg(gpgerr, "Error: gpgme_op_decrypt");
    101 	ret = gpgme_data_seek(out, 0, SEEK_SET);
    102 	if (ret)
    103 		fatalx("gpgme_data_seek");
    104 	if ((a = gpgme_data_read(out, buf, 100)) > 0) 
    105 		buf[a] = '\0';
    106 	gpgme_data_release(in);
    107 	gpgme_data_release(out);
    108 	gpgme_release(ctx);
    109 }
    110 
    111 
    112 void
    113 printpass(char *item)
    114 {
    115 	char buf[MAX_BUFSIZ];
    116 	int fin;
    117 
    118 	gethomedir();
    119 	snprintf(file, sizeof(file), "%s/.password-store/%s.gpg", home, item);
    120 	/* Check if file exists */
    121 	fin = open(file, O_RDONLY);
    122 	if (fin == -1)
    123 		fatal("%s is not in password store.", file);
    124 	decrypt(buf);
    125 	printf("%s\n", buf);
    126 	close(fin);
    127 }
    128 
    129 void
    130 getuserid(char *u, int usize)
    131 {
    132 	char file[PATH_MAX];
    133 	FILE *fp;
    134 	int i;
    135 
    136 	snprintf(file, sizeof(file), "%s/.password-store/.gpg-id", home);
    137 	fp = fopen(file, "r");
    138 	if (!fp)
    139 		fatal("fopen: %s", file);
    140 	while ((i = fgetc(fp)) != EOF && usize--)
    141 		*u++ = i;
    142 	*u = '\0';
    143 }
    144 
    145 void
    146 mkdirp(const char *tp)
    147 {
    148 	char *i, t[PATH_MAX];
    149 	int r;
    150 	mode_t mode = S_IRWXU;
    151 
    152 	memcpy(t, file, strlen(file) + 1);
    153 	while ((i = strchr(tp, '/'))) {	
    154 		*i = '\0';
    155 		snprintf(file, sizeof(file), "%s/%s", t, tp);
    156 		printf("mkdir %s\n", file);
    157 		if ((r = mkdir(file, mode)) == -1) {
    158 			if (errno != EEXIST)
    159 				fatal("mkdir");
    160 		}
    161 		tp = i + 1;
    162 	}
    163 }
    164 
    165 void
    166 encrypt()
    167 {
    168 	gpgme_data_t in, out;
    169 	gpgme_error_t gpgerr;
    170 	gpgme_ctx_t ctx;
    171 	gpgme_key_t key;
    172 	gpgme_key_t keys[2];
    173 	gpgme_protocol_t proto; 
    174 	char uid[MAX_BUFSIZ], t[PATH_MAX];
    175 	FILE *fin, *fout;
    176 
    177 	proto = GPGME_PROTOCOL_OpenPGP;
    178 	key = NULL;
    179 
    180 	initgpgme();
    181 	getuserid(uid, MAX_BUFSIZ);
    182 	gpgerr = gpgme_new(&ctx);
    183 	if (gpgme_err_code(gpgerr) != GPG_ERR_NO_ERROR)
    184 		fatalgpg(gpgerr, "gpme_new");
    185 	gpgerr = gpgme_set_protocol(ctx, proto); 
    186 	if (gpgme_err_code(gpgerr) == GPG_ERR_INV_VALUE)
    187 		fatalgpg(gpgerr, "gpgme_set_protocol");
    188 	gpgme_set_armor(ctx, 1);
    189 	if (gpgme_op_keylist_start(ctx, uid, 0) != GPG_ERR_INV_VALUE)
    190 		while (!(gpgerr = gpgme_op_keylist_next(ctx, &key))) {
    191 			if (key->can_encrypt)
    192 				break;
    193 		}
    194 	if (gpgme_err_code(gpgerr) == GPG_ERR_EOF)
    195 		fatalgpg(gpgerr, "can not find key");
    196 	keys[0] = key;
    197 	keys[1] = NULL;
    198 	fin = fopen(file, "r");
    199 	memcpy(t, file, strlen(file) + 1);
    200 	snprintf(file, sizeof(file), "%s.gpg", t);
    201 	fout = fopen(file, "w");
    202 	gpgerr = gpgme_data_new_from_stream(&in, fin);
    203 	if (gpgme_err_code(gpgerr) != GPG_ERR_NO_ERROR)
    204 		fatalgpg(gpgerr, "gpgme_data_new_from_stream");
    205 	gpgerr = gpgme_data_new_from_stream(&out, fout);
    206 	gpgme_data_set_encoding(out, GPGME_DATA_ENCODING_ARMOR);
    207 	if (gpgme_err_code(gpgerr) != GPG_ERR_NO_ERROR)
    208 		fatalgpg(gpgerr, "gpgme_data_new_from_stream");
    209 	gpgerr = gpgme_op_encrypt(ctx, keys, GPGME_ENCRYPT_ALWAYS_TRUST, in, out);	
    210 	if (gpgme_err_code(gpgerr) != GPG_ERR_NO_ERROR)
    211 		fatalgpg(gpgerr, "gpgme_op_encrypt");
    212 	if (remove(t))
    213 		fprintf(stderr, "remove failed\n");
    214 	gpgme_key_release(key);
    215 	gpgme_data_release(in);
    216 	gpgme_data_release(out);
    217 	gpgme_release(ctx);
    218 }
    219 
    220 void
    221 insert(char *item)
    222 {
    223 	char *filename, t[PATH_MAX];
    224 	FILE *fp;
    225 	char pass[MAX_BUFSIZ];
    226 	int c, fd;
    227 
    228 	c = 'y';
    229 	fd = fileno(stdin);
    230 
    231 	gethomedir();
    232 	snprintf(file, sizeof(file), "%s/.password-store", home);
    233 	filename = basename(item);
    234 	mkdirp(item);	
    235 	memcpy(t, file, strlen(file) + 1);
    236 	snprintf(file, sizeof(file), "%s/%s", t, filename);
    237 	snprintf(t, sizeof(t), "%s.gpg", file);
    238 	if ((fp = fopen(t, "r"))) {
    239 		if (isatty(fd)) {
    240 			printf("An entry already exists for %s. Overwrite it? [y/N] ", filename);
    241 			if ((c = getchar()) == 'N' || c == 'n') {
    242 				fclose(fp);
    243 				logdbgx("Don't overwrite");
    244 				exit(1);
    245 			}
    246 		} else
    247 			/* Assuming user knows what he/she is doing */
    248 			printf("Overwriting %s\n", filename);
    249 	}
    250 	if (c != 'Y' && c != 'y')
    251 		exit(1);
    252 	if (!(fp = fopen(file, "w+b")))
    253 		fatal("fopen: %s", file);
    254 	if (isatty(fd)) {
    255 		readpassphrase("Enter password: ", pass, MAX_BUFSIZ, RPP_ECHO_OFF);
    256 		memcpy(t, pass, strlen(pass) + 1);
    257 		readpassphrase("Retype password: ", pass, MAX_BUFSIZ, RPP_ECHO_OFF);
    258 		if (!strcmp(pass, t))
    259 		{
    260 			int i = 0;
    261 			while(t[i] != '\0') {
    262 				if (fputc(t[i], fp) == EOF)
    263 					fatal("fputc: %s", file);
    264 				i++;
    265 			}
    266 		} else 
    267 			fatalx("Passwords don't match.");
    268 	} else {
    269 		int c;
    270 		while ((c = getchar()) != EOF && c != '\n')
    271 			fputc(c, fp);
    272 	}
    273 	fclose(fp);
    274 	encrypt();
    275 }
    276 
    277 int
    278 isinit(void)
    279 {
    280 	int fp;
    281 	struct stat sb;
    282 
    283 	gethomedir();
    284 	snprintf(file, sizeof(file), "%s/.password-store", home);
    285 	if ((fp = open(file, O_RDONLY)) != -1 &&
    286 	    (!fstat(fp, &sb)) &&
    287 	    (S_ISDIR(sb.st_mode))) {
    288 		close(fp);
    289 		return 1;
    290 	}
    291 	return 0;
    292 }
    293 
    294 void
    295 initpass(char *pgpid)
    296 {
    297 	FILE *gpg;
    298 	mode_t mode = S_IRWXU;
    299 
    300 	if (!pgpid) {
    301 		usage();
    302 	}
    303 	if (!isinit())
    304 	{
    305 		if (mkdir(file, mode))
    306 			fatal("mkdir");
    307 		snprintf(file, sizeof(file), "%s/.password-store/.gpg-id", home);
    308 		if (!(gpg = fopen(file, "a"))) 
    309 			fatal("fopen");
    310 		if (fputs(pgpid, gpg) == EOF)
    311 			fatal("fputs");
    312 		printf("Password store initialized for %s\n", pgpid);
    313 		fclose(gpg);
    314 	} else
    315 		printf("Password store initialized for %s\n", pgpid);
    316 }
    317 
    318 int
    319 main(int argc, char** argv)
    320 {
    321 	char **s;
    322 	int i;
    323 
    324 	debug = 0;
    325 
    326 	for (s = argv, i = 0; i < argc; i++) {
    327 		if (!strcmp("-d", *s++))
    328 			debug = 1;
    329 	}
    330 	if (argc > 1)
    331 	{
    332 		argv++;
    333 		loginit("pass");
    334 		if 	(!strcmp("init", *argv))
    335 			initpass(*(argv + 1));
    336 		else if (!strcmp("insert", *argv))
    337 			insert(*(argv + 1));
    338 		else if (!strcmp("rm", *argv))
    339 			delete(*(argv + 1));
    340 		else
    341 			printpass(*argv);
    342 	} else 
    343 		usage();
    344 	return 0;
    345 }