wip redoing authfile
[pam_pcsc_cr.git] / authfile.c
index f1ac1509379c1ba15ee4250f89b4ca1113189fbd..9b1313c85ce63e6aa1964043d703bfbcd7c8fdcb 100644 (file)
@@ -3,6 +3,7 @@
 #endif
 #include <sys/types.h>
 #include <sys/stat.h>
+#include <pwd.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <alloca.h>
 #include "authobj.h"
 #include "authfile.h"
-#include "pcsc_cr.h"
 
-#define OBJSIZE 256
+/*
+ * Template string may contain zero or one '~' and zero or one '?'.
+ * '~' at the beginning of the template string is substituted with
+ * the home directory of the userid. In any other position it is
+ * substituted with the userid itself. '?' is substituted with the
+ * tokenid. There is no way to make the resulting path contain '~'
+ * or '?'. If there is more than one '~' or '?', or if the '~' is
+ * at the beginning but userid does not resolve via getpwnam, or
+ * the character to substitute is present but the argument is NULL,
+ * NULL is returned. Otherwise, malloc()'ed area containg the path
+ * string.
+ */
 
-int update_authfile(const char *fn, const char *tokenid, const char *id,
-                const char *password, const char *nonce,
-                const unsigned char *secret, const int secsize,
-                const unsigned char *payload, const int paysize)
+static char *template = "~/.pam_cr/auth";
+
+void authfile_template(char *str)
+{
+       template = str;
+}
+
+static char *make_path(const char *tokenid, const char *userid)
+{
+       const char *usub;
+       char *path;
+       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 == template) {
+               pw = getpwnam(userid);
+               if (!pw) return NULL;
+               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 '~':
+               strcpy(q, usub);
+               while (*q) q++;
+               break;
+       case '?':
+               strcpy(q, tokenid);
+               while (*q) q++;
+               break;
+       default:
+               *q++ = *p;
+               break;
+       }
+       *q = '\0';
+       return path;
+}
+
+struct _auth_obj authfile(const char *tokenid,
+               const char *userid, const char *password,
+               void (*update_nonce)(char *nonce, const int nonsize),
+               const unsigned char *secret, const int secsize,
+               const unsigned char *payload, const int paylsize,
+               struct _auth_chunk (*fetch_key)(const unsigned char *chal,
+                                               const int csize))
 {
-       FILE *fp;
-       int rc;
-       int i;
-       unsigned char key[20];
-       int keysize = sizeof(key);
-       unsigned char *mysecret = secret;
-       int mysecsize = secsize;
-       unsigned char *myload = payload;
-       int myloadsize = paysize;
-       unsigned char *authobj = NULL;
-       int authsize = 0;
+       struct _auth_obj ret = {0};
+       FILE *fp = NULL;
+       char *fn;
        char *buf = NULL;
-       char *mytokenid = NULL;
-       char *myid = NULL;
-       char *mynonce = NULL;
-       char *hauthobj = NULL;
-       unsigned char *oldauthobj = NULL;
-       int oldauthsize;
-
-       if ((fp = fopen(fn, "r"))) {
+       const char *wtokenid = NULL, *wuserid = NULL, *wnonce = NULL;
+       const char *hablob = NULL;
+       unsigned char *ablob = NULL;
+       int blobsize = 0;
+       char *newnonce;
+       int nonsize;
+       struct _auth_obj ao;
+
+       if ((fn = make_path(tokenid, userid)) == NULL) {
+               ret.err = "authfile path impossible to build";
+               return ret;
+       }
+       fp = fopen(fn, "r");
+       free(fn);
+       if (fp) {
                struct stat st;
                int fd = fileno(fp);
 
-               if (!fstat(fd, &st)) {
-                       eprint("fstat \"%s\" (fd %d) error: %s",
-                               fn, fd, strerror(errno));
-                       st.st_size = 2047;
-               }
+               if (fstat(fd, &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)) {
@@ -55,105 +110,97 @@ int update_authfile(const char *fn, const char *tokenid, const char *id,
 
                        p = &buf[strlen(buf) - 1];
                        while (*p == '\n' || *p == '\r') *p-- = '\0';
-                       mytokenid = strtok(buf, ":");
-                       myid = strtok(NULL, ":");
-                       mynonce = strtok(NULL, ":");
-                       hauthobj = strtok(NULL, ":");
+                       wtokenid = strtok(buf, ":");
+                       wuserid = strtok(NULL, ":");
+                       wnonce = strtok(NULL, ":");
+                       hablob = strtok(NULL, ":");
                } else {
-                       eprint("error reading from %s: %s",
-                               fn, strerror(errno));
+                       ret.err = strerror(errno);
                }
                fclose(fp);
        }
-       if (hauthobj) {
-               int hlen;
+       if (ret.err) return ret;
 
-               hlen = strlen(hauthobj);
+       if (hablob) {
+               int hlen = strlen(hablob);
                if (hlen % 32 != 0) {
-                       eprint("error: auth string has wrong length");
+                       ret.err = "error: auth string has wrong length";
                } else if (hlen !=
-                               strspn(hauthobj, "0123456789abcdefABCDEF")) {
-                       eprint("error: auth string not hexadecimal");
+                               strspn(hablob, "0123456789abcdefABCDEF")) {
+                       ret.err = "error: auth string not hexadecimal";
                } else {
-                       oldauthsize = hlen/2;
-                       oldauthobj = alloca(oldauthsize);
-                       for (i = 0; i < oldauthsize; i++)
-                               sscanf(&hauthobj[i*2], "%2hhx", &oldauthobj[i]);
+                       int i;
+
+                       blobsize = hlen/2;
+                       ablob = alloca(blobsize);
+                       for (i = 0; i < blobsize; i++)
+                               sscanf(&hablob[i*2], "%2hhx", &ablob[i]);
                }
        }
+       if (ret.err) return ret;
 
-       if (!secret) {
-               unsigned char chal[64];
-               int csize = sizeof(chal);
-               long rc;
+       nonsize = wnonce ? strlen(wnonce)*2 : 32;
+       if (nonsize < 32) nonsize = 32;
+       newnonce = alloca(nonsize);
+       if (wnonce) strcpy(newnonce, wnonce);
+       else memset(newnonce, 0, nonsize);
+       update_nonce(newnonce, nonsize);
 
-               if (!oldauthobj || !password) {
-                       eprint("if no secret given, old auth file must"
-                               " be present and password must be given");
-                       return -1;
-               }
-               rc = make_challenge(myid, password, mynonce, chal, &csize);
-               if (rc) {
-                       eprint("cannot make challenge");
-                       return -1;
-               }
-               rc = pcsc_cr(chal, csize, key, &keysize);
-               if (rc) {
-                       eprint("error querying token: %s", pcsc_errstr(rc));
-                       return -1;
-               }
-               mysecsize = oldauthsize;
-               mysecret = alloca(mysecsize);
-               myloadsize  = oldauthsize;
-               myload = alloca(myloadsize);
-               rc = parse_authobj(key, keysize, oldauthobj, oldauthsize,
-                       mysecret, &mysecsize, myload, &myloadsize);
-               if (rc) {
-                       eprint("cannot parse old authobj: %d", rc);
-                       return -1;
-               }
-       }
-       if (tokenid) mytokenid = tokenid;
-       if (id) myid = id;
-       if (nonce) mynonce = nonce;
-       else {
-               unsigned int prev = atoi(mynonce);
-               mynonce = alloca(16);
-               sprintf(mynonce, "%d", prev + 1);
-       }
+       ao = authobj(userid?userid:wuserid, password,
+                       wnonce, newnonce, secret, secsize,
+                       payload, paylsize, ablob, blobsize,
+                       fetch_key);
 
-       authsize = ((mysecsize + myloadsize + 16 + 4 * sizeof(short) - 1) /
-                       16 + 1) * 16;
-       authobj = alloca(authsize);
-       rc = make_authobj(myid, password, mynonce, mysecret, mysecsize,
-                       myload, myloadsize, authobj, &authsize);
-       if (rc) {
-               eprint("make_authobj error %d", rc);
-               return -1;
+       if (ao.err) {
+               ret.err = ao.err;
+               if (ao.data) memset(ao.data, 0, ao.datasize);
+               if (ao.payload) memset(ao.payload, 0, ao.paylsize);
+               if (ao.buffer) free(ao.buffer);
+               return ret;
        }
 
        if ((fp = fopen(fn, "w"))) {
-               if (fprintf(fp, "%s:%s:%s:", mytokenid, myid, mynonce) < 0) {
-                       eprint("cannot write to \"%s\": %s",
-                               fn, strerror(errno));
-                       return -1;
-               }
-               for (i = 0; i < authsize; i++)
-                   if (fprintf(fp, "%02x", authobj[i]) < 0) {
-                       eprint("cannot write to \"%s\": %s",
-                               fn, strerror(errno));
-                       return -1;
+               int i;
+
+               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) {
+                       ret.err = strerror(errno);
                }
                fprintf(fp, "\n");
                if (fclose(fp) < 0) {
-                       eprint("cannot close \"%s\": %s",
-                               fn, strerror(errno));
-                       return -1;
+                       ret.err = strerror(errno);
                }
        } else {
-               eprint("cannot open \"%s\": %s",
-                       fn, strerror(errno));
-               return -1;
+               ret.err = strerror(errno);
+       }
+
+       if (!ret.err) {
+               int bufsize = (wuserid?strlen(wuserid)+1:0) + ao.paylsize;
+               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);
+                                       ret.data = p;
+                                       ret.datasize = strlen(wuserid)+1;
+                                       p += strlen(wuserid)+1;
+                               }
+                               if (ao.payload) {
+                                       memcpy(p, ao.payload, ao.paylsize);
+                                       ret.payload = p;
+                                       ret.paylsize = ao.paylsize;
+                               }
+                       }
+               }
        }
-       return 0;
+
+       if (ao.data) memset(ao.data, 0, ao.datasize);
+       if (ao.payload) memset(ao.payload, 0, ao.paylsize);
+       if (ao.buffer) free(ao.buffer);
+       return ret;
 }