pass

A stripped down version of the standard unix password manager "pass"
git clone git://nocebo.space/pass
Log | Files | Refs | README | LICENSE

pass.c (7511B)


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