/*
* Client for nf2fa daemon
* Author: Catalin(ux) M. BOIE
*/
#define _GNU_SOURCE
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <arpa/inet.h>
#include "nf2fa_common.h"
#include "util.h"
#include "conf.h"
#include "totp.h"
#include "key.h"
#include "protocol.h"
static void help(void)
{
fprintf(stderr, "Usage: nf2afc <command> [options]\n");
fprintf(stderr, "Commands:\n");
fprintf(stderr, "\tenroll <name> - enroll new device(s)\n");
fprintf(stderr, "\tlist - list the enrollments\n");
fprintf(stderr, "\tunenroll <id> - unenroll a device\n");
}
int main(int argc, char *argv[])
{
unsigned char buf[8 * 4096];
int sock, r;
struct sockaddr_un sa_un;
socklen_t slen;
ssize_t n;
unsigned short i;
unsigned char cmd, err;
struct key k;
if (argc < 2) {
help();
return 1;
}
r = conf_load(CONF_FILE);
if (r == -1) {
fprintf(stderr, "Cannot load conf file!\n");
return 1;
}
i = 0;
if (strcmp(argv[1], "enroll") == 0) {
if (argc < 3) {
help();
return 1;
}
snprintf(k.name, sizeof(k.name), "%s", argv[2]);
while (1) {
char pass2[15];
r = read_password("Password (only 0-9 and a-f): ",
k.pass, sizeof(k.pass));
if (r == -1)
return 1;
r = read_password("Password (confirmation): ",
pass2, sizeof(pass2));
if (r == -1)
return 1;
r = key_pass_validate(k.pass, pass2);
if (r == -1) {
fprintf(stderr, "Error: %s\n", key_error());
continue;
}
break;
}
buf[i++] = NF2FA_CMD_ENROLL;
i += protocol_enqueue_string(buf + i, k.name);
i += protocol_enqueue_string(buf + i, k.pass);
} else if (strcmp(argv[1], "list") == 0) {
buf[i++] = NF2FA_CMD_LIST;
} else if (strcmp(argv[1], "unenroll") == 0) {
unsigned char id;
char sid[4];
if (argc < 3) {
help();
return 1;
}
id = strtoul(argv[2], NULL, 10);
sprintf(sid, "%03hhu", id);
if (strcmp(sid, argv[2]) != 0) {
fprintf(stderr,
"Error: Invalid id! Must be XXX (0 <= X <= 9)\n");
return 1;
}
buf[i++] = NF2FA_CMD_UNENROLL;
buf[i++] = id;
} else {
fprintf(stderr, "Error: unknown command!\n");
help();
return 1;
}
sock = socket(AF_UNIX, SOCK_STREAM, 0);
if (sock == -1) {
fprintf(stderr, "Error: cannot create unix socket: %s!\n",
strerror(errno));
return 1;
}
memset(&sa_un, 0, sizeof(struct sockaddr_un));
sa_un.sun_family = AF_UNIX;
strncpy(sa_un.sun_path, cfg_sock_path, sizeof(sa_un.sun_path) - 1);
slen = sizeof(struct sockaddr_un);
n = connect(sock, (struct sockaddr *) &sa_un, slen);
if (n == -1) {
fprintf(stderr, "Error: cannot connect to nf2fa daemon: %s!\n",
strerror(errno));
return 1;
}
n = send(sock, buf, i, 0);
if (n == -1) {
fprintf(stderr, "Error: cannot send data to nf2fa daemon: %s!\n",
strerror(errno));
return 1;
}
n = recv(sock, buf, sizeof(buf), 0);
if (n == -1) {
fprintf(stderr, "Error: cannot receive data from nf2fa daemon: %s!\n",
strerror(errno));
return 1;
}
if (n == 0) {
fprintf(stderr,
"Error: nf2fa daemon unexpectedly closed the connection.\n");
return 1;
}
if (n < 2) {
fprintf(stderr, "Error: nf2fa daemon sent partial data.\n");
return 1;
}
close(sock);
//printf("DEBUG: Received:\n");
//dump(buf, n);
i = 0;
cmd = buf[i++]; n--;
err = buf[i++]; n--;
while (err != 0x00) {
char error[64];
unsigned char len, len2;
len = buf[i++]; n--;
if (len > n)
break;
if (len > sizeof(error) - 1)
len2 = sizeof(error) - 1;
else
len2 = len;
memcpy(error, buf + i, len2);
error[len2] = '\0';
i += len; n -= len;
fprintf(stderr, "Error: %s!\n", error);
return 1;
}
while (cmd == NF2FA_CMD_ENROLL) {
char info[128];
unsigned char id, len, len2;
int r;
if (n < 2)
break;
id = buf[i++]; n--;
len = buf[i++]; n--;
if (len > n)
break;
if (len > sizeof(k.key) - 1)
len2 = sizeof(k.key) - 1;
else
len2 = len;
memcpy(k.key, buf + i, len2);
k.key[len2] = '\0';
i += len; n -= len;
printf("Key is %s (id %03hhu)."
" Scan the QR code below or type the key.\n",
k.key, id);
snprintf(info, sizeof(info), "%s - id %03hhu", k.name, id);
r = totp_text(k.key, info);
if (r != 0) {
fprintf(stderr, "Error: %s\n", totp_error());
return 1;
}
return 0;
}
while (cmd == NF2FA_CMD_LIST) {
int err = 1;
if (n == 0) {
printf("No enrollments.\n");
return 0;
}
printf("No Enroll time Name\n");
while (1) {
struct key k;
unsigned int x;
char t[32], itime[32];
struct tm tm;
unsigned char len, len2;
if (n == 0) {
err = 0;
break;
}
k.id = buf[i++]; n--;
len = buf[i++]; n--;
if (len > n)
break;
if (len > sizeof(k.name) - 1)
len2 = sizeof(k.name) - 1;
else
len2 = len;
memcpy(k.name, buf + i, len2);
k.name[len2] = '\0';
i += len; n -= len;
if (n < 4)
break;
memcpy(&x, buf + i, 4);
k.ts = ntohl(x);
i += 4; n -= 4;
if (n < 4)
break;
memcpy(&x, buf + i, 4); i += 4; n -= 4;
k.itime = ntohl(x);
if (n < 1)
break;
len = buf[i++]; n--;
if (len > n)
break;
if (len > sizeof(k.ip) - 1)
len2 = sizeof(k.ip) - 1;
else
len2 = len;
memcpy(k.ip, buf + i, len2);
k.ip[len2] = '\0';
i += len; n -= len;
// show
if (k.ts == 0) {
sprintf(t, "%-14s", "not used");
} else {
localtime_r(&k.ts, &tm);
strftime(t, sizeof(t), "%F %H:%M", &tm);
}
localtime_r(&k.itime, &tm);
strftime(itime, sizeof(itime), "%F %H:%M", &tm);
printf("%03hhu %s %s\n", k.id, itime, k.name);
if (k.ts)
printf(" Last access from IP %s on %s\n",
k.ip, t);
}
if (err)
break;
return 0;
}
while (cmd == NF2FA_CMD_UNENROLL) {
printf("Successfully un-enrolled.\n");
return 0;
}
fprintf(stderr, "Unknown or broken answer [0x%02hhx]!\n", buf[0]);
return 1;
}