1 // GPGEdit edits GPG encrypted files
2 // Copyright (C) 2018 Aaron Ball <nullspoon@oper.io>
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program. If not, see <https://www.gnu.org/licenses/>.
16 #include "gpg.h"
17
18
19 int gpg_failiferr(gpgme_error_t err) {
20 if(err != GPG_ERR_NO_ERROR) {
21 if(err == GPG_ERR_INV_VALUE) {
22 logger(LOG_FATAL, "Invalid data structure\n");
23 } else if(err == GPG_ERR_NO_DATA) {
24 logger(LOG_FATAL, "Data struct contains no encrypted data\n");
25 } else if(err == GPG_ERR_DECRYPT_FAILED) {
26 logger(LOG_FATAL, "Could not decrypt file\n");
27 } else if(err == GPG_ERR_ENOMEM) {
28 logger(LOG_FATAL, "Insufficient memory to allocate GPG context\n");
29 } else if(err == GPG_ERR_NOT_OPERATIONAL) {
30 logger(LOG_FATAL, "GPGME not initialized\n");
31 } else if(err == GPG_ERR_UNUSABLE_PUBKEY) {
32 logger(LOG_FATAL, "One or more public keys unusable\n");
33 } else if(err == GPG_ERR_BAD_PASSPHRASE) {
34 logger(LOG_FATAL, "Incorrect passphrase\n");
35 } else if(err == GPG_ERR_NOT_IMPLEMENTED) {
36 logger(LOG_FATAL, "Not implemented\n");
37 } else {
38 logger(LOG_ERROR, "Unknown error: %d\n", err);
39 }
40 logger(LOG_ERROR, " %s\n", gpgme_strerror(err));
41 return err;
42 }
43 return 0;
44 }
45
46
47 gpgme_error_t init_gpg(gpgme_ctx_t* ctx, gpgme_protocol_t proto) {
48 gpgme_error_t err;
49
50 gpgme_check_version (NULL);
51 setlocale (LC_ALL, "");
52 gpgme_set_locale (NULL, LC_CTYPE, setlocale (LC_CTYPE, NULL));
53
54 err = gpgme_engine_check_version (proto);
55 gpg_failiferr(err);
56
57 err = gpgme_new(ctx);
58 gpg_failiferr(err);
59
60 gpgme_set_protocol(*ctx, proto);
61 gpgme_set_armor(*ctx, 1);
62
63 return err;
64 }
65
66
67 int gpg_data_fwrite(gpgme_data_t dh, FILE* out) {
68 char buf[READSIZE];
69 size_t total = 0;
70 size_t size = 0;
71
72 gpgme_data_seek(dh, 0, SEEK_SET);
73 if(gpgme_data_read(dh, buf, 1) < 1) {
74 logger(LOG_ERROR, "GPG data struct is empty. Nothing to write.\n");
75 return -1;
76 }
77
78 // Set seek to beginning of file
79 gpgme_data_seek(dh, 0, SEEK_SET);
80
81 while((size = gpgme_data_read(dh, buf, READSIZE)) > 0) {
82 total += size;
83 buf[size] = '\0'; // Ensure string terminates at the correct length
84 fprintf(out, "%s", buf); // Write to file descriptor
85 }
86
87 return total;
88 }
89
90
91 int gpg_recip_count(gpgme_recipient_t firstrecip) {
92 gpgme_recipient_t recip; // Current recipient object
93 int count = 0; // Count of recipients
94
95 // Get recipient list
96 recip = firstrecip;
97 while(recip != NULL) {
98 // printf("-- recipient: %s\n", recip->keyid);
99 recip = recip->next;
100 count++;
101 }
102
103 return count;
104 }
105
106
107 // gpg_strll_to_key_t:
108 // Converts a string linked list containing keyids (multiple identifier types
109 // are supported) into an array of gpgme_key_t objects.
110 //
111 // @ctx GPGME context (already initialized)
112 // @firstrecip First linkedlist recipient item
113 //
114 // @return gpgme_key_t* Array of gpgme_key_t objects, one for each keyid string
115 gpgme_key_t* gpg_strll_to_key_t(gpgme_ctx_t ctx, struct strll* firstrecip) {
116 gpgme_key_t* out;
117 gpgme_error_t err;
118 int i = 0;
119 struct strll* recip = firstrecip;
120
121 // Initialize the gpgme_key_t pointer array
122 out = (gpgme_key_t*) malloc(1);
123 out = NULL;
124
125 // Loop over the keys back to zero
126 while(recip != NULL) {
127 out = realloc(out, sizeof(gpgme_key_t) * (i + 1));
128 err = gpgme_get_key(ctx, recip->str, &out[i], 0);
129 gpg_failiferr(err);
130 recip = recip->next;
131 i++;
132 }
133 out[i] = NULL;
134 return out;
135 }
136
137
138 void gpg_key_dump(gpgme_key_t key) {
139 printf("Recipient: %s\n", key->fpr);
140 printf(" protocol: %d\n", key->protocol);
141 printf(" revoked: %d\n", key->revoked);
142 printf(" expired: %d\n", key->expired);
143 printf(" disabled: %d\n", key->disabled);
144 printf(" invalid: %d\n", key->invalid);
145 printf(" can_encrypt: %d\n", key->can_encrypt);
146 printf(" can_sign: %d\n", key->can_sign);
147 printf(" can_certify: %d\n", key->can_certify);
148 printf(" can_auth: %d\n", key->can_authenticate);
149 printf(" qualified: %d\n", key->is_qualified);
150 printf(" secret: %d\n", key->secret);
151 printf(" issuer_serial: %s\n", key->issuer_serial);
152 printf(" issuer_name: %s\n", key->issuer_name);
153 printf(" chain_id: %s\n", key->chain_id);
154 }
155
156
157 size_t gpg_encrypt_file(char* infile, char* outfile, struct strll* recipll) {
158 gpgme_ctx_t ctx;
159 gpgme_data_t indata;
160 gpgme_data_t outdata;
161 gpgme_error_t err;
162 gpgme_key_t* keys = NULL;
163
164 err = init_gpg(&ctx, GPGME_PROTOCOL_OPENPGP);
165 gpg_failiferr(err);
166
167 // For debugging purposes
168 logger(LOG_DEBUG, "Reading updated data from memory\n");
169 // Initialize data structures
170 err = gpgme_data_new_from_file(&indata, infile, 1);
171 gpg_failiferr(err);
172
173 err = gpgme_data_new(&outdata);
174 gpg_failiferr(err);
175
176 // Get key id objects from keyids char array
177 keys = gpg_strll_to_key_t(ctx, recipll);
178
179 // For debugging purposes
180 // int i = 0;
181 // while(keys[i] != NULL) {
182 // gpg_key_dump(keys[i]);
183 // i++;
184 // }
185
186 // Encrypt!
187 err = gpgme_op_encrypt(ctx, keys, GPGME_ENCRYPT_ALWAYS_TRUST, indata, outdata);
188 gpg_failiferr(err);
189
190
191 // For debugging purposes
192 logger(LOG_DEBUG, "No errors detecting during encryption. Writing to disk\n");
193
194 // Write to disk!
195 FILE* refd = fopen(outfile, "w");
196 gpg_data_fwrite(outdata, refd);
197 fclose(refd);
198
199 // cleanup
200 gpgme_data_release(indata);
201 gpgme_data_release(outdata);
202 gpgme_release(ctx);
203 return 0;
204 }
205
206
207 gpgme_decrypt_result_t
208 gpg_decrypt_file(char* infile, char* outfile, gpgme_ctx_t ctx) {
209 gpgme_error_t err;
210 gpgme_data_t indata = NULL;
211 gpgme_data_t outdata = NULL;
212
213 // Read ciphertext file into gpgme data struct, and fail if any errors
214 // returned.
215 gpgme_data_new(&outdata);
216 err = gpgme_data_new_from_file(&indata, infile, 1);
217 gpg_failiferr(err);
218
219 // Attempt to decrypt
220 err = gpgme_op_decrypt(ctx, indata, outdata);
221 gpg_failiferr(err);
222
223 // Write decrypted data to outfile
224 FILE* outfd = fopen(outfile, "w");
225 gpg_data_fwrite(outdata, outfd);
226
227 // Clean up
228 fclose(outfd);
229 gpgme_data_release(indata);
230 gpgme_data_release(outdata);
231 return gpgme_op_decrypt_result(ctx);
232 }
|