.su objects to gitignore
[pam_pcsc_cr.git] / authobj.c
1 /*
2 Copyright (c) 2013 Eugene Crosser
3
4 This software is provided 'as-is', without any express or implied
5 warranty. In no event will the authors be held liable for any damages
6 arising from the use of this software.
7
8 Permission is granted to anyone to use this software for any purpose,
9 including commercial applications, and to alter it and redistribute it
10 freely, subject to the following restrictions:
11
12     1. The origin of this software must not be misrepresented; you must
13     not claim that you wrote the original software. If you use this
14     software in a product, an acknowledgment in the product documentation
15     would be appreciated but is not required.
16
17     2. Altered source versions must be plainly marked as such, and must
18     not be misrepresented as being the original software.
19
20     3. This notice may not be removed or altered from any source
21     distribution.
22 */
23
24 #ifdef HAVE_CONFIG_H
25 # include "config.h"
26 #endif
27 #include <stdio.h>
28 #include <string.h>
29 #include <stdlib.h>
30 #include <alloca.h>
31 #include "serial.h"
32 #include "crypto.h"
33 #include "authobj.h"
34
35 static struct _auth_chunk
36 make_challenge(const char *uid, const char *pass, const char *nonce)
37 {
38         struct _auth_chunk ho = {0};
39         unsigned long rc;
40         serializer_t srl;
41         int datasize = strlen(uid) + strlen(pass) + strlen(nonce) +
42                         4 * sizeof(short);
43         unsigned char *data = alloca(datasize);
44         int hashsize = sizeof(ho.data);
45
46         serial_init(&srl, data, datasize);
47         if (serial_put(&srl, uid, strlen(uid)) != strlen(uid)) {
48                 ho.err = "challenge: serialization of uid failed";
49         } else if (serial_put(&srl, pass, strlen(pass)) != strlen(pass)) {
50                 ho.err = "challenge: serialization of pass failed";
51         } else if (serial_put(&srl, nonce, strlen(nonce)) != strlen(nonce)) {
52                 ho.err = "challenge: serialization of nonce failed";
53         } else if (serial_put(&srl, NULL, 0) != 0) {
54                 ho.err = "challenge: serialization of terminator failed";
55         }
56         if (!ho.err) {
57                 if ((rc = hash(data, serial_size(&srl), &ho.data, &hashsize))) {
58                         ho.err = crypto_errstr(rc);
59                 } else if (hashsize != sizeof(ho.data)) {
60                         ho.err = "challenge: hash size is wrong";
61                 }
62         }
63         memset(data, 0, datasize);
64         return ho;
65 }
66
67 static struct _auth_chunk
68 new_key(const unsigned char *challenge, const int challengesize,
69         const unsigned char *secret, const int secsize)
70 {
71         struct _auth_chunk ho = {0};
72         unsigned long rc;
73         int keysize = sizeof(ho.data);
74
75         if ((rc = hmac(secret, secsize, challenge, challengesize,
76                         &ho.data, &keysize))) {
77                 ho.err = crypto_errstr(rc);
78         } else if (keysize != sizeof(ho.data)) {
79                 ho.err = "make_key: hash size is wrong";
80         }
81         return ho;
82 }
83
84 static struct _auth_chunk
85 make_key(const char *userid, const char *password, const char *nonce,
86         const unsigned char *secret, const int secsize,
87         struct _auth_chunk (*fetch_key)(const unsigned char *chal,
88                                         const int csize))
89 {
90         struct _auth_chunk ho_chal, ho_key = {0};
91
92         if (!userid || !password || !nonce) {
93                 ho_key.err = "make_key: missing uid, pass or nonce";
94                 return ho_key;
95         }
96         ho_chal = make_challenge(userid, password, nonce);
97         if (ho_chal.err) {
98                 ho_key.err = ho_chal.err;
99                 return ho_key;
100         }
101         if (secret && secsize) {
102                 ho_key = new_key(ho_chal.data, sizeof(ho_chal.data),
103                                 secret, secsize);
104         } else if (fetch_key) {
105                 ho_key = (*fetch_key)(ho_chal.data, sizeof(ho_chal.data));
106         } else {
107                 ho_key.err = "make_key: neither secret nor fetch_key present";
108         }
109         memset(&ho_chal, 0, sizeof(ho_chal));
110         return ho_key;
111 }
112
113 static struct _auth_obj
114 make_authobj(const char *userid, const char *password, const char *nonce,
115                 const unsigned char *secret, const int secsize,
116                 const unsigned char *payload, const int paylsize)
117 {
118         struct _auth_obj ao = {0};
119         unsigned long rc;
120         unsigned char *data;
121         int datasize;
122         unsigned char datahash[HASHSIZE];
123         int datahashsize = HASHSIZE;
124         serializer_t srl;
125
126         datasize = ((secsize + paylsize + HASHSIZE + 4 * sizeof(short) - 1) /
127                         CBLKSIZE + 1) * CBLKSIZE;
128         data = alloca(datasize);
129         /* 
130            We allocate memory rounded up to CBLKSIZE on the stack, but do not
131            use the last bytes. Stack protectors, if enabled, fill this memory
132            with `canary` value. Later, when encryption function is called,
133            stack protector detects that it tries to access "uninitialized
134            memory". Which, while technically true, is not an error. Still,
135            let us make stack protector happy by initializing the whole area:
136          */
137         memset(data, 0, datasize);
138         serial_init(&srl, data, datasize);
139         if (serial_put(&srl, secret, secsize) != secsize) {
140                 ao.err = "authobj: serialization of secret failed";
141         } else if (serial_put(&srl, payload, paylsize) != paylsize) {
142                 ao.err = "authobj: serialization of payload failed";
143         } else if ((rc = hash(data, serial_size(&srl),
144                                 datahash, &datahashsize))) {
145                 ao.err = crypto_errstr(rc);
146         } else if (serial_put(&srl, datahash, datahashsize) != datahashsize) {
147                 ao.err = "authobj: serialization of hash failed";
148         } else if (serial_put(&srl, NULL, 0) != 0) {
149                 ao.err = "authobj: serialization of terminator failed";
150         } else {
151                 unsigned long lrc;
152                 int osize = ((serial_size(&srl) -1) / CBLKSIZE + 1) * CBLKSIZE;
153                 struct _auth_chunk ho_key;
154
155                 ho_key = make_key(userid, password, nonce,
156                                         secret, secsize, NULL);
157                 if (ho_key.err) {
158                         ao.err = ho_key.err;
159                 } else if ((ao.buffer = malloc(osize + paylsize)) == NULL) {
160                         ao.err = "make authobj: malloc failed";
161                 } else if ((lrc = encrypt(ho_key.data, CBLKSIZE, data,
162                                         ao.buffer, osize))) {
163                         ao.err = crypto_errstr(lrc);
164                 } else {
165                         ao.data = ao.buffer;
166                         ao.datasize = osize;
167                         if (payload && paylsize) {
168                                 /* payload passthrough */
169                                 ao.payload = ao.data + osize;
170                                 memcpy(ao.payload, payload, paylsize);
171                                 ao.paylsize = paylsize;
172                         }
173                 }
174                 memset(&ho_key, 0, sizeof(ho_key));
175         }
176         memset(data, 0, datasize);
177         return ao;
178 }
179
180 static struct _auth_obj
181 parse_authobj(const char *userid, const char *password, const char *nonce,
182                 const unsigned char *secret, const int secsize,
183                 const unsigned char *ablob, const int blobsize,
184                 struct _auth_chunk (*fetch_key)(const unsigned char *chal,
185                                                 const int csize))
186 {
187         unsigned long rc;
188         struct _auth_obj ao = {0};
189         struct _auth_chunk ho_key;
190
191         ho_key = make_key(userid, password, nonce, secret, secsize, fetch_key);
192         if (ho_key.err) {
193                 ao.err = ho_key.err;
194         } else if ((ao.buffer = malloc(blobsize)) == NULL) {
195                 ao.err = "parse authobj: malloc failed";
196         } else if ((rc = decrypt(ho_key.data, CBLKSIZE,
197                                 ablob, ao.buffer, blobsize))) {
198                 ao.err = crypto_errstr(rc);
199         } else {
200                 serializer_t srl;
201                 unsigned char myhash[HASHSIZE];
202                 int myhsize = HASHSIZE;
203                 unsigned char *theirhash;
204                 int theirhsize;
205                 unsigned long rc;
206
207                 serial_init(&srl, ao.buffer, blobsize);
208                 if (serial_get(&srl, (void**)&ao.data, &ao.datasize)) {
209                         ao.err = "mismatch: impossible secret";
210                 } else if (serial_get(&srl, (void**)&ao.payload, &ao.paylsize)) {
211                         ao.err = "mismatch: impossible payload";
212                 } else if ((rc = hash(ao.buffer, serial_size(&srl),
213                                         myhash, &myhsize))) {
214                         ao.err = crypto_errstr(rc);
215                 } else if (serial_get(&srl, (void**)&theirhash, &theirhsize)) {
216                         ao.err = "mismatch: impossible hash";
217                 } else if (theirhsize != HASHSIZE) {
218                         ao.err = "mismatch: hash is of wrong size";
219                 } else if ((myhsize != theirhsize) ||
220                                 memcmp(myhash, theirhash, myhsize)) {
221                         ao.err = "mismatch: different hash";
222                 }
223         }
224         memset(&ho_key, 0, sizeof(ho_key));
225         return ao;
226 }
227
228 struct _auth_obj authobj(const char *userid, const char *password,
229                 const char *oldnonce, const char *newnonce,
230                 const unsigned char *secret, const int secsize,
231                 const unsigned char *payload, const int paylsize,
232                 const unsigned char *ablob, const int blobsize,
233                 struct _auth_chunk (*fetch_key)(const unsigned char *chal,
234                                                 const int csize))
235 {
236         const unsigned char *wsecret;
237         int wsecsize;
238         const unsigned char *wpayload;
239         int wpaylsize;
240         struct _auth_obj old_ao = {0};
241         struct _auth_obj new_ao = {0};
242
243         if (!secret || !secsize || !payload) {
244                 if (!ablob || !blobsize) {
245                         new_ao.err = "authobj: previous data not supplied";
246                         return new_ao;
247                 }
248                 old_ao = parse_authobj(userid, password, oldnonce,
249                                         secret, secsize,
250                                         ablob, blobsize, fetch_key);
251                 if (old_ao.err) {
252                         new_ao.err = old_ao.err;
253                         if (old_ao.buffer) free(old_ao.buffer);
254                         return new_ao;
255                 } else {
256                         if (secret && secsize) {
257                                 wsecret = secret;
258                                 wsecsize = secsize;
259                         } else {
260                                 wsecret = old_ao.data;
261                                 wsecsize = old_ao.datasize;
262                         }
263                         if (payload) {
264                                 wpayload = payload;
265                                 wpaylsize = paylsize;
266                         } else {
267                                 wpayload = old_ao.payload;
268                                 wpaylsize = old_ao.paylsize;
269                         }
270                 }
271         } else {
272                 wsecret = secret;
273                 wsecsize = secsize;
274                 wpayload = payload;
275                 wpaylsize = paylsize;
276         }
277
278
279         new_ao = make_authobj(userid, password, newnonce,
280                                 wsecret, wsecsize, wpayload, wpaylsize);
281
282         if (old_ao.data) memset(old_ao.data, 0, old_ao.datasize);
283         if (old_ao.payload) memset(old_ao.payload, 0, old_ao.paylsize);
284         if (old_ao.buffer) free(old_ao.buffer);
285         return new_ao;
286 }