html includes svg inside, adjust make rule
[pam_pcsc_cr.git] / authfile.c
index 9b1313c85ce63e6aa1964043d703bfbcd7c8fdcb..bb2d593deefa60a01c331d2adc1557d5e6820e19 100644 (file)
@@ -1,15 +1,40 @@
+/*
+Copyright (c) 2013 Eugene Crosser
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+    1. The origin of this software must not be misrepresented; you must
+    not claim that you wrote the original software. If you use this
+    software in a product, an acknowledgment in the product documentation
+    would be appreciated but is not required.
+
+    2. Altered source versions must be plainly marked as such, and must
+    not be misrepresented as being the original software.
+
+    3. This notice may not be removed or altered from any source
+    distribution.
+*/
+
 #ifdef HAVE_CONFIG_H
 # include "config.h"
 #endif
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <pwd.h>
+#include <time.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 #include <errno.h>
 #include <alloca.h>
+#include "base64.h"
 #include "authobj.h"
 #include "authfile.h"
 
  * string.
  */
 
-static char *template = "~/.pam_cr/auth";
+static const char *template = "~/.pam_cr/auth";
 
-void authfile_template(char *str)
+void authfile_template(const char *str)
 {
        template = str;
 }
 
-static char *make_path(const char *tokenid, const char *userid)
+static int path_size(const char *tokenid, const char *userid)
 {
        const char *usub;
-       char *path;
-       char *p, *q;
+       const char *p, *q;
        struct passwd *pw;
 
-       if ((p = strchr(template, '~')) != strrchr(template, '~')) return NULL;
-       if ((q = strchr(template, '?')) != strrchr(template, '?')) return NULL;
-       if (p && !userid) return NULL;
-       if (q && !tokenid) return NULL;
+       if ((p = strchr(template, '~')) != strrchr(template, '~')) return 0;
+       if ((q = strchr(template, '?')) != strrchr(template, '?')) return 0;
+       if (p && !userid) return 0;
+       if (q && !tokenid) return 0;
        if (p == template) {
                pw = getpwnam(userid);
-               if (!pw) return NULL;
+               if (!pw) return 0;
+               usub = pw->pw_dir;
+       } else {
+               usub = userid;
+       }
+       return strlen(template)+(p?strlen(usub):0)+(q?strlen(tokenid):0)+1;
+}
+
+static void
+make_path(char * const path, const char *tokenid, const char *userid)
+{
+       const char *usub;
+       const char *p;
+       char *q;
+       struct passwd *pw;
+
+       path[0] = '\0';
+       if (template[0] == '~') {
+               pw = getpwnam(userid);
+               if (!pw) return;
                usub = pw->pw_dir;
        } else {
                usub = userid;
        }
-       path = malloc(strlen(template) + p?strlen(usub):0 +
-                                               q?strlen(tokenid):0 + 1);
-       if (!path) return NULL;
        q = path;
        for (p = template; *p; p++) switch (*p) {
        case '~':
@@ -69,7 +109,19 @@ static char *make_path(const char *tokenid, const char *userid)
                break;
        }
        *q = '\0';
-       return path;
+}
+
+int parse(char * const buf, const int argc, const char *argv[const])
+{
+       char *p, *q;
+       int i;
+
+       for (i = 0, p = buf; *p; p = q+1, i++) {
+               for (q = p; *q && *q != ':' && *q != '\r' && *q != '\n'; q++) ;
+               *q = '\0';
+               if (*p && i < argc) argv[i] = p;
+       }
+       return i != argc;
 }
 
 struct _auth_obj authfile(const char *tokenid,
@@ -81,73 +133,64 @@ struct _auth_obj authfile(const char *tokenid,
                                                const int csize))
 {
        struct _auth_obj ret = {0};
+       mode_t oldmask;
        FILE *fp = NULL;
-       char *fn;
+       char *fn, *nfn;
+       int fnl;
+       struct stat st = {0};
        char *buf = NULL;
-       const char *wtokenid = NULL, *wuserid = NULL, *wnonce = NULL;
-       const char *hablob = NULL;
+       struct {
+               const char *tokenid;
+               const char *userid;
+               const char *nonce;
+               const char *hablob;
+       } w = {"", NULL, NULL, NULL};
        unsigned char *ablob = NULL;
        int blobsize = 0;
        char *newnonce;
        int nonsize;
        struct _auth_obj ao;
 
-       if ((fn = make_path(tokenid, userid)) == NULL) {
+       if ((fnl = path_size(tokenid, userid)) == 0) {
                ret.err = "authfile path impossible to build";
                return ret;
        }
+       fn = alloca(fnl);
+       make_path(fn, tokenid, userid);
+       nfn = alloca(fnl+32);
+       snprintf(nfn, fnl+32, "%s.%d.%ld", fn, (int)getpid(), (long)time(NULL));
        fp = fopen(fn, "r");
-       free(fn);
        if (fp) {
-               struct stat st;
-               int fd = fileno(fp);
-
-               if (fstat(fd, &st)) st.st_size = 2047;
+               if (fstat(fileno(fp), &st)) st.st_size = 2047;
                if (st.st_size > 2047) st.st_size = 2047;
                buf = alloca(st.st_size + 1);
-               if (fgets(buf, st.st_size + 1, fp)) {
-                       char *p;
-
-                       p = &buf[strlen(buf) - 1];
-                       while (*p == '\n' || *p == '\r') *p-- = '\0';
-                       wtokenid = strtok(buf, ":");
-                       wuserid = strtok(NULL, ":");
-                       wnonce = strtok(NULL, ":");
-                       hablob = strtok(NULL, ":");
-               } else {
+               if (!fgets(buf, st.st_size + 1, fp)) {
                        ret.err = strerror(errno);
+               } else if (parse(buf, sizeof(w)/sizeof(char*),
+                                       (const char ** const)&w)){
+                       ret.err = "error: unparseable auth file";
                }
                fclose(fp);
        }
        if (ret.err) return ret;
 
-       if (hablob) {
-               int hlen = strlen(hablob);
-               if (hlen % 32 != 0) {
-                       ret.err = "error: auth string has wrong length";
-               } else if (hlen !=
-                               strspn(hablob, "0123456789abcdefABCDEF")) {
-                       ret.err = "error: auth string not hexadecimal";
-               } else {
-                       int i;
-
-                       blobsize = hlen/2;
-                       ablob = alloca(blobsize);
-                       for (i = 0; i < blobsize; i++)
-                               sscanf(&hablob[i*2], "%2hhx", &ablob[i]);
-               }
+       if (w.hablob) {
+               blobsize = strlen(w.hablob)*3/4;
+               ablob = alloca(blobsize);
+               if (b64_decode(w.hablob, ablob, &blobsize))
+                       ret.err = "error: undecodeable auth string";
        }
        if (ret.err) return ret;
 
-       nonsize = wnonce ? strlen(wnonce)*2 : 32;
+       nonsize = w.nonce ? strlen(w.nonce)*2 : 32;
        if (nonsize < 32) nonsize = 32;
        newnonce = alloca(nonsize);
-       if (wnonce) strcpy(newnonce, wnonce);
+       if (w.nonce) strcpy(newnonce, w.nonce);
        else memset(newnonce, 0, nonsize);
        update_nonce(newnonce, nonsize);
 
-       ao = authobj(userid?userid:wuserid, password,
-                       wnonce, newnonce, secret, secsize,
+       ao = authobj(userid?userid:w.userid, password,
+                       w.nonce, newnonce, secret, secsize,
                        payload, paylsize, ablob, blobsize,
                        fetch_key);
 
@@ -159,41 +202,52 @@ struct _auth_obj authfile(const char *tokenid,
                return ret;
        }
 
-       if ((fp = fopen(fn, "w"))) {
-               int i;
+       oldmask = umask(077);
+       if ((fp = fopen(nfn, "w"))) {
+               int bsize = ((ao.datasize-1)/3+1)*4+1;
+               char *b64 = alloca(bsize);
 
-               if (fprintf(fp, "%s:%s:%s:", tokenid?tokenid:wtokenid,
-                               userid?userid:wuserid, newnonce) < 0) {
-                       ret.err = strerror(errno);
-               } else for (i = 0; i < ao.datasize; i++)
-                   if (fprintf(fp, "%02x", ao.data[i]) < 0) {
+               if (b64_encode(ao.data, ao.datasize, b64, &bsize)) {
+                       ret.err = "error: could not encode auth string";
+               } else if (fprintf(fp, "%s:%s:%s:%s\n",
+                               tokenid?tokenid:w.tokenid,
+                               userid?userid:w.userid, newnonce, b64) < 0) {
                        ret.err = strerror(errno);
                }
-               fprintf(fp, "\n");
+               if (st.st_uid || st.st_gid) {
+                       if (fchown(fileno(fp), st.st_uid, st.st_gid)) /*ign*/;
+               }
                if (fclose(fp) < 0) {
                        ret.err = strerror(errno);
                }
        } else {
                ret.err = strerror(errno);
        }
+       (void)umask(oldmask);
+       if (ret.err) {
+               unlink(nfn); /* may not exist but no matter */
+       } else if (rename(nfn, fn)) {
+               ret.err = strerror(errno);
+       }
 
        if (!ret.err) {
-               int bufsize = (wuserid?strlen(wuserid)+1:0) + ao.paylsize;
+               int bufsize = (w.userid?strlen(w.userid)+1:0) + ao.paylsize + 1;
                if (bufsize) {
                        if ((ret.buffer = malloc(bufsize)) == NULL) {
                                ret.err = "authfile malloc failed";
                        } else {
                                unsigned char *p = ret.buffer;
-                               if (wuserid) {
-                                       strcpy((char*)p, wuserid);
+                               if (w.userid) {
+                                       strcpy((char*)p, w.userid);
                                        ret.data = p;
-                                       ret.datasize = strlen(wuserid)+1;
-                                       p += strlen(wuserid)+1;
+                                       ret.datasize = strlen(w.userid)+1;
+                                       p += strlen(w.userid)+1;
                                }
                                if (ao.payload) {
                                        memcpy(p, ao.payload, ao.paylsize);
+                                       p[ao.paylsize] = '\0';
                                        ret.payload = p;
-                                       ret.paylsize = ao.paylsize;
+                                       ret.paylsize = ao.paylsize+1;
                                }
                        }
                }