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