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. There is no way to make the
46 * resulting path contain '~'. If there is more than one '~', or if
47 * the '~' is at the beginning but userid does not resolve via
48 * getpwnam, or '~' is present but the argument is NULL, path_size
49 * returns 0, and make_path returns 1.
52 static const char *template = "~/.pam_cr/auth";
54 void authfile_template(const char *str)
60 I know using these two functions and alloca() in between it ugly, but
61 I like the alternatives even less. =ec
64 static int path_size(const struct passwd *pw)
68 if ((p = strchr(template, '~')) != strrchr(template, '~')) return 0;
69 if (p && !pw) return 0;
70 if (p == template) return strlen(template)+strlen(pw->pw_dir)+1;
71 else return strlen(template)+strlen(pw->pw_name)+1;
75 make_path(char * const path, const struct passwd *pw)
82 for (p = template; *p; p++) switch (*p) {
85 if (p == template) strcpy(q, pw->pw_dir);
86 else strcpy(q, pw->pw_name);
97 int parse(char * const buf, const int argc, const char *argv[const])
102 for (i = 0, p = buf; *p; p = q+1, i++) {
103 for (q = p; *q && *q != ':' && *q != '\r' && *q != '\n'; q++) ;
105 if (*p && i < argc) argv[i] = p;
110 struct _auth_obj authfile(const char *userid, const char *password,
111 void (*update_nonce)(char *nonce, const int nonsize),
112 const unsigned char *secret, const int secsize,
113 const unsigned char *payload, const int paylsize,
114 struct _auth_chunk (*fetch_key)(const unsigned char *chal,
117 struct _auth_obj ret = {0};
118 const struct passwd *pw = NULL;
123 struct stat st = {0};
129 } w = {NULL, NULL, NULL};
130 unsigned char *ablob = NULL;
136 if (userid) pw = getpwnam(userid);
137 if ((fnl = path_size(pw)) == 0) {
138 ret.err = "authfile path_size failed";
142 if (make_path(fn, pw)) {
143 ret.err = "authfile make_path failed";
146 nfn = alloca(fnl+32);
147 snprintf(nfn, fnl+32, "%s.%d.%ld", fn, (int)getpid(), (long)time(NULL));
150 if (fstat(fileno(fp), &st)) st.st_size = 2047;
151 if (st.st_size > 2047) st.st_size = 2047;
152 buf = alloca(st.st_size + 1);
153 if (!fgets(buf, st.st_size + 1, fp)) {
154 ret.err = strerror(errno);
155 } else if (parse(buf, sizeof(w)/sizeof(char*),
156 (const char ** const)&w)){
157 ret.err = "error: unparseable auth file";
161 if (ret.err) return ret;
164 blobsize = strlen(w.hablob)*3/4;
165 ablob = alloca(blobsize);
166 if (b64_decode(w.hablob, ablob, &blobsize))
167 ret.err = "error: undecodeable auth string";
169 if (ret.err) return ret;
171 nonsize = w.nonce ? strlen(w.nonce)*2 : 32;
172 if (nonsize < 32) nonsize = 32;
173 newnonce = alloca(nonsize);
174 if (w.nonce) strcpy(newnonce, w.nonce);
175 else memset(newnonce, 0, nonsize);
176 update_nonce(newnonce, nonsize);
178 ao = authobj(userid?userid:w.userid, password,
179 w.nonce, newnonce, secret, secsize,
180 payload, paylsize, ablob, blobsize,
185 if (ao.data) memset(ao.data, 0, ao.datasize);
186 if (ao.payload) memset(ao.payload, 0, ao.paylsize);
187 if (ao.buffer) free(ao.buffer);
191 oldmask = umask(077);
192 if ((fp = fopen(nfn, "w"))) {
193 int bsize = ((ao.datasize-1)/3+1)*4+2; /* why +2 ??? */
194 char *b64 = alloca(bsize);
196 if (b64_encode(ao.data, ao.datasize, b64, &bsize)) {
197 ret.err = "error: could not encode auth string";
198 } else if (fprintf(fp, "%s:%s:%s\n",
199 userid?userid:w.userid, newnonce, b64) < 0) {
200 ret.err = strerror(errno);
202 if (st.st_uid || st.st_gid) {
203 if (fchown(fileno(fp), st.st_uid, st.st_gid)) /*ign*/;
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);