Subversion Repositories planix.SVN

Rev

Rev 2 | Blame | Compare with Previous | Last modification | View Log | RSS feed

#include <u.h>
#include <libc.h>
#include <mp.h>
#include <ctype.h>
#include <libsec.h>
#include <fcall.h>
#include <thread.h>
#include <9p.h>
#include "netssh.h"

enum {
        Arbsz = 256,
};

static int
parsepubkey(char *s, RSApub *key, char **sp, int base)
{
        int n;
        char *host, *p, *z;

        z = nil;
        n = strtoul(s, &p, 10);
        host = nil;
        if(n < Arbsz || !isspace(*p)){          /* maybe this is a host name */
                host = s;
                s = strpbrk(s, " \t");
                if(s == nil)
                        return -1;
                z = s;
                *s++ = '\0';
                s += strspn(s, " \t");

                n = strtoul(s, &p, 10);
                if(n < Arbsz || !isspace(*p)){
                        if(z)
                                *z = ' ';
                        return -1;
                }
        }

        /* Arbsz is just a sanity check */
        if((key->ek = strtomp(p, &p, base, nil)) == nil ||
            (key->n = strtomp(p, &p, base, nil)) == nil ||
            (*p != '\0' && !isspace(*p)) || mpsignif(key->n) < Arbsz) {
                mpfree(key->ek);
                mpfree(key->n);
                key->ek = nil;
                key->n = nil;
                if(z)
                        *z = ' ';
                return -1;
        }
        if(host == nil){
                if(*p != '\0'){
                        p += strspn(p, " \t");
                        if(*p != '\0'){
                                host = emalloc9p(strlen(p)+1);
                                strcpy(host, p);
                        }
                }
                free(s);
        }
        *sp = host;
        return 0;
}

RSApub*
readpublickey(Biobuf *b, char **sp)
{
        char *s;
        RSApub *key;

        key = emalloc9p(sizeof(RSApub));
        if(key == nil)
                return nil;

        for (; (s = Brdstr(b, '\n', 1)) != nil; free(s))
                if(s[0] != '#'){
                        if(parsepubkey(s, key, sp, 10) == 0 ||
                            parsepubkey(s, key, sp, 16) == 0)
                                return key;
                        fprint(2, "warning: skipping line '%s'; cannot parse\n",
                                s);
                }
        free(key);
        return nil;
}

static int
match(char *pattern, char *aliases)
{
        char *s, *snext, *a, *anext, *ae;

        for(s = pattern; s && *s; s = snext){
                if((snext = strchr(s, ',')) != nil)
                        *snext++ = '\0';
                for(a = aliases; a && *a; a = anext){
                        if((anext = strchr(a, ',')) != nil){
                                ae = anext;
                                anext++;
                        }else
                                ae = a + strlen(a);
                        if(ae - a == strlen(s) && memcmp(s, a, ae - a) == 0)
                                return 0;
                }
        }
        return 1;
}

int
findkey(char *keyfile, char *host, RSApub *key)
{
        char *h;
        Biobuf *b;
        RSApub *k;
        int res;

        if ((b = Bopen(keyfile, OREAD)) == nil)
                return NoKeyFile;

        for (res = NoKey; res != KeyOk;) {
                if ((k = readpublickey(b, &h)) == nil)
                        break;
                if (match(h, host) == 0) {
                        if (mpcmp(k->n, key->n) == 0 &&
                            mpcmp(k->ek, key->ek) == 0)
                                res = KeyOk;
                        else
                                res = KeyWrong;
                }
                free(h);
                free(k->ek);
                free(k->n);
                free(k);
        }
        Bterm(b);
        return res;
}

int
replacekey(char *keyfile, char *host, RSApub *hostkey)
{
        int ret;
        char *h, *nkey, *p;
        Biobuf *br, *bw;
        Dir *d, nd;
        RSApub *k;

        ret = -1;
        d = nil;
        nkey = smprint("%s.new", keyfile);
        if(nkey == nil)
                return -1;

        if((br = Bopen(keyfile, OREAD)) == nil)
                goto out;
        if((bw = Bopen(nkey, OWRITE)) == nil){
                Bterm(br);
                goto out;
        }

        while((k = readpublickey(br, &h)) != nil){
                if(match(h, host) != 0)
                        Bprint(bw, "%s %d %.10M %.10M\n",
                                h, mpsignif(k->n), k->ek, k->n);
                free(h);
                rsapubfree(k);
        }
        Bprint(bw, "%s %d %.10M %.10M\n", host, mpsignif(hostkey->n),
                hostkey->ek, hostkey->n);
        Bterm(bw);
        Bterm(br);

        d = dirstat(nkey);
        if(d == nil){
                fprint(2, "new key file disappeared?\n");
                goto out;
        }

        p = strrchr(d->name, '.');
        if(p == nil || strcmp(p, ".new") != 0){
                fprint(2, "%s: new key file changed names? %s to %s\n",
                        argv0, nkey, d->name);
                goto out;
        }

        *p = '\0';
        nulldir(&nd);
        nd.name = d->name;
        if(remove(keyfile) < 0){
                fprint(2, "%s: error removing %s: %r\n", argv0, keyfile);
                goto out;
        }
        if(dirwstat(nkey, &nd) < 0){
                fprint(2, "%s: error renaming %s to %s: %r\n",
                        argv0, nkey, d->name);
                goto out;
        }
        ret = 0;
out:
        free(d);
        free(nkey);
        return ret;
}

int
appendkey(char *keyfile, char *host, RSApub *key)
{
        int fd, ret;

        ret = -1;
        if((fd = open(keyfile, OWRITE)) < 0){
                fd = create(keyfile, OWRITE, 0666);
                if(fd < 0){
                        fprint(2, "%s: can't open nor create %s: %r\n",
                                argv0, keyfile);
                        return -1;
                }
        }
        if(seek(fd, 0, 2) >= 0 &&
            fprint(fd, "%s %d %.10M %.10M\n", host, mpsignif(key->n),
             key->ek, key->n) >= 0)
                ret = 0;
        close(fd);
        return ret;
}