wip redoing authfile
[pam_pcsc_cr.git] / authfile.c
1 #ifdef HAVE_CONFIG_H
2 # include "config.h"
3 #endif
4 #include <sys/types.h>
5 #include <sys/stat.h>
6 #include <pwd.h>
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <unistd.h>
11 #include <errno.h>
12 #include <alloca.h>
13 #include "authobj.h"
14 #include "authfile.h"
15
16 /*
17  * Template string may contain zero or one '~' and zero or one '?'.
18  * '~' at the beginning of the template string is substituted with
19  * the home directory of the userid. In any other position it is
20  * substituted with the userid itself. '?' is substituted with the
21  * tokenid. There is no way to make the resulting path contain '~'
22  * or '?'. If there is more than one '~' or '?', or if the '~' is
23  * at the beginning but userid does not resolve via getpwnam, or
24  * the character to substitute is present but the argument is NULL,
25  * NULL is returned. Otherwise, malloc()'ed area containg the path
26  * string.
27  */
28
29 static char *template = "~/.pam_cr/auth";
30
31 void authfile_template(char *str)
32 {
33         template = str;
34 }
35
36 static char *make_path(const char *tokenid, const char *userid)
37 {
38         const char *usub;
39         char *path;
40         char *p, *q;
41         struct passwd *pw;
42
43         if ((p = strchr(template, '~')) != strrchr(template, '~')) return NULL;
44         if ((q = strchr(template, '?')) != strrchr(template, '?')) return NULL;
45         if (p && !userid) return NULL;
46         if (q && !tokenid) return NULL;
47         if (p == template) {
48                 pw = getpwnam(userid);
49                 if (!pw) return NULL;
50                 usub = pw->pw_dir;
51         } else {
52                 usub = userid;
53         }
54         path = malloc(strlen(template) + p?strlen(usub):0 +
55                                                 q?strlen(tokenid):0 + 1);
56         if (!path) return NULL;
57         q = path;
58         for (p = template; *p; p++) switch (*p) {
59         case '~':
60                 strcpy(q, usub);
61                 while (*q) q++;
62                 break;
63         case '?':
64                 strcpy(q, tokenid);
65                 while (*q) q++;
66                 break;
67         default:
68                 *q++ = *p;
69                 break;
70         }
71         *q = '\0';
72         return path;
73 }
74
75 struct _auth_obj authfile(const char *tokenid,
76                 const char *userid, const char *password,
77                 void (*update_nonce)(char *nonce, const int nonsize),
78                 const unsigned char *secret, const int secsize,
79                 const unsigned char *payload, const int paylsize,
80                 struct _auth_chunk (*fetch_key)(const unsigned char *chal,
81                                                 const int csize))
82 {
83         struct _auth_obj ret = {0};
84         FILE *fp = NULL;
85         char *fn;
86         char *buf = NULL;
87         const char *wtokenid = NULL, *wuserid = NULL, *wnonce = NULL;
88         const char *hablob = NULL;
89         unsigned char *ablob = NULL;
90         int blobsize = 0;
91         char *newnonce;
92         int nonsize;
93         struct _auth_obj ao;
94
95         if ((fn = make_path(tokenid, userid)) == NULL) {
96                 ret.err = "authfile path impossible to build";
97                 return ret;
98         }
99         fp = fopen(fn, "r");
100         free(fn);
101         if (fp) {
102                 struct stat st;
103                 int fd = fileno(fp);
104
105                 if (fstat(fd, &st)) st.st_size = 2047;
106                 if (st.st_size > 2047) st.st_size = 2047;
107                 buf = alloca(st.st_size + 1);
108                 if (fgets(buf, st.st_size + 1, fp)) {
109                         char *p;
110
111                         p = &buf[strlen(buf) - 1];
112                         while (*p == '\n' || *p == '\r') *p-- = '\0';
113                         wtokenid = strtok(buf, ":");
114                         wuserid = strtok(NULL, ":");
115                         wnonce = strtok(NULL, ":");
116                         hablob = strtok(NULL, ":");
117                 } else {
118                         ret.err = strerror(errno);
119                 }
120                 fclose(fp);
121         }
122         if (ret.err) return ret;
123
124         if (hablob) {
125                 int hlen = strlen(hablob);
126                 if (hlen % 32 != 0) {
127                         ret.err = "error: auth string has wrong length";
128                 } else if (hlen !=
129                                 strspn(hablob, "0123456789abcdefABCDEF")) {
130                         ret.err = "error: auth string not hexadecimal";
131                 } else {
132                         int i;
133
134                         blobsize = hlen/2;
135                         ablob = alloca(blobsize);
136                         for (i = 0; i < blobsize; i++)
137                                 sscanf(&hablob[i*2], "%2hhx", &ablob[i]);
138                 }
139         }
140         if (ret.err) return ret;
141
142         nonsize = wnonce ? strlen(wnonce)*2 : 32;
143         if (nonsize < 32) nonsize = 32;
144         newnonce = alloca(nonsize);
145         if (wnonce) strcpy(newnonce, wnonce);
146         else memset(newnonce, 0, nonsize);
147         update_nonce(newnonce, nonsize);
148
149         ao = authobj(userid?userid:wuserid, password,
150                         wnonce, newnonce, secret, secsize,
151                         payload, paylsize, ablob, blobsize,
152                         fetch_key);
153
154         if (ao.err) {
155                 ret.err = ao.err;
156                 if (ao.data) memset(ao.data, 0, ao.datasize);
157                 if (ao.payload) memset(ao.payload, 0, ao.paylsize);
158                 if (ao.buffer) free(ao.buffer);
159                 return ret;
160         }
161
162         if ((fp = fopen(fn, "w"))) {
163                 int i;
164
165                 if (fprintf(fp, "%s:%s:%s:", tokenid?tokenid:wtokenid,
166                                 userid?userid:wuserid, newnonce) < 0) {
167                         ret.err = strerror(errno);
168                 } else for (i = 0; i < ao.datasize; i++)
169                     if (fprintf(fp, "%02x", ao.data[i]) < 0) {
170                         ret.err = strerror(errno);
171                 }
172                 fprintf(fp, "\n");
173                 if (fclose(fp) < 0) {
174                         ret.err = strerror(errno);
175                 }
176         } else {
177                 ret.err = strerror(errno);
178         }
179
180         if (!ret.err) {
181                 int bufsize = (wuserid?strlen(wuserid)+1:0) + ao.paylsize;
182                 if (bufsize) {
183                         if ((ret.buffer = malloc(bufsize)) == NULL) {
184                                 ret.err = "authfile malloc failed";
185                         } else {
186                                 unsigned char *p = ret.buffer;
187                                 if (wuserid) {
188                                         strcpy((char*)p, wuserid);
189                                         ret.data = p;
190                                         ret.datasize = strlen(wuserid)+1;
191                                         p += strlen(wuserid)+1;
192                                 }
193                                 if (ao.payload) {
194                                         memcpy(p, ao.payload, ao.paylsize);
195                                         ret.payload = p;
196                                         ret.paylsize = ao.paylsize;
197                                 }
198                         }
199                 }
200         }
201
202         if (ao.data) memset(ao.data, 0, ao.datasize);
203         if (ao.payload) memset(ao.payload, 0, ao.paylsize);
204         if (ao.buffer) free(ao.buffer);
205         return ret;
206 }