1 /*
2 * Off-the-Record Messaging library
3 * Copyright (C) 2004-2015 Ian Goldberg, Rob Smits, Chris Alexander,
4 * Willy Lew, Lisa Du, Nikita Borisov
5 * <otr@cypherpunks.ca>
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of version 2.1 of the GNU Lesser General
9 * Public License as published by the Free Software Foundation.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21 /* system headers */
22 #include <stdio.h>
23 #include <stdlib.h>
24
25 /* libgcrypt headers */
26 #include <gcrypt.h>
27
28 /* libotr headers */
29 #include "instag.h"
30 #include "userstate.h"
31
32 /* Forget the given instag. */
otrl_instag_forget(OtrlInsTag * instag)33 void otrl_instag_forget(OtrlInsTag* instag) {
34 if (!instag) return;
35
36 if (instag->accountname) free(instag->accountname);
37 if (instag->protocol) free(instag->protocol);
38
39 /* Re-link the list */
40 *(instag->tous) = instag->next;
41 if (instag->next) {
42 instag->next->tous = instag->tous;
43 }
44
45 free(instag);
46 }
47
48 /* Forget all instags in a given OtrlUserState. */
otrl_instag_forget_all(OtrlUserState us)49 void otrl_instag_forget_all(OtrlUserState us) {
50 while(us->instag_root) {
51 otrl_instag_forget(us->instag_root);
52 }
53 }
54
55 /* Fetch the instance tag from the given OtrlUserState associated with
56 * the given account */
otrl_instag_find(OtrlUserState us,const char * accountname,const char * protocol)57 OtrlInsTag * otrl_instag_find(OtrlUserState us, const char *accountname,
58 const char *protocol)
59 {
60 OtrlInsTag *p;
61
62 for(p=us->instag_root; p; p=p->next) {
63 if (!strcmp(p->accountname, accountname) &&
64 !strcmp(p->protocol, protocol)) {
65 return p;
66 }
67 }
68 return NULL;
69 }
70
71 /* Read our instance tag from a file on disk into the given
72 * OtrlUserState. */
otrl_instag_read(OtrlUserState us,const char * filename)73 gcry_error_t otrl_instag_read(OtrlUserState us, const char *filename)
74 {
75 gcry_error_t err;
76 FILE *instf;
77
78 /* Open the instance tag file. */
79 instf = fopen(filename, "rb");
80 if (!instf) {
81 return gcry_error_from_errno(errno);
82 }
83
84 err = otrl_instag_read_FILEp(us, instf);
85 fclose(instf);
86 return err;
87 }
88
89 /* Read our instance tag from a file on disk into the given
90 * OtrlUserState. The FILE* must be open for reading. */
otrl_instag_read_FILEp(OtrlUserState us,FILE * instf)91 gcry_error_t otrl_instag_read_FILEp(OtrlUserState us, FILE *instf)
92 {
93 if (!instf) return gcry_error(GPG_ERR_NO_ERROR);
94
95 OtrlInsTag *p;
96 char storeline[1000];
97 size_t maxsize = sizeof(storeline);
98
99 while(fgets(storeline, maxsize, instf)) {
100 char *prevpos;
101 char *pos;
102 unsigned int instag = 0;
103
104 p = malloc(sizeof(*p));
105 if (!p) {
106 return gcry_error(GPG_ERR_ENOMEM);
107 }
108
109 /* Parse the line, which should be of the form:
110 * accountname\tprotocol\t40_hex_nybbles\n */
111 prevpos = storeline;
112 pos = strchr(prevpos, '\t');
113 if (!pos) {
114 free(p);
115 continue;
116 }
117 *pos = '\0';
118 pos++;
119 p->accountname = malloc(pos - prevpos);
120 if (!(p->accountname)) {
121 free(p);
122 return gcry_error(GPG_ERR_ENOMEM);
123 }
124 memmove(p->accountname, prevpos, pos - prevpos);
125
126 prevpos = pos;
127 pos = strchr(prevpos, '\t');
128 if (!pos) {
129 free(p->accountname);
130 free(p);
131 continue;
132 }
133 *pos = '\0';
134 pos++;
135 p->protocol = malloc(pos - prevpos);
136 if (!(p->protocol)) {
137 free(p->accountname);
138 free(p);
139 return gcry_error(GPG_ERR_ENOMEM);
140 }
141 memmove(p->protocol, prevpos, pos - prevpos);
142
143 prevpos = pos;
144 pos = strchr(prevpos, '\r');
145 if (!pos) pos = strchr(prevpos, '\n');
146 if (!pos) {
147 free(p->accountname);
148 free(p->protocol);
149 free(p);
150 continue;
151 }
152 *pos = '\0';
153 pos++;
154 /* hex str of length 8 */
155 if (strlen(prevpos) != 8) {
156 free(p->accountname);
157 free(p->protocol);
158 free(p);
159 continue;
160 }
161
162 sscanf(prevpos, "%08x", &instag);
163
164 if (instag < OTRL_MIN_VALID_INSTAG) {
165 free(p->accountname);
166 free(p->protocol);
167 free(p);
168 continue;
169 }
170 p->instag = instag;
171
172 /* Link it up */
173 p->next = us->instag_root;
174 if (p->next) {
175 p->next->tous = &(p->next);
176 }
177 p->tous = &(us->instag_root);
178 us->instag_root = p;
179 }
180
181 return gcry_error(GPG_ERR_NO_ERROR);
182 }
183
184 /* Generate a new instance tag for the given account and write to file */
otrl_instag_generate(OtrlUserState us,const char * filename,const char * accountname,const char * protocol)185 gcry_error_t otrl_instag_generate(OtrlUserState us, const char *filename,
186 const char *accountname, const char *protocol)
187 {
188 gcry_error_t err;
189 FILE *instf;
190
191 /* Open the instance tag file. */
192 instf = fopen(filename, "wb");
193 if (!instf) {
194 return gcry_error_from_errno(errno);
195 }
196
197 err = otrl_instag_generate_FILEp(us, instf, accountname, protocol);
198 fclose(instf);
199 return err;
200 }
201
202 /* Return a new valid instance tag */
otrl_instag_get_new()203 otrl_instag_t otrl_instag_get_new()
204 {
205 otrl_instag_t result = 0;
206
207 while(result < OTRL_MIN_VALID_INSTAG) {
208 otrl_instag_t * instag = (otrl_instag_t *)gcry_random_bytes(
209 sizeof(otrl_instag_t), GCRY_STRONG_RANDOM);
210 result = *instag;
211 gcry_free(instag);
212 }
213
214 return result;
215 }
216
217 /* Generate a new instance tag for the given account and write to file
218 * The FILE* must be open for writing. */
otrl_instag_generate_FILEp(OtrlUserState us,FILE * instf,const char * accountname,const char * protocol)219 gcry_error_t otrl_instag_generate_FILEp(OtrlUserState us, FILE *instf,
220 const char *accountname, const char *protocol)
221 {
222 OtrlInsTag *p;
223 if (!accountname || !protocol) return gcry_error(GPG_ERR_NO_ERROR);
224
225 p = (OtrlInsTag *)malloc(sizeof(OtrlInsTag));
226 p->accountname = strdup(accountname);
227 p->protocol = strdup(protocol);
228
229 p->instag = otrl_instag_get_new();
230
231 /* Add to our list in OtrlUserState */
232 p->next = us->instag_root;
233 if (p->next) {
234 p->next->tous = &(p->next);
235 }
236 p->tous = &(us->instag_root);
237 us->instag_root = p;
238
239 otrl_instag_write_FILEp(us, instf);
240
241 return gcry_error(GPG_ERR_NO_ERROR);
242 }
243
244 /* Write our instance tags to a file on disk. */
otrl_instag_write(OtrlUserState us,const char * filename)245 gcry_error_t otrl_instag_write(OtrlUserState us, const char *filename)
246 {
247 gcry_error_t err;
248 FILE *instf;
249
250 /* Open the instance tag file. */
251 instf = fopen(filename, "wb");
252 if (!instf) {
253 return gcry_error_from_errno(errno);
254 }
255
256 err = otrl_instag_write_FILEp(us, instf);
257 fclose(instf);
258 return err;
259 }
260
261 /* Write our instance tags to a file on disk.
262 * The FILE* must be open for writing. */
otrl_instag_write_FILEp(OtrlUserState us,FILE * instf)263 gcry_error_t otrl_instag_write_FILEp(OtrlUserState us, FILE *instf)
264 {
265 OtrlInsTag *p;
266 /* This line should be ignored when read back in, since there are no
267 tabs. */
268 fprintf(instf, "# WARNING! You shouldn't copy this file to another"
269 " computer. It is unnecessary and can cause problems.\n");
270 for(p=us->instag_root; p; p=p->next) {
271 fprintf(instf, "%s\t%s\t%08x\n", p->accountname, p->protocol,
272 p->instag);
273 }
274
275 return gcry_error(GPG_ERR_NO_ERROR);
276 }
277
278