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)
62 I know using these two functions and alloca() in between it ugly, but
63 I like the alternatives even less. =ec
66 static int path_size(const char *tokenid, const struct passwd *pw)
71 if ((p = strchr(template, '~')) != strrchr(template, '~')) return 0;
72 if ((q = strchr(template, '?')) != strrchr(template, '?')) return 0;
73 if (p && !pw) return 0;
74 if (q && !tokenid) return 0;
75 if (p == template) usub = pw->pw_dir;
76 else usub = pw->pw_name;
77 return strlen(template)+(p?strlen(usub):0)+(q?strlen(tokenid):0)+1;
81 make_path(char * const path, const char *tokenid, const struct passwd *pw)
88 for (p = template; *p; p++) switch (*p) {
91 if (p == template) strcpy(q, pw->pw_dir);
92 else strcpy(q, pw->pw_name);
96 if (!tokenid) return 1;
108 int parse(char * const buf, const int argc, const char *argv[const])
113 for (i = 0, p = buf; *p; p = q+1, i++) {
114 for (q = p; *q && *q != ':' && *q != '\r' && *q != '\n'; q++) ;
116 if (*p && i < argc) argv[i] = p;
121 struct _auth_obj authfile(const char *tokenid,
122 const char *userid, const char *password,
123 void (*update_nonce)(char *nonce, const int nonsize),
124 const unsigned char *secret, const int secsize,
125 const unsigned char *payload, const int paylsize,
126 struct _auth_chunk (*fetch_key)(const unsigned char *chal,
129 struct _auth_obj ret = {0};
130 const struct passwd *pw = NULL;
135 struct stat st = {0};
142 } w = {"", NULL, NULL, NULL};
143 unsigned char *ablob = NULL;
149 if (userid) pw = getpwnam(userid);
150 if ((fnl = path_size(tokenid, pw)) == 0) {
151 ret.err = "authfile path_size failed";
155 if (make_path(fn, tokenid, pw)) {
156 ret.err = "authfile make_path failed";
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 blobsize = strlen(w.hablob)*3/4;
178 ablob = alloca(blobsize);
179 if (b64_decode(w.hablob, ablob, &blobsize))
180 ret.err = "error: undecodeable auth string";
182 if (ret.err) return ret;
184 nonsize = w.nonce ? strlen(w.nonce)*2 : 32;
185 if (nonsize < 32) nonsize = 32;
186 newnonce = alloca(nonsize);
187 if (w.nonce) strcpy(newnonce, w.nonce);
188 else memset(newnonce, 0, nonsize);
189 update_nonce(newnonce, nonsize);
191 ao = authobj(userid?userid:w.userid, password,
192 w.nonce, newnonce, secret, secsize,
193 payload, paylsize, ablob, blobsize,
198 if (ao.data) memset(ao.data, 0, ao.datasize);
199 if (ao.payload) memset(ao.payload, 0, ao.paylsize);
200 if (ao.buffer) free(ao.buffer);
204 oldmask = umask(077);
205 if ((fp = fopen(nfn, "w"))) {
206 int bsize = ((ao.datasize-1)/3+1)*4+1;
207 char *b64 = alloca(bsize);
209 if (b64_encode(ao.data, ao.datasize, b64, &bsize)) {
210 ret.err = "error: could not encode auth string";
211 } else if (fprintf(fp, "%s:%s:%s:%s\n",
212 tokenid?tokenid:w.tokenid,
213 userid?userid:w.userid, newnonce, b64) < 0) {
214 ret.err = strerror(errno);
216 if (st.st_uid || st.st_gid) {
217 if (fchown(fileno(fp), st.st_uid, st.st_gid)) /*ign*/;
219 if (fclose(fp) < 0) {
220 ret.err = strerror(errno);
223 ret.err = strerror(errno);
225 (void)umask(oldmask);
227 unlink(nfn); /* may not exist but no matter */
228 } else if (rename(nfn, fn)) {
229 ret.err = strerror(errno);
233 int bufsize = (w.userid?strlen(w.userid)+1:0) + ao.paylsize + 1;
235 if ((ret.buffer = malloc(bufsize)) == NULL) {
236 ret.err = "authfile malloc failed";
238 unsigned char *p = ret.buffer;
240 strcpy((char*)p, w.userid);
242 ret.datasize = strlen(w.userid)+1;
243 p += strlen(w.userid)+1;
246 memcpy(p, ao.payload, ao.paylsize);
247 p[ao.paylsize] = '\0';
249 ret.paylsize = ao.paylsize+1;
255 if (ao.data) memset(ao.data, 0, ao.datasize);
256 if (ao.payload) memset(ao.payload, 0, ao.paylsize);
257 if (ao.buffer) free(ao.buffer);