X-Git-Url: http://www.average.org/gitweb/?p=pam_pcsc_cr.git;a=blobdiff_plain;f=authfile.c;h=c43b2d655b0a6f2ee1d7640b648b0cc0cf9f35c7;hp=1e0826d4d8396505f7da71a1f8e3c0a8dad99dd1;hb=dc50dc3cbff0a815c899828c991e17f85458631c;hpb=4a5da38dfd8b0236564ffe6b70eab1eeb399fff0 diff --git a/authfile.c b/authfile.c index 1e0826d..c43b2d6 100644 --- a/authfile.c +++ b/authfile.c @@ -1,6 +1,33 @@ +/* +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 +#include +#include +#include #include #include #include @@ -9,112 +36,234 @@ #include #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. + */ + +static const char *template = "~/.pam_cr/auth"; + +void authfile_template(const char *str) +{ + template = str; +} + +static int path_size(const char *tokenid, const char *userid) +{ + const char *usub; + const char *p, *q; + struct passwd *pw; + + 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 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; + } + 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'; +} -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) +int parse(char * const buf, const int argc, const char *argv[const]) { - FILE *fp; - int rc; + char *p, *q; int i; - unsigned char key[20]; - int keysize = sizeof(key); - unsigned char mysecret[20]; - int mysecsize = sizeof(mysecret); - unsigned char myload[256]; - int myloadsize = sizeof(myload); - unsigned char *authobj = alloca(OBJSIZE); - int authsize = OBJSIZE; - char buf[512]; - char *oldtokenid = NULL, *oldid = NULL, *oldnonce = NULL, - *hauthobj = NULL; - unsigned char *oldauthobj = NULL; - int oldauthsize; - - if ((fp = fopen(fn, "r"))) { - if (fgets(buf, sizeof(buf), fp)) { - oldtokenid = strtok(buf, ":\r\n"); - oldid = strtok(NULL, ":\r\n"); - oldnonce = strtok(NULL, ":\r\n"); - hauthobj = strtok(NULL, ":\r\n"); - } else { - eprint("error reading from %s: %s\n", - fn, strerror(errno)); + + 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, + 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)) +{ + struct _auth_obj ret = {0}; + mode_t oldmask; + FILE *fp = NULL; + char *fn, *nfn; + int fnl; + struct stat st = {0}; + char *buf = 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 ((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"); + if (fp) { + 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)) { + 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 (hauthobj) { - int hlen; + if (ret.err) return ret; - hlen = strlen(hauthobj); + if (w.hablob) { + int hlen = strlen(w.hablob); if (hlen % 32 != 0) { - eprint("error: auth string has wrong length\n"); + ret.err = "error: auth string has wrong length"; } else if (hlen != - strspn(hauthobj, "0123456789abcdefABCDEF")) { - eprint("error: auth string not hexadecimal\n"); + strspn(w.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(&w.hablob[i*2], "%2hhx", &ablob[i]); } } + if (ret.err) return ret; + + nonsize = w.nonce ? strlen(w.nonce)*2 : 32; + if (nonsize < 32) nonsize = 32; + newnonce = alloca(nonsize); + if (w.nonce) strcpy(newnonce, w.nonce); + else memset(newnonce, 0, nonsize); + update_nonce(newnonce, nonsize); + + ao = authobj(userid?userid:w.userid, password, + w.nonce, newnonce, secret, secsize, + payload, paylsize, ablob, blobsize, + fetch_key); + + 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 (oldauthobj && password && !secret) { - unsigned char chal[64]; - int csize = sizeof(chal); - long rc; + oldmask = umask(077); + if ((fp = fopen(nfn, "w"))) { + int i; - rc = make_challenge(id, password, nonce, chal, &csize); - if (rc) { - eprint("cannot make challenge\n"); - return -1; + if (fprintf(fp, "%s:%s:%s:", tokenid?tokenid:w.tokenid, + userid?userid:w.userid, 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); } - rc = pcsc_cr(chal, csize, key, &keysize); - if (rc) { - eprint("error querying token: %s\n", pcsc_errstr(rc)); - return -1; + fprintf(fp, "\n"); + if (st.st_uid || st.st_gid) { + if (fchown(fileno(fp), st.st_uid, st.st_gid)) /*ign*/; } - rc = parse_authobj(key, keysize, oldauthobj, oldauthsize, - mysecret, &mysecsize, myload, &myloadsize); - if (rc) { - eprint("cannot parse old authobj: %d\n", rc); - return -1; + if (fclose(fp) < 0) { + ret.err = strerror(errno); } + } else { + ret.err = strerror(errno); } - - rc = make_authobj(id, password, nonce, mysecret, mysecsize, - payload, paysize, authobj, &authsize); - if (rc) { - eprint("make_authobj error %d\n", rc); - return -1; - } - fp = fopen(fn, "w"); - if (!fp) { - eprint("cannot open \"%s\": %s\n", - fn, strerror(errno)); - return -1; + (void)umask(oldmask); + if (ret.err) { + unlink(nfn); /* may not exist but no matter */ + } else if (rename(nfn, fn)) { + ret.err = strerror(errno); } - if (fprintf(fp, "%s:%s:%s:", tokenid, id, nonce) < 0) { - eprint("cannot write to \"%s\": %s\n", - fn, strerror(errno)); - return -1; - } - for (i = 0; i < authsize; i++) - if (fprintf(fp, "%02x", authobj[i]) < 0) { - eprint("cannot write to \"%s\": %s\n", - fn, strerror(errno)); - return -1; - } - fprintf(fp, "\n"); - if (fclose(fp) < 0) { - eprint("cannot close \"%s\": %s\n", - fn, strerror(errno)); - return -1; + + if (!ret.err) { + 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 (w.userid) { + strcpy((char*)p, w.userid); + ret.data = p; + 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+1; + } + } + } } - 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; }