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};
139 struct stat st = {0};
146 } w = {"", NULL, NULL, NULL};
147 unsigned char *ablob = NULL;
153 if ((fnl = path_size(tokenid, userid)) == 0) {
154 ret.err = "authfile path impossible to build";
158 make_path(fn, tokenid, userid);
159 nfn = alloca(fnl+32);
160 snprintf(nfn, fnl+32, "%s.%d.%ld", fn, (int)getpid(), (long)time(NULL));
163 if (fstat(fileno(fp), &st)) st.st_size = 2047;
164 if (st.st_size > 2047) st.st_size = 2047;
165 buf = alloca(st.st_size + 1);
166 if (!fgets(buf, st.st_size + 1, fp)) {
167 ret.err = strerror(errno);
168 } else if (parse(buf, sizeof(w)/sizeof(char*),
169 (const char ** const)&w)){
170 ret.err = "error: unparseable auth file";
174 if (ret.err) return ret;
177 int hlen = strlen(w.hablob);
178 if (hlen % 32 != 0) {
179 ret.err = "error: auth string has wrong length";
181 strspn(w.hablob, "0123456789abcdefABCDEF")) {
182 ret.err = "error: auth string not hexadecimal";
187 ablob = alloca(blobsize);
188 for (i = 0; i < blobsize; i++)
189 sscanf(&w.hablob[i*2], "%2hhx", &ablob[i]);
192 if (ret.err) return ret;
194 nonsize = w.nonce ? strlen(w.nonce)*2 : 32;
195 if (nonsize < 32) nonsize = 32;
196 newnonce = alloca(nonsize);
197 if (w.nonce) strcpy(newnonce, w.nonce);
198 else memset(newnonce, 0, nonsize);
199 update_nonce(newnonce, nonsize);
201 ao = authobj(userid?userid:w.userid, password,
202 w.nonce, newnonce, secret, secsize,
203 payload, paylsize, ablob, blobsize,
208 if (ao.data) memset(ao.data, 0, ao.datasize);
209 if (ao.payload) memset(ao.payload, 0, ao.paylsize);
210 if (ao.buffer) free(ao.buffer);
214 oldmask = umask(077);
215 if ((fp = fopen(nfn, "w"))) {
218 if (fprintf(fp, "%s:%s:%s:", tokenid?tokenid:w.tokenid,
219 userid?userid:w.userid, newnonce) < 0) {
220 ret.err = strerror(errno);
221 } else for (i = 0; i < ao.datasize; i++)
222 if (fprintf(fp, "%02x", ao.data[i]) < 0) {
223 ret.err = strerror(errno);
226 if (st.st_uid || st.st_gid) {
227 if (fchown(fileno(fp), st.st_uid, st.st_gid)) /*ign*/;
229 if (fclose(fp) < 0) {
230 ret.err = strerror(errno);
233 ret.err = strerror(errno);
235 (void)umask(oldmask);
237 unlink(nfn); /* may not exist but no matter */
238 } else if (rename(nfn, fn)) {
239 ret.err = strerror(errno);
243 int bufsize = (w.userid?strlen(w.userid)+1:0) + ao.paylsize + 1;
245 if ((ret.buffer = malloc(bufsize)) == NULL) {
246 ret.err = "authfile malloc failed";
248 unsigned char *p = ret.buffer;
250 strcpy((char*)p, w.userid);
252 ret.datasize = strlen(w.userid)+1;
253 p += strlen(w.userid)+1;
256 memcpy(p, ao.payload, ao.paylsize);
257 p[ao.paylsize] = '\0';
259 ret.paylsize = ao.paylsize+1;
265 if (ao.data) memset(ao.data, 0, ao.datasize);
266 if (ao.payload) memset(ao.payload, 0, ao.paylsize);
267 if (ao.buffer) free(ao.buffer);