f8d577d8d66eb43efbcf89409c620791ceca0ca9
[pam_pcsc_cr.git] / authobj.c
1 #ifdef HAVE_CONFIG_H
2 # include "config.h"
3 #endif
4 #include <stdio.h>
5 #include <string.h>
6 #include <alloca.h>
7 #include "serial.h"
8 #include "crypto.h"
9 #include "authobj.h"
10 #include "pcsc_cr.h"
11
12 static struct _auth_chunk
13 make_challenge(const char *uid, const char *pass, const char *nonce)
14 {
15         struct _auth_chunk ho = {0};
16         unsigned long rc;
17         serializer_t srl;
18         int datasize = strlen(uid) + strlen(pass) + strlen(nonce) +
19                         4 * sizeof(short);
20         unsigned char *data = alloca(datasize);
21         int hashsize = sizeof(ho.data);
22
23         serial_init(&srl, data, datasize);
24         if (serial_put(&srl, uid, strlen(uid)) != strlen(uid)) {
25                 ho.err = "challenge: serialization of uid failed";
26         } else if (serial_put(&srl, pass, strlen(pass)) != strlen(pass)) {
27                 ho.err = "challenge: serialization of pass failed";
28         } else if (serial_put(&srl, nonce, strlen(nonce)) != strlen(nonce)) {
29                 ho.err = "challenge: serialization of nonce failed";
30         } else if (serial_put(&srl, NULL, 0) != 0) {
31                 ho.err = "challenge: serialization of terminator failed";
32         }
33         if (!ho.err) {
34                 if ((rc = hash(data, serial_size(&srl), &ho.data, &hashsize))) {
35                         ho.err = crypto_errstr(rc);
36                 } else if (hashsize != sizeof(ho.data)) {
37                         ho.err = "challenge: hash size is wrong";
38                 }
39         }
40         memset(data, 0, datasize);
41         return ho;
42 }
43
44 static struct _auth_chunk
45 new_key(const unsigned char *challenge, const int challengesize,
46         const unsigned char *secret, const int secsize)
47 {
48         struct _auth_chunk ho = {0};
49         unsigned long rc;
50         int keysize = sizeof(ho.data);
51
52         if ((rc = hmac(secret, secsize, challenge, challengesize,
53                         &ho.data, &keysize))) {
54                 ho.err = crypto_errstr(rc);
55         } else if (keysize != sizeof(ho.data)) {
56                 ho.err = "make_key: hash size is wrong";
57         }
58         return ho;
59 }
60
61 static struct _auth_chunk
62 make_key(const char *userid, const char *password, const char *nonce,
63         const unsigned char *secret, const int secsize,
64         struct _auth_chunk (*fetch_key)(const unsigned char *chal,
65                                         const int csize))
66 {
67         struct _auth_chunk ho_chal, ho_key = {0};
68
69         if (!userid || !password || !nonce) {
70                 ho_key.err = "make_key: missing uid, pass or nonce";
71                 return ho_key;
72         }
73         ho_chal = make_challenge(userid, password, nonce);
74         if (ho_chal.err) {
75                 ho_key.err = ho_chal.err;
76                 return ho_key;
77         }
78         if (secret && secsize) {
79                 ho_key = new_key(ho_chal.data, sizeof(ho_chal.data),
80                                 secret, secsize);
81         } else if (fetch_key) {
82                 ho_key = (*fetch_key)(ho_chal.data, sizeof(ho_chal.data));
83         } else {
84                 ho_key.err = "make_key: neither secret nor fetch_key present";
85         }
86         memset(&ho_chal, 0, sizeof(ho_chal));
87         return ho_key;
88 }
89
90 static struct _auth_obj
91 make_authobj(char *userid, char *password, char *nonce,
92                 const unsigned char *secret, const int secsize,
93                 const unsigned char *payload, const int paylsize)
94 {
95         struct _auth_obj ao = {0};
96         unsigned long rc;
97         unsigned char *data;
98         int datasize;
99         unsigned char datahash[HASHSIZE];
100         int datahashsize = HASHSIZE;
101         serializer_t srl;
102
103         datasize = ((secsize + paylsize + HASHSIZE + 4 * sizeof(short) - 1) /
104                         CBLKSIZE + 1) * CBLKSIZE;
105         data = alloca(datasize);
106         serial_init(&srl, data, datasize);
107         if (serial_put(&srl, secret, secsize) != secsize) {
108                 ao.err = "authobj: serialization of secret failed";
109         } else if (serial_put(&srl, payload, paylsize) != paylsize) {
110                 ao.err = "authobj: serialization of payload failed";
111         } else if ((rc = hash(data, serial_size(&srl),
112                                 datahash, &datahashsize))) {
113                 ao.err = crypto_errstr(rc);
114         } else if (serial_put(&srl, datahash, datahashsize) != datahashsize) {
115                 ao.err = "authobj: serialization of hash failed";
116         } else if (serial_put(&srl, NULL, 0) != 0) {
117                 ao.err = "authobj: serialization of terminator failed";
118         } else {
119                 unsigned long lrc;
120                 int osize = ((serial_size(&srl) -1) / CBLKSIZE + 1) * CBLKSIZE;
121                 struct _auth_chunk ho_key;
122
123                 ho_key = make_key(userid, password, nonce,
124                                         secret, secsize, NULL);
125                 if (ho_key.err) {
126                         ao.err = ho_key.err;
127                 } else if ((ao.buffer = malloc(osize + paylsize)) == NULL) {
128                         ao.err = "make authobj: malloc failed";
129                 } else if ((lrc = encrypt(ho_key.data, CBLKSIZE, data,
130                                         ao.buffer, osize))) {
131                         ao.err = crypto_errstr(lrc);
132                 } else {
133                         ao.data = ao.buffer;
134                         ao.datasize = osize;
135                         if (payload && paylsize) {
136                                 /* payload passthrough */
137                                 ao.payload = ao.data + osize;
138                                 memcpy(ao.payload, payload, paylsize);
139                                 ao.paylsize = paylsize;
140                         }
141                 }
142                 memset(&ho_key, 0, sizeof(ho_key));
143         }
144         memset(data, 0, datasize);
145         return ao;
146 }
147
148 static struct _auth_obj
149 parse_authobj(char *userid, char *password, char *nonce,
150                 const unsigned char *secret, const int secsize,
151                 const unsigned char *ablob, const int blobsize,
152                 struct _auth_chunk (*fetch_key)(const unsigned char *chal,
153                                                 const int csize))
154 {
155         unsigned long rc;
156         struct _auth_obj ao = {0};
157         struct _auth_chunk ho_key;
158
159         ho_key = make_key(userid, password, nonce, secret, secsize, fetch_key);
160         if (ho_key.err) {
161                 ao.err = ho_key.err;
162         } else if ((ao.buffer = malloc(blobsize)) == NULL) {
163                 ao.err = "parse authobj: malloc failed";
164         } else if ((rc = decrypt(ho_key.data, CBLKSIZE,
165                                 ablob, ao.buffer, blobsize))) {
166                 ao.err = crypto_errstr(rc);
167         } else {
168                 serializer_t srl;
169                 unsigned char myhash[HASHSIZE];
170                 int myhsize = HASHSIZE;
171                 unsigned char *theirhash;
172                 int theirhsize;
173                 unsigned long rc;
174
175                 serial_init(&srl, ao.buffer, blobsize);
176                 if (serial_get(&srl, (void**)&ao.data, &ao.datasize)) {
177                         ao.err = "mismatch: impossible secret";
178                 } else if (serial_get(&srl, (void**)&ao.payload, &ao.paylsize)) {
179                         ao.err = "mismatch: impossible payload";
180                 } else if ((rc = hash(ao.buffer, serial_size(&srl),
181                                         myhash, &myhsize))) {
182                         ao.err = crypto_errstr(rc);
183                 } else if (serial_get(&srl, (void**)&theirhash, &theirhsize)) {
184                         ao.err = "mismatch: impossible hash";
185                 } else if (theirhsize != HASHSIZE) {
186                         ao.err = "mismatch: hash is of wrong size";
187                 } else if ((myhsize != theirhsize) ||
188                                 memcmp(myhash, theirhash, myhsize)) {
189                         ao.err = "mismatch: different hash";
190                 }
191         }
192         memset(&ho_key, 0, sizeof(ho_key));
193         return ao;
194 }
195
196 struct _auth_obj authobj(const char *userid, const char *password,
197                 const char *oldnonce, const char *newnonce,
198                 const unsigned char *secret, const int secsize,
199                 const unsigned char *payload, const int paylsize,
200                 const unsigned char *ablob, const int blobsize,
201                 struct _auth_chunk (*fetch_key)(const unsigned char *chal,
202                                                 const int csize))
203 {
204         unsigned char *wsecret;
205         int wsecsize;
206         unsigned char *wpayload;
207         int wpaylsize;
208         struct _auth_obj old_ao;
209         struct _auth_obj new_ao = {0};
210
211         if (!secret || !secsize || !payload) {
212                 old_ao = parse_authobj(userid, password, oldnonce,
213                                         secret, secsize,
214                                         ablob, blobsize, fetch_key);
215                 if (old_ao.err) {
216                         new_ao.err = old_ao.err;
217                         if (old_ao.buffer) free(old_ao.buffer);
218                         return new_ao;
219                 } else {
220                         if (secret && secsize) {
221                                 wsecret = secret;
222                                 wsecsize = secsize;
223                         } else {
224                                 wsecret = old_ao.data;
225                                 wsecsize = old_ao.datasize;
226                         }
227                         if (payload) {
228                                 wpayload = payload;
229                                 wpaylsize = paylsize;
230                         } else {
231                                 wpayload = old_ao.payload;
232                                 wpaylsize = old_ao.paylsize;
233                         }
234                 }
235         } else {
236                 wsecret = secret;
237                 wsecsize = secsize;
238                 wpayload = payload;
239                 wpaylsize = paylsize;
240         }
241
242
243         new_ao = make_authobj(userid, password, newnonce,
244                                 wsecret, wsecsize, wpayload, wpaylsize);
245
246         if (old_ao.data) memset(old_ao.data, 0, old_ao.datasize);
247         if (old_ao.payload) memset(old_ao.payload, 0, old_ao.paylsize);
248         if (old_ao.buffer) free(old_ao.buffer);
249         return new_ao;
250 }