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
30 static const char *template = "~/.pam_cr/auth";
32 void authfile_template(const char *str)
37 static int path_size(const char *tokenid, const char *userid)
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;
48 pw = getpwnam(userid);
54 return strlen(template) + p?strlen(usub):0 + q?strlen(tokenid):0 + 1;
58 make_path(char * const path, const char *tokenid, const char *userid)
66 if (template[0] == '~') {
67 pw = getpwnam(userid);
74 for (p = template; *p; p++) switch (*p) {
90 int parse(char * const buf, const int argc, const char *argv[const])
95 for (i = 0, p = buf; *p; p = q+1, i++) {
96 for (q = p; *q && *q != ':' && *q != '\r' && *q != '\n'; q++) ;
98 if (*p && i < argc) argv[i] = p;
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,
111 struct _auth_obj ret = {0};
122 } w = {"", NULL, NULL, NULL};
123 unsigned char *ablob = NULL;
129 if ((fnl = path_size(tokenid, userid)) == 0) {
130 ret.err = "authfile path impossible to build";
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));
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";
153 if (ret.err) return ret;
156 int hlen = strlen(w.hablob);
157 if (hlen % 32 != 0) {
158 ret.err = "error: auth string has wrong length";
160 strspn(w.hablob, "0123456789abcdefABCDEF")) {
161 ret.err = "error: auth string not hexadecimal";
166 ablob = alloca(blobsize);
167 for (i = 0; i < blobsize; i++)
168 sscanf(&w.hablob[i*2], "%2hhx", &ablob[i]);
171 if (ret.err) return ret;
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);
180 ao = authobj(userid?userid:w.userid, password,
181 w.nonce, newnonce, secret, secsize,
182 payload, paylsize, ablob, blobsize,
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);
193 oldmask = umask(077);
194 if ((fp = fopen(nfn, "w"))) {
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);
205 if (fclose(fp) < 0) {
206 ret.err = strerror(errno);
209 ret.err = strerror(errno);
211 (void)umask(oldmask);
213 unlink(nfn); /* may not exist but no matter */
214 } else if (rename(nfn, fn)) {
215 ret.err = strerror(errno);
219 int bufsize = (w.userid?strlen(w.userid)+1:0) + ao.paylsize + 1;
221 if ((ret.buffer = malloc(bufsize)) == NULL) {
222 ret.err = "authfile malloc failed";
224 unsigned char *p = ret.buffer;
226 strcpy((char*)p, w.userid);
228 ret.datasize = strlen(w.userid)+1;
229 p += strlen(w.userid)+1;
232 memcpy(p, ao.payload, ao.paylsize);
233 p[ao.paylsize] = '\0';
235 ret.paylsize = ao.paylsize+1;
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);