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