use a cookie for authentication

This commit is contained in:
Alexandre Ratchov 2011-04-16 18:30:24 +02:00
parent 2a4059ae60
commit 0663ca6f54
6 changed files with 160 additions and 21 deletions

View File

@ -41,6 +41,7 @@ struct amsg {
#define AMSG_SETVOL 9 /* set volume */
#define AMSG_HELLO 10 /* say hello, check versions and so ... */
#define AMSG_BYE 11 /* ask server to drop connection */
#define AMSG_AUTH 12 /* send authentication cookie */
uint32_t cmd;
uint32_t __pad;
union {
@ -79,6 +80,10 @@ struct amsg {
char opt[12]; /* profile name */
char who[12]; /* hint for leases */
} hello;
struct amsg_auth {
#define AMSG_COOKIELEN 16
uint8_t cookie[AMSG_COOKIELEN];
} auth;
} u;
};

View File

@ -533,12 +533,14 @@ Note that the sequencer must use
.Va aucat:0
as the MTC source, i.e. the audio server, not the audio player.
.Sh ENVIRONMENT
.Bl -tag -width "AUDIODEVICE" -compact
.Bl -tag -width "AUCAT_COOKIE" -compact
.It Ev AUDIODEVICE
.Xr sndio 7
audio device to use if the
.Fl f
option is not specified.
.It Ev AUCAT_COOKIE
file containing a user's session cookie.
.El
.Sh EXAMPLES
The following will mix and play two stereo streams,

View File

@ -96,15 +96,16 @@ struct ctl_ops ctl_sockops = {
sock_quitreq
};
unsigned sock_sesrefs = 0; /* connections to the session */
uid_t sock_sesuid; /* owner of the session */
unsigned sock_sesrefs = 0; /* connections to the session */
uint8_t sock_sescookie[AMSG_COOKIELEN]; /* owner of the session */
void
sock_close(struct file *arg)
{
struct sock *f = (struct sock *)arg;
sock_sesrefs--;
if (f->pstate != SOCK_AUTH)
sock_sesrefs--;
pipe_close(&f->pipe.file);
if (f->dev) {
dev_unref(f->dev);
@ -337,22 +338,13 @@ sock_new(struct fileops *ops, int fd)
close(fd);
return NULL;
}
if (sock_sesrefs == 0) {
/* start a new session */
sock_sesuid = uid;
} else if (uid != sock_sesuid) {
/* session owned by another user, drop connection */
close(fd);
return NULL;
}
sock_sesrefs++;
f = (struct sock *)pipe_new(ops, fd, "sock");
if (f == NULL) {
close(fd);
return NULL;
}
f->pstate = SOCK_HELLO;
f->pstate = SOCK_AUTH;
f->mode = 0;
f->opt = NULL;
f->dev = NULL;
@ -982,6 +974,23 @@ sock_midiattach(struct sock *f)
dev_midiattach(f->dev, rbuf, wbuf);
}
int
sock_auth(struct sock *f)
{
struct amsg_auth *p = &f->rmsg.u.auth;
if (sock_sesrefs == 0) {
/* start a new session */
memcpy(sock_sescookie, p->cookie, AMSG_COOKIELEN);
} else if (memcmp(sock_sescookie, p->cookie, AMSG_COOKIELEN) != 0) {
/* another session is active, drop connection */
return 0;
}
sock_sesrefs++;
f->pstate = SOCK_HELLO;
return 1;
}
int
sock_hello(struct sock *f)
{
@ -1323,6 +1332,30 @@ sock_execmsg(struct sock *f)
f->rtodo = sizeof(struct amsg);
f->rstate = SOCK_RMSG;
break;
case AMSG_AUTH:
#ifdef DEBUG
if (debug_level >= 3) {
sock_dbg(f);
dbg_puts(": AUTH message\n");
}
#endif
if (f->pstate != SOCK_AUTH) {
#ifdef DEBUG
if (debug_level >= 1) {
sock_dbg(f);
dbg_puts(": AUTH, bad state\n");
}
#endif
aproc_del(f->pipe.file.rproc);
return 0;
}
if (!sock_auth(f)) {
aproc_del(f->pipe.file.rproc);
return 0;
}
f->rstate = SOCK_RMSG;
f->rtodo = sizeof(struct amsg);
break;
case AMSG_HELLO:
#ifdef DEBUG
if (debug_level >= 3) {

View File

@ -42,13 +42,14 @@ struct sock {
#define SOCK_WMSG 1 /* amsg being written */
#define SOCK_WDATA 2 /* data chunk being written */
unsigned wstate; /* state of the write-end FSM */
#define SOCK_HELLO 0 /* waiting for HELLO message */
#define SOCK_INIT 1 /* parameter negotiation */
#define SOCK_START 2 /* filling play buffers */
#define SOCK_READY 3 /* play buffers full */
#define SOCK_RUN 4 /* attached to the mix / sub */
#define SOCK_STOP 5 /* draining rec buffers */
#define SOCK_MIDI 6 /* raw byte stream (midi) */
#define SOCK_AUTH 0 /* waiting for AUTH message */
#define SOCK_HELLO 1 /* waiting for HELLO message */
#define SOCK_INIT 2 /* parameter negotiation */
#define SOCK_START 3 /* filling play buffers */
#define SOCK_READY 4 /* play buffers full */
#define SOCK_RUN 5 /* attached to the mix / sub */
#define SOCK_STOP 6 /* draining rec buffers */
#define SOCK_MIDI 7 /* raw byte stream (midi) */
unsigned pstate; /* one of the above */
unsigned mode; /* bitmask of MODE_XXX */
struct aparams rpar; /* read (ie play) parameters */

View File

@ -17,10 +17,12 @@
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/un.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <poll.h>
#include <stdio.h>
#include <stdlib.h>
@ -194,6 +196,70 @@ aucat_wdata(struct aucat *hdl, const void *buf, size_t len, unsigned wbpf, int *
return n;
}
int
aucat_gencookie(unsigned char *cookie)
{
arc4random_buf(cookie, AMSG_COOKIELEN);
return 1;
}
void
aucat_savecookie(char *path, unsigned char *cookie)
{
int fd;
fd = open(path, O_WRONLY | O_TRUNC | O_CREAT, 0600);
if (fd < 0) {
DPERROR(path);
return;
}
if (write(fd, cookie, AMSG_COOKIELEN) < 0) {
DPERROR(path);
return;
}
close(fd);
}
int
aucat_loadcookie(unsigned char *cookie)
{
char buf[PATH_MAX], *path;
int fd, len, res;
path = issetugid() ? NULL : getenv("AUCAT_COOKIE");
if (path == NULL) {
path = issetugid() ? NULL : getenv("HOME");
if (path == NULL)
goto bad_gen;
snprintf(buf, PATH_MAX, "%s/.aucat_cookie", path);
path = buf;
}
fd = open(path, O_RDONLY);
if (fd < 0) {
if (errno != ENOENT)
DPERROR(path);
goto bad_gen;
}
len = read(fd, cookie, AMSG_COOKIELEN);
if (len < 0) {
DPERROR(path);
goto bad_close;
}
if (len != AMSG_COOKIELEN) {
DPRINTF("%s: short read\n", path);
goto bad_close;
}
close(fd);
return 1;
bad_close:
close(fd);
bad_gen:
res = aucat_gencookie(cookie);
if (path != NULL)
aucat_savecookie(path, cookie);
return res;
}
int
aucat_open(struct aucat *hdl, const char *str, char *sock, unsigned mode, int nbio)
{
@ -256,6 +322,13 @@ aucat_open(struct aucat *hdl, const char *str, char *sock, unsigned mode, int nb
* say hello to server
*/
AMSG_INIT(&hdl->wmsg);
hdl->wmsg.cmd = AMSG_AUTH;
if (!aucat_loadcookie(hdl->wmsg.u.auth.cookie))
goto bad_connect;
hdl->wtodo = sizeof(struct amsg);
if (!aucat_wmsg(hdl, &eof))
goto bad_connect;
AMSG_INIT(&hdl->wmsg);
hdl->wmsg.cmd = AMSG_HELLO;
hdl->wmsg.u.hello.version = AMSG_VERSION;
hdl->wmsg.u.hello.mode = mode;

View File

@ -146,6 +146,25 @@ MIDI port controlling the first
.Xr aucat 1
audio server.
.El
.Sh AUTHENTICATION
If a shared
.Xr aucat 1
or
.Xr midicat 1
server is running, for privacy reasons only one user may have
connections to it at a given time
(though the same user could have multiple connections to it).
Users are identified by their
.Em session cookie ,
which is automatically generated by audio or MIDI applications
upon the first connection to the server.
The cookie is stored in
.Pa "$HOME/.aucat_cookie"
and contains 128 bits of raw random data generated with
.Xr arc4random 3 .
.Pp
If a session needs to be shared between multiple users, they
can connect to the server using the same cookie.
.Sh ENVIRONMENT
.Bl -tag -width "AUDIODEVICEXXX" -compact
.It Ev AUDIODEVICE
@ -154,6 +173,12 @@ no device chooser.
.It Ev MIDIDEVICE
MIDI port to use if the application provides
no MIDI port chooser.
.It AUCAT_COOKIE
Path to file containing the session cookie to be used
when connecting to
.Xr aucat
or
.Xr midicat
.El
.Pp
Environment variables are ignored by programs