mirror of https://github.com/ericonr/sndio.git
use a cookie for authentication
This commit is contained in:
parent
2a4059ae60
commit
0663ca6f54
|
@ -41,6 +41,7 @@ struct amsg {
|
||||||
#define AMSG_SETVOL 9 /* set volume */
|
#define AMSG_SETVOL 9 /* set volume */
|
||||||
#define AMSG_HELLO 10 /* say hello, check versions and so ... */
|
#define AMSG_HELLO 10 /* say hello, check versions and so ... */
|
||||||
#define AMSG_BYE 11 /* ask server to drop connection */
|
#define AMSG_BYE 11 /* ask server to drop connection */
|
||||||
|
#define AMSG_AUTH 12 /* send authentication cookie */
|
||||||
uint32_t cmd;
|
uint32_t cmd;
|
||||||
uint32_t __pad;
|
uint32_t __pad;
|
||||||
union {
|
union {
|
||||||
|
@ -79,6 +80,10 @@ struct amsg {
|
||||||
char opt[12]; /* profile name */
|
char opt[12]; /* profile name */
|
||||||
char who[12]; /* hint for leases */
|
char who[12]; /* hint for leases */
|
||||||
} hello;
|
} hello;
|
||||||
|
struct amsg_auth {
|
||||||
|
#define AMSG_COOKIELEN 16
|
||||||
|
uint8_t cookie[AMSG_COOKIELEN];
|
||||||
|
} auth;
|
||||||
} u;
|
} u;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -533,12 +533,14 @@ Note that the sequencer must use
|
||||||
.Va aucat:0
|
.Va aucat:0
|
||||||
as the MTC source, i.e. the audio server, not the audio player.
|
as the MTC source, i.e. the audio server, not the audio player.
|
||||||
.Sh ENVIRONMENT
|
.Sh ENVIRONMENT
|
||||||
.Bl -tag -width "AUDIODEVICE" -compact
|
.Bl -tag -width "AUCAT_COOKIE" -compact
|
||||||
.It Ev AUDIODEVICE
|
.It Ev AUDIODEVICE
|
||||||
.Xr sndio 7
|
.Xr sndio 7
|
||||||
audio device to use if the
|
audio device to use if the
|
||||||
.Fl f
|
.Fl f
|
||||||
option is not specified.
|
option is not specified.
|
||||||
|
.It Ev AUCAT_COOKIE
|
||||||
|
file containing a user's session cookie.
|
||||||
.El
|
.El
|
||||||
.Sh EXAMPLES
|
.Sh EXAMPLES
|
||||||
The following will mix and play two stereo streams,
|
The following will mix and play two stereo streams,
|
||||||
|
|
59
aucat/sock.c
59
aucat/sock.c
|
@ -96,15 +96,16 @@ struct ctl_ops ctl_sockops = {
|
||||||
sock_quitreq
|
sock_quitreq
|
||||||
};
|
};
|
||||||
|
|
||||||
unsigned sock_sesrefs = 0; /* connections to the session */
|
unsigned sock_sesrefs = 0; /* connections to the session */
|
||||||
uid_t sock_sesuid; /* owner of the session */
|
uint8_t sock_sescookie[AMSG_COOKIELEN]; /* owner of the session */
|
||||||
|
|
||||||
void
|
void
|
||||||
sock_close(struct file *arg)
|
sock_close(struct file *arg)
|
||||||
{
|
{
|
||||||
struct sock *f = (struct sock *)arg;
|
struct sock *f = (struct sock *)arg;
|
||||||
|
|
||||||
sock_sesrefs--;
|
if (f->pstate != SOCK_AUTH)
|
||||||
|
sock_sesrefs--;
|
||||||
pipe_close(&f->pipe.file);
|
pipe_close(&f->pipe.file);
|
||||||
if (f->dev) {
|
if (f->dev) {
|
||||||
dev_unref(f->dev);
|
dev_unref(f->dev);
|
||||||
|
@ -337,22 +338,13 @@ sock_new(struct fileops *ops, int fd)
|
||||||
close(fd);
|
close(fd);
|
||||||
return NULL;
|
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");
|
f = (struct sock *)pipe_new(ops, fd, "sock");
|
||||||
if (f == NULL) {
|
if (f == NULL) {
|
||||||
close(fd);
|
close(fd);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
f->pstate = SOCK_HELLO;
|
f->pstate = SOCK_AUTH;
|
||||||
f->mode = 0;
|
f->mode = 0;
|
||||||
f->opt = NULL;
|
f->opt = NULL;
|
||||||
f->dev = NULL;
|
f->dev = NULL;
|
||||||
|
@ -982,6 +974,23 @@ sock_midiattach(struct sock *f)
|
||||||
dev_midiattach(f->dev, rbuf, wbuf);
|
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
|
int
|
||||||
sock_hello(struct sock *f)
|
sock_hello(struct sock *f)
|
||||||
{
|
{
|
||||||
|
@ -1323,6 +1332,30 @@ sock_execmsg(struct sock *f)
|
||||||
f->rtodo = sizeof(struct amsg);
|
f->rtodo = sizeof(struct amsg);
|
||||||
f->rstate = SOCK_RMSG;
|
f->rstate = SOCK_RMSG;
|
||||||
break;
|
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:
|
case AMSG_HELLO:
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
if (debug_level >= 3) {
|
if (debug_level >= 3) {
|
||||||
|
|
15
aucat/sock.h
15
aucat/sock.h
|
@ -42,13 +42,14 @@ struct sock {
|
||||||
#define SOCK_WMSG 1 /* amsg being written */
|
#define SOCK_WMSG 1 /* amsg being written */
|
||||||
#define SOCK_WDATA 2 /* data chunk being written */
|
#define SOCK_WDATA 2 /* data chunk being written */
|
||||||
unsigned wstate; /* state of the write-end FSM */
|
unsigned wstate; /* state of the write-end FSM */
|
||||||
#define SOCK_HELLO 0 /* waiting for HELLO message */
|
#define SOCK_AUTH 0 /* waiting for AUTH message */
|
||||||
#define SOCK_INIT 1 /* parameter negotiation */
|
#define SOCK_HELLO 1 /* waiting for HELLO message */
|
||||||
#define SOCK_START 2 /* filling play buffers */
|
#define SOCK_INIT 2 /* parameter negotiation */
|
||||||
#define SOCK_READY 3 /* play buffers full */
|
#define SOCK_START 3 /* filling play buffers */
|
||||||
#define SOCK_RUN 4 /* attached to the mix / sub */
|
#define SOCK_READY 4 /* play buffers full */
|
||||||
#define SOCK_STOP 5 /* draining rec buffers */
|
#define SOCK_RUN 5 /* attached to the mix / sub */
|
||||||
#define SOCK_MIDI 6 /* raw byte stream (midi) */
|
#define SOCK_STOP 6 /* draining rec buffers */
|
||||||
|
#define SOCK_MIDI 7 /* raw byte stream (midi) */
|
||||||
unsigned pstate; /* one of the above */
|
unsigned pstate; /* one of the above */
|
||||||
unsigned mode; /* bitmask of MODE_XXX */
|
unsigned mode; /* bitmask of MODE_XXX */
|
||||||
struct aparams rpar; /* read (ie play) parameters */
|
struct aparams rpar; /* read (ie play) parameters */
|
||||||
|
|
|
@ -17,10 +17,12 @@
|
||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
#include <sys/un.h>
|
#include <sys/un.h>
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
#include <limits.h>
|
||||||
#include <poll.h>
|
#include <poll.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
@ -194,6 +196,70 @@ aucat_wdata(struct aucat *hdl, const void *buf, size_t len, unsigned wbpf, int *
|
||||||
return n;
|
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
|
int
|
||||||
aucat_open(struct aucat *hdl, const char *str, char *sock, unsigned mode, int nbio)
|
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
|
* say hello to server
|
||||||
*/
|
*/
|
||||||
AMSG_INIT(&hdl->wmsg);
|
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.cmd = AMSG_HELLO;
|
||||||
hdl->wmsg.u.hello.version = AMSG_VERSION;
|
hdl->wmsg.u.hello.version = AMSG_VERSION;
|
||||||
hdl->wmsg.u.hello.mode = mode;
|
hdl->wmsg.u.hello.mode = mode;
|
||||||
|
|
|
@ -146,6 +146,25 @@ MIDI port controlling the first
|
||||||
.Xr aucat 1
|
.Xr aucat 1
|
||||||
audio server.
|
audio server.
|
||||||
.El
|
.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
|
.Sh ENVIRONMENT
|
||||||
.Bl -tag -width "AUDIODEVICEXXX" -compact
|
.Bl -tag -width "AUDIODEVICEXXX" -compact
|
||||||
.It Ev AUDIODEVICE
|
.It Ev AUDIODEVICE
|
||||||
|
@ -154,6 +173,12 @@ no device chooser.
|
||||||
.It Ev MIDIDEVICE
|
.It Ev MIDIDEVICE
|
||||||
MIDI port to use if the application provides
|
MIDI port to use if the application provides
|
||||||
no MIDI port chooser.
|
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
|
.El
|
||||||
.Pp
|
.Pp
|
||||||
Environment variables are ignored by programs
|
Environment variables are ignored by programs
|
||||||
|
|
Loading…
Reference in New Issue