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>
42 * Template string may contain zero or one '~' and zero or one '?'.
43 * '~' at the beginning of the template string is substituted with
44 * the home directory of the userid. In any other position it is
45 * substituted with the userid itself. '?' is substituted with the
46 * tokenid. There is no way to make the resulting path contain '~'
47 * or '?'. If there is more than one '~' or '?', or if the '~' is
48 * at the beginning but userid does not resolve via getpwnam, or
49 * the character to substitute is present but the argument is NULL,
50 * NULL is returned. Otherwise, malloc()'ed area containg the path
54 static const char *template = "~/.pam_cr/auth";
56 void authfile_template(const char *str)
61 static int path_size(const char *tokenid, const char *userid)
67 if ((p = strchr(template, '~')) != strrchr(template, '~')) return 0;
68 if ((q = strchr(template, '?')) != strrchr(template, '?')) return 0;
69 if (p && !userid) return 0;
70 if (q && !tokenid) return 0;
72 pw = getpwnam(userid);
78 return strlen(template)+(p?strlen(usub):0)+(q?strlen(tokenid):0)+1;
82 make_path(char * const path, const char *tokenid, const char *userid)
90 if (template[0] == '~') {
91 pw = getpwnam(userid);
98 for (p = template; *p; p++) switch (*p) {
114 int parse(char * const buf, const int argc, const char *argv[const])
119 for (i = 0, p = buf; *p; p = q+1, i++) {
120 for (q = p; *q && *q != ':' && *q != '\r' && *q != '\n'; q++) ;
122 if (*p && i < argc) argv[i] = p;
127 struct _auth_obj authfile(const char *tokenid,
128 const char *userid, const char *password,
129 void (*update_nonce)(char *nonce, const int nonsize),
130 const unsigned char *secret, const int secsize,
131 const unsigned char *payload, const int paylsize,
132 struct _auth_chunk (*fetch_key)(const unsigned char *chal,
135 struct _auth_obj ret = {0};
140 struct stat st = {0};
147 } w = {"", NULL, NULL, NULL};
148 unsigned char *ablob = NULL;
154 if ((fnl = path_size(tokenid, userid)) == 0) {
155 ret.err = "authfile path impossible to build";
159 make_path(fn, tokenid, userid);
160 nfn = alloca(fnl+32);
161 snprintf(nfn, fnl+32, "%s.%d.%ld", fn, (int)getpid(), (long)time(NULL));
164 if (fstat(fileno(fp), &st)) st.st_size = 2047;
165 if (st.st_size > 2047) st.st_size = 2047;
166 buf = alloca(st.st_size + 1);
167 if (!fgets(buf, st.st_size + 1, fp)) {
168 ret.err = strerror(errno);
169 } else if (parse(buf, sizeof(w)/sizeof(char*),
170 (const char ** const)&w)){
171 ret.err = "error: unparseable auth file";
175 if (ret.err) return ret;
178 blobsize = strlen(w.hablob)*3/4;
179 ablob = alloca(blobsize);
180 if (b64_decode(w.hablob, ablob, &blobsize))
181 ret.err = "error: undecodeable auth string";
183 if (ret.err) return ret;
185 nonsize = w.nonce ? strlen(w.nonce)*2 : 32;
186 if (nonsize < 32) nonsize = 32;
187 newnonce = alloca(nonsize);
188 if (w.nonce) strcpy(newnonce, w.nonce);
189 else memset(newnonce, 0, nonsize);
190 update_nonce(newnonce, nonsize);
192 ao = authobj(userid?userid:w.userid, password,
193 w.nonce, newnonce, secret, secsize,
194 payload, paylsize, ablob, blobsize,
199 if (ao.data) memset(ao.data, 0, ao.datasize);
200 if (ao.payload) memset(ao.payload, 0, ao.paylsize);
201 if (ao.buffer) free(ao.buffer);
205 oldmask = umask(077);
206 if ((fp = fopen(nfn, "w"))) {
207 int bsize = ((ao.datasize-1)/3+1)*4+1;
208 char *b64 = alloca(bsize);
210 if (b64_encode(ao.data, ao.datasize, b64, &bsize)) {
211 ret.err = "error: could not encode auth string";
212 } else if (fprintf(fp, "%s:%s:%s:%s\n",
213 tokenid?tokenid:w.tokenid,
214 userid?userid:w.userid, newnonce, b64) < 0) {
215 ret.err = strerror(errno);
217 if (st.st_uid || st.st_gid) {
218 if (fchown(fileno(fp), st.st_uid, st.st_gid)) /*ign*/;
220 if (fclose(fp) < 0) {
221 ret.err = strerror(errno);
224 ret.err = strerror(errno);
226 (void)umask(oldmask);
228 unlink(nfn); /* may not exist but no matter */
229 } else if (rename(nfn, fn)) {
230 ret.err = strerror(errno);
234 int bufsize = (w.userid?strlen(w.userid)+1:0) + ao.paylsize + 1;
236 if ((ret.buffer = malloc(bufsize)) == NULL) {
237 ret.err = "authfile malloc failed";
239 unsigned char *p = ret.buffer;
241 strcpy((char*)p, w.userid);
243 ret.datasize = strlen(w.userid)+1;
244 p += strlen(w.userid)+1;
247 memcpy(p, ao.payload, ao.paylsize);
248 p[ao.paylsize] = '\0';
250 ret.paylsize = ao.paylsize+1;
256 if (ao.data) memset(ao.data, 0, ao.datasize);
257 if (ao.payload) memset(ao.payload, 0, ao.paylsize);
258 if (ao.buffer) free(ao.buffer);