f6a7fea4057d7df1fc90a2f847762d2981c4c7bd
[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 const char *template = "~/.pam_cr/auth";
31
32 void authfile_template(const 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         const 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         const char *p;
62         char *q;
63         struct passwd *pw;
64
65         path[0] = '\0';
66         if (template[0] == '~') {
67                 pw = getpwnam(userid);
68                 if (!pw) return;
69                 usub = pw->pw_dir;
70         } else {
71                 usub = userid;
72         }
73         q = path;
74         for (p = template; *p; p++) switch (*p) {
75         case '~':
76                 strcpy(q, usub);
77                 while (*q) q++;
78                 break;
79         case '?':
80                 strcpy(q, tokenid);
81                 while (*q) q++;
82                 break;
83         default:
84                 *q++ = *p;
85                 break;
86         }
87         *q = '\0';
88 }
89
90 int parse(char * const buf, const int argc, const char *argv[const])
91 {
92         char *p, *q;
93         int i;
94
95         for (i = 0, p = buf; *p; p = q+1, i++) {
96                 for (q = p; *q && *q != ':' && *q != '\r' && *q != '\n'; q++) ;
97                 *q = '\0';
98                 if (*p && i < argc) argv[i] = p;
99         }
100         return i != argc;
101 }
102
103 struct _auth_obj authfile(const char *tokenid,
104                 const char *userid, const char *password,
105                 void (*update_nonce)(char *nonce, const int nonsize),
106                 const unsigned char *secret, const int secsize,
107                 const unsigned char *payload, const int paylsize,
108                 struct _auth_chunk (*fetch_key)(const unsigned char *chal,
109                                                 const int csize))
110 {
111         struct _auth_obj ret = {0};
112         mode_t oldmask;
113         FILE *fp = NULL;
114         char *fn, *nfn;
115         int fnl;
116         char *buf = NULL;
117         struct {
118                 const char *tokenid;
119                 const char *userid;
120                 const char *nonce;
121                 const char *hablob;
122         } w = {"", NULL, NULL, NULL};
123         unsigned char *ablob = NULL;
124         int blobsize = 0;
125         char *newnonce;
126         int nonsize;
127         struct _auth_obj ao;
128
129         if ((fnl = path_size(tokenid, userid)) == 0) {
130                 ret.err = "authfile path impossible to build";
131                 return ret;
132         }
133         fn = alloca(fnl);
134         make_path(fn, tokenid, userid);
135         nfn = alloca(fnl+32);
136         snprintf(nfn, fnl+32, "%s.%d.%ld", fn, (int)getpid(), (long)time(NULL));
137         fp = fopen(fn, "r");
138         if (fp) {
139                 struct stat st;
140                 int fd = fileno(fp);
141
142                 if (fstat(fd, &st)) st.st_size = 2047;
143                 if (st.st_size > 2047) st.st_size = 2047;
144                 buf = alloca(st.st_size + 1);
145                 if (!fgets(buf, st.st_size + 1, fp)) {
146                         ret.err = strerror(errno);
147                 } else if (parse(buf, sizeof(w)/sizeof(char*),
148                                         (const char ** const)&w)){
149                         ret.err = "error: unparseable auth file";
150                 }
151                 fclose(fp);
152         }
153         if (ret.err) return ret;
154
155         if (w.hablob) {
156                 int hlen = strlen(w.hablob);
157                 if (hlen % 32 != 0) {
158                         ret.err = "error: auth string has wrong length";
159                 } else if (hlen !=
160                                 strspn(w.hablob, "0123456789abcdefABCDEF")) {
161                         ret.err = "error: auth string not hexadecimal";
162                 } else {
163                         int i;
164
165                         blobsize = hlen/2;
166                         ablob = alloca(blobsize);
167                         for (i = 0; i < blobsize; i++)
168                                 sscanf(&w.hablob[i*2], "%2hhx", &ablob[i]);
169                 }
170         }
171         if (ret.err) return ret;
172
173         nonsize = w.nonce ? strlen(w.nonce)*2 : 32;
174         if (nonsize < 32) nonsize = 32;
175         newnonce = alloca(nonsize);
176         if (w.nonce) strcpy(newnonce, w.nonce);
177         else memset(newnonce, 0, nonsize);
178         update_nonce(newnonce, nonsize);
179
180         ao = authobj(userid?userid:w.userid, password,
181                         w.nonce, newnonce, secret, secsize,
182                         payload, paylsize, ablob, blobsize,
183                         fetch_key);
184
185         if (ao.err) {
186                 ret.err = ao.err;
187                 if (ao.data) memset(ao.data, 0, ao.datasize);
188                 if (ao.payload) memset(ao.payload, 0, ao.paylsize);
189                 if (ao.buffer) free(ao.buffer);
190                 return ret;
191         }
192
193         oldmask = umask(077);
194         if ((fp = fopen(nfn, "w"))) {
195                 int i;
196
197                 if (fprintf(fp, "%s:%s:%s:", tokenid?tokenid:w.tokenid,
198                                 userid?userid:w.userid, newnonce) < 0) {
199                         ret.err = strerror(errno);
200                 } else for (i = 0; i < ao.datasize; i++)
201                     if (fprintf(fp, "%02x", ao.data[i]) < 0) {
202                         ret.err = strerror(errno);
203                 }
204                 fprintf(fp, "\n");
205                 if (fclose(fp) < 0) {
206                         ret.err = strerror(errno);
207                 }
208         } else {
209                 ret.err = strerror(errno);
210         }
211         (void)umask(oldmask);
212         if (ret.err) {
213                 unlink(nfn); /* may not exist but no matter */
214         } else if (rename(nfn, fn)) {
215                 ret.err = strerror(errno);
216         }
217
218         if (!ret.err) {
219                 int bufsize = (w.userid?strlen(w.userid)+1:0) + ao.paylsize + 1;
220                 if (bufsize) {
221                         if ((ret.buffer = malloc(bufsize)) == NULL) {
222                                 ret.err = "authfile malloc failed";
223                         } else {
224                                 unsigned char *p = ret.buffer;
225                                 if (w.userid) {
226                                         strcpy((char*)p, w.userid);
227                                         ret.data = p;
228                                         ret.datasize = strlen(w.userid)+1;
229                                         p += strlen(w.userid)+1;
230                                 }
231                                 if (ao.payload) {
232                                         memcpy(p, ao.payload, ao.paylsize);
233                                         p[ao.paylsize] = '\0';
234                                         ret.payload = p;
235                                         ret.paylsize = ao.paylsize+1;
236                                 }
237                         }
238                 }
239         }
240
241         if (ao.data) memset(ao.data, 0, ao.datasize);
242         if (ao.payload) memset(ao.payload, 0, ao.paylsize);
243         if (ao.buffer) free(ao.buffer);
244         return ret;
245 }