Skip to content

Instantly share code, notes, and snippets.

@jpmens
Created December 18, 2012 19:02
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jpmens/4330892 to your computer and use it in GitHub Desktop.
Save jpmens/4330892 to your computer and use it in GitHub Desktop.
/*
* sshpubkeys (C)2012 by Jan-Piet Mens
*/
#define FUSE_USE_VERSION 25
#include <fuse.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <ctype.h>
#include <stdlib.h>
#include <ldns/ldns.h>
#define DOMAINFILE "/domain"
char *domain;
/* FIXME: needs linked list */
#define MAXSERVERS 10
static char *servers[MAXSERVERS] = {
// "127.0.0.1"
"192.168.33.1"
};
int nservers = 1;
ldns_resolver *new_resolver()
{
ldns_resolver *res = NULL;
ldns_rdf *tmp;
int n;
res = ldns_resolver_new();
for (n = 0; n < nservers; n++) {
printf("----resolver: add %s\n", servers[n]);
tmp = ldns_rdf_new_frm_str(LDNS_RDF_TYPE_AAAA, servers[n]);
if (!tmp) {
tmp = ldns_rdf_new_frm_str(LDNS_RDF_TYPE_A, servers[n]);
}
if (!tmp) {
ldns_resolver_deep_free(res);
puts("failed to create resolver");
continue;
}
ldns_resolver_push_nameserver(res, tmp);
ldns_rdf_deep_free(tmp);
}
return res;
}
int getSSHkeys(char *qname, char *target, long targetlen)
{
ldns_resolver *res = NULL;
ldns_rdf *d = NULL;
ldns_pkt *p = NULL;
ldns_rr_list *txt = NULL;
char fqdn[BUFSIZ];
sprintf(fqdn, "%s.%s", qname, domain);
printf("********* getSSHkeys(%s)\n", fqdn);
if ((d = ldns_dname_new_frm_str(fqdn)) == NULL) {
return (1);
}
if ((res = new_resolver()) == NULL) {
return (1);
}
/* use the resolver to send a query for the mx
* records of the domain given on the command line
*/
p = ldns_resolver_query(res,
d,
LDNS_RR_TYPE_TXT,
LDNS_RR_CLASS_IN,
LDNS_RD);
ldns_rdf_deep_free(d);
if (!p) {
return (1);
} else {
/* retrieve the TXT records from the answer section of that
* packet
*/
txt = ldns_pkt_rr_list_by_type(p,
LDNS_RR_TYPE_TXT,
LDNS_SECTION_ANSWER);
if (!txt) {
fprintf(stderr, "%s NXDOMAIN or NODATA\n", fqdn);
ldns_pkt_free(p);
ldns_resolver_deep_free(res);
return (1);
} else {
long n, i;
char *str, *bp, *tp = target;
ldns_rdf *t;
ldns_rr_list_sort(txt);
// ldns_rr_list_print(stdout, txt);
if ((n = ldns_rr_list_rr_count(txt)) < 1) {
printf("Expecting at least 1 answer: got %ld\n", n);
return (1);
}
for (i = 0; i < ldns_rr_list_rr_count(txt); i++) {
int j;
for (j = 0; j < ldns_rr_rd_count(ldns_rr_list_rr(txt,i)); j++) {
t = ldns_rr_rdf(ldns_rr_list_rr(txt, i), j);
if (!t)
continue;
str = ldns_rdf2str(t);
for (bp = str; bp && *bp; bp++) {
if (*bp != '"')
*tp++ = *bp;
}
free(str);
*tp = 0;
}
*tp++ = '\n';
*tp = 0;
}
ldns_rr_list_deep_free(txt);
}
}
ldns_pkt_free(p);
ldns_resolver_deep_free(res);
return 0;
}
#define BLEN 10120
/* ------------------------------------------------------------------------ */
/*
* FIXME: getattr() reads the keys from DNS and read() does so too; should
* be a way to cache inbetween, but I'm not sure whether this code
* is threaded
*/
static int sshpub_getattr(const char *path, struct stat *stbuf)
{
int n;
char buf[BLEN];
long targetlen = BLEN;
memset(stbuf, 0, sizeof(struct stat));
printf("GETATTR [%s, %0X]\n", path, stbuf->st_mode);
time_t now = time(NULL);
stbuf->st_uid = geteuid();
stbuf->st_gid = getegid();
stbuf->st_ctime = now;
stbuf->st_mtime = now;
stbuf->st_atime = now;
if (strcmp(path, "/") == 0) {
stbuf->st_mode = S_IFDIR | 0755;
stbuf->st_nlink = 2;
return 0;
} else if (strcmp(path, DOMAINFILE) == 0) {
stbuf->st_mode = S_IFREG | 0444;
stbuf->st_nlink = 1;
stbuf->st_size = strlen(domain) + 1;
return 0;
}
for (n = 0; n < MAXSERVERS && servers[n]; n++) {
if (strcmp(path+1, servers[n]) == 0) {
stbuf->st_mode = S_IFDIR | 0755;
stbuf->st_nlink = 2;
stbuf->st_size = strlen(servers[n]);
return 0;
}
}
if (getSSHkeys((char *)path+1, buf, targetlen) == 0) {
stbuf->st_mode = S_IFREG | 0444;
stbuf->st_nlink = 1;
stbuf->st_size = strlen(buf);
return 0;
}
return -ENOENT;
}
static int sshpub_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
off_t offset, struct fuse_file_info *fi)
{
(void) offset;
(void) fi;
int n;
if(strcmp(path, "/") != 0)
return -ENOENT;
filler(buf, ".", NULL, 0);
filler(buf, "..", NULL, 0);
filler(buf, "domain", NULL, 0);
for (n = 0; n < MAXSERVERS && servers[n]; n++) {
char path[1024];
sprintf(path, "/%s", servers[n]);
filler(buf, path + 1, NULL, 0);
}
return 0;
}
static int sshpub_open(const char *path, struct fuse_file_info *fi)
{
/* Allow read/write on /domain */
if (strcmp(path, DOMAINFILE) == 0) {
return 0;
}
printf("++++++++++++ OPEN(%s, %0X)\n", path, fi->flags);
if ((fi->flags & 3) != O_RDONLY)
return -EACCES;
return 0;
}
static int sshpub_read(const char *path, char *buf, size_t size, off_t offset,
struct fuse_file_info *fi)
{
size_t len;
(void) fi;
char dnsbuf[BLEN];
long targetlen = BLEN;
printf("++++++++++++ READ(%s, %X, size=%i, offset=%jd)\n\n", path, fi->flags, size, offset);
if (strcmp(path, DOMAINFILE) == 0) {
sprintf(buf, "%s\n", domain);
return (strlen(buf));
}
if ((fi->flags & 3) != O_RDONLY)
return -EACCES;
if (getSSHkeys((char *)path+1, dnsbuf, targetlen) == 0) {
len = strlen(dnsbuf);
if (offset < len) {
if (offset + size > len)
size = len - offset;
memcpy(buf, dnsbuf + offset, size);
} else
size = 0;
printf("++++ read returns %i\n", size);
return size;
}
return -ENOENT;
}
static int sshpub_write(const char *path, const char *buf, size_t size, off_t offset,
struct fuse_file_info *fi)
{
printf("WRITE CALLED with [%s]\n", path);
if (strcmp(path, DOMAINFILE) == 0) {
char *p;
if (domain)
free(domain);
domain = malloc(size+1);
strncpy(domain, buf, size);
domain[size] = 0;
// domain = strndup(buf, size); // FIXME
/* rstrip */
for (p = domain + strlen(domain) - 1; p >= domain; p--) {
if (isspace(*p))
*p = '\0';
}
return size;
}
return -EACCES;
}
static int sshpub_mkdir(const char *path, mode_t mode)
{
printf("MKDIR CALLED with [%s]\n", path);
servers[nservers] = strdup(path + 1);
nservers++;
return 0;
}
static int sshpub_creat(const char *path, mode_t mode, struct fuse_file_info *fi)
{
printf("CREAT CALLED with [%s]\n", path);
if (strcmp(path, DOMAINFILE) == 0) {
return 0;
}
return -EACCES;
}
/*
* truncate() needed to support overwrite of /domains file
*/
int sshpub_truncate(const char *path, off_t offset)
{
printf("TRUNCATE CALLED with [%s]\n", path);
if (strcmp(path, DOMAINFILE) == 0) {
return 0;
}
return -EACCES;
}
/*
* FIXME: Program hangs on OS/X, possibly due to something wrong here
* "hangs" means bouncing ball for about 60s then program exits
*/
int sshpub_statfs(const char *path, struct statvfs *vfs)
{
printf("STATFS (%s)\n", path);
memset(vfs, sizeof(struct statvfs), 0);
vfs->f_bsize = 4096;
vfs->f_frsize = 4096; /* Fundamental file system block size */
vfs->f_blocks = 512; /* Blocks on FS in units of f_frsize */
vfs->f_bfree = 511; /* Free blocks */
vfs->f_bavail = 511; /* Blocks available to non-root */
vfs->f_files = 1024; /* Total inodes */
vfs->f_ffree = 1020; /* Free inodes */
vfs->f_favail = 1020; /* Free inodes for non-root */
vfs->f_fsid = 0xbeefeaed; /* Filesystem ID */
vfs->f_flag = 0x00L; /* Bit mask of values */
vfs->f_namemax = 128; /* Max file name length */
return 0;
}
static struct fuse_operations sshpub_oper = {
.getattr = sshpub_getattr,
.readdir = sshpub_readdir,
.open = sshpub_open,
.read = sshpub_read,
.mkdir = sshpub_mkdir,
.create = sshpub_creat,
.write = sshpub_write,
.truncate = sshpub_truncate,
.statfs = sshpub_statfs,
};
int main(int argc, char *argv[])
{
domain = strdup("localhost");
return fuse_main(argc, argv, &sshpub_oper);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment