2 Copyright (c) 2013 Eugene Crosser
4 This software is provided 'as-is', without any express or implied
5 warranty. In no event will the authors be held liable for any damages
6 arising from the use of this software.
8 Permission is granted to anyone to use this software for any purpose,
9 including commercial applications, and to alter it and redistribute it
10 freely, subject to the following restrictions:
12 1. The origin of this software must not be misrepresented; you must
13 not claim that you wrote the original software. If you use this
14 software in a product, an acknowledgment in the product documentation
15 would be appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must
18 not be misrepresented as being the original software.
20 3. This notice may not be removed or altered from any source
27 #include <sys/types.h>
41 * Template string may contain zero or one '~' and zero or one '?'.
42 * '~' at the beginning of the template string is substituted with
43 * the home directory of the userid. In any other position it is
44 * substituted with the userid itself. '?' is substituted with the
45 * tokenid. There is no way to make the resulting path contain '~'
46 * or '?'. If there is more than one '~' or '?', or if the '~' is
47 * at the beginning but userid does not resolve via getpwnam, or
48 * the character to substitute is present but the argument is NULL,
49 * NULL is returned. Otherwise, malloc()'ed area containg the path
53 static const char *template = "~/.pam_cr/auth";
55 void authfile_template(const char *str)
60 static int path_size(const char *tokenid, const char *userid)
66 if ((p = strchr(template, '~')) != strrchr(template, '~')) return 0;
67 if ((q = strchr(template, '?')) != strrchr(template, '?')) return 0;
68 if (p && !userid) return 0;
69 if (q && !tokenid) return 0;
71 pw = getpwnam(userid);
77 return strlen(template) + p?strlen(usub):0 + q?strlen(tokenid):0 + 1;
81 make_path(char * const path, const char *tokenid, const char *userid)
89 if (template[0] == '~') {
90 pw = getpwnam(userid);
97 for (p = template; *p; p++) switch (*p) {
113 int parse(char * const buf, const int argc, const char *argv[const])
118 for (i = 0, p = buf; *p; p = q+1, i++) {
119 for (q = p; *q && *q != ':' && *q != '\r' && *q != '\n'; q++) ;
121 if (*p && i < argc) argv[i] = p;
126 struct _auth_obj authfile(const char *tokenid,
127 const char *userid, const char *password,
128 void (*update_nonce)(char *nonce, const int nonsize),
129 const unsigned char *secret, const int secsize,
130 const unsigned char *payload, const int paylsize,
131 struct _auth_chunk (*fetch_key)(const unsigned char *chal,
134 struct _auth_obj ret = {0};
145 } w = {"", NULL, NULL, NULL};
146 unsigned char *ablob = NULL;
152 if ((fnl = path_size(tokenid, userid)) == 0) {
153 ret.err = "authfile path impossible to build";
157 make_path(fn, tokenid, userid);
158 nfn = alloca(fnl+32);
159 snprintf(nfn, fnl+32, "%s.%d.%ld", fn, (int)getpid(), (long)time(NULL));
165 if (fstat(fd, &st)) st.st_size = 2047;
166 if (st.st_size > 2047) st.st_size = 2047;
167 buf = alloca(st.st_size + 1);
168 if (!fgets(buf, st.st_size + 1, fp)) {
169 ret.err = strerror(errno);
170 } else if (parse(buf, sizeof(w)/sizeof(char*),
171 (const char ** const)&w)){
172 ret.err = "error: unparseable auth file";
176 if (ret.err) return ret;
179 int hlen = strlen(w.hablob);
180 if (hlen % 32 != 0) {
181 ret.err = "error: auth string has wrong length";
183 strspn(w.hablob, "0123456789abcdefABCDEF")) {
184 ret.err = "error: auth string not hexadecimal";
189 ablob = alloca(blobsize);
190 for (i = 0; i < blobsize; i++)
191 sscanf(&w.hablob[i*2], "%2hhx", &ablob[i]);
194 if (ret.err) return ret;
196 nonsize = w.nonce ? strlen(w.nonce)*2 : 32;
197 if (nonsize < 32) nonsize = 32;
198 newnonce = alloca(nonsize);
199 if (w.nonce) strcpy(newnonce, w.nonce);
200 else memset(newnonce, 0, nonsize);
201 update_nonce(newnonce, nonsize);
203 ao = authobj(userid?userid:w.userid, password,
204 w.nonce, newnonce, secret, secsize,
205 payload, paylsize, ablob, blobsize,
210 if (ao.data) memset(ao.data, 0, ao.datasize);
211 if (ao.payload) memset(ao.payload, 0, ao.paylsize);
212 if (ao.buffer) free(ao.buffer);
216 oldmask = umask(077);
217 if ((fp = fopen(nfn, "w"))) {
220 if (fprintf(fp, "%s:%s:%s:", tokenid?tokenid:w.tokenid,
221 userid?userid:w.userid, newnonce) < 0) {
222 ret.err = strerror(errno);
223 } else for (i = 0; i < ao.datasize; i++)
224 if (fprintf(fp, "%02x", ao.data[i]) < 0) {
225 ret.err = strerror(errno);
228 if (fclose(fp) < 0) {
229 ret.err = strerror(errno);
232 ret.err = strerror(errno);
234 (void)umask(oldmask);
236 unlink(nfn); /* may not exist but no matter */
237 } else if (rename(nfn, fn)) {
238 ret.err = strerror(errno);
242 int bufsize = (w.userid?strlen(w.userid)+1:0) + ao.paylsize + 1;
244 if ((ret.buffer = malloc(bufsize)) == NULL) {
245 ret.err = "authfile malloc failed";
247 unsigned char *p = ret.buffer;
249 strcpy((char*)p, w.userid);
251 ret.datasize = strlen(w.userid)+1;
252 p += strlen(w.userid)+1;
255 memcpy(p, ao.payload, ao.paylsize);
256 p[ao.paylsize] = '\0';
258 ret.paylsize = ao.paylsize+1;
264 if (ao.data) memset(ao.data, 0, ao.datasize);
265 if (ao.payload) memset(ao.payload, 0, ao.paylsize);
266 if (ao.buffer) free(ao.buffer);