Commit Graph

230 Commits (master)

Author SHA1 Message Date
Michael Santos c184ce68b2 Depend on libstrophe 0.9.2+
libstrophe 0.9.2 supports TLS certificate verification. Tested by:

* valid certificate: verified using strace that xmppipe is reading the
  system SSL cert store

* invalid certificate:

    sudo chmod 700 /usr/lib/ssl

  Verified xmppipe rejected the cert as invalid without the local CA
  root.

* valid certificate, invalid domain

  Verified a subdomain hosted on the XMPP node but not included in the
  TLS certificate is rejected.
7 years ago
Michael Santos 0ad3918c05 Fix options
Terminate long option list so xmppipe doesn't segfault when passed an
unknown option.

Correct the usage. Revert to using --ouput for the MUC name instead of
--stdout to avoid confusion.
7 years ago
Michael Santos 1d89c8cf96 cli: --output -> --stdout 7 years ago
Michael Santos 9fa747fd5c seccomp: allow llseek(2) for TLS verification
libstrophe 0.9.2 uses OpenSSL to validate TLS certs by checking against
the system cert store.

Allow llseek(2). Probably a better way of handling syscalls is to allow
classes of syscalls based on OpenBSD's pledge.
7 years ago
Dmitry Podgorny ef78dc7deb Replace libuuid with xmpp_uuid_gen()
xmpp_uuid_gen() appeared in libstrophe-0.9.0 and solves issues with
libuuid across different systems.
7 years ago
Michael Santos f201c6a483 Add support for long options 7 years ago
Michael Santos 41a6897bc1 Use native strtonum(3) on BSDs 7 years ago
Michael Santos 78978f725d sandbox: ignore return value of cap_rights_init(3) 7 years ago
Michael Santos 70423ab6a7 capsicum: remove duplicated process rlimit 7 years ago
Michael Santos 20f8b5904c Increment version for pre-connect sandbox 7 years ago
Michael Santos 81b4c2f4db seccomp sandbox: allow sendmmsg(2) 7 years ago
Michael Santos e3e3d0bcf9 seccomp: pre-connect sandbox
Preliminary pre-connect sandbox for Linux. Tested on 32-bit ARM,
requires testing on other platforms.
7 years ago
Michael Santos f734b5b77b freebsd: disable forking in preconnect sandbox 7 years ago
Michael Santos 6aa2cb528e sandbox: enforce rlimit restrictions before connect
Basic pre-connect sandbox: disable the ability for the xmppipe process
to fork.
7 years ago
Michael Santos 90c57630b6 openbsd: pre-connect pledge sandbox 7 years ago
Michael Santos c17b196053 sandbox: add a pre-connect sandbox
Add a sandbox enforced before options are parsed and the connection is
established to the XMPP server. This sandbox will allow network
operations.

The post-connect sandbox is unchanged and restricts operations to stdio.

The commit just adds the infrastructure for the pre-connect sandbox.
7 years ago
Michael Santos 9a87cd4e1b openbsd: fix compile error 7 years ago
Michael Santos 899e988a6f roomname: use UID in default roomname
Use the UID of the xmppipe process instead of the PID in the default
name. This allows many processes running under the same user on a host
to share the same output channel and makes it easier to pre-create the
MUC if the xmppipe XMPP user does not have MUC creation privs.
7 years ago
Michael Santos be90386d6e stream management: check h value in server response 7 years ago
Michael Santos f4d9184bac Add wrapper around strtonum(3) for options 7 years ago
Michael Santos cee9094fc8 options: use strtonum(3) to convert numbers
Limit the ranges for integers accepted as command line options.
7 years ago
Michael Santos f30f666d87 Convert last handled stanza using strtonum(3) 7 years ago
Michael Santos 58cb075664 state: set room name/resource before options 7 years ago
Michael Santos ad56bab3cc xmppipe_roomname: use define for hostname 7 years ago
Michael Santos 5cb6364cd0 Check gethostname(2) for error
Whether gethostname(2) returns an error depends on the implementation.
Some implementations:

* truncate the hostname if length is less than the hostname, with or
  without a trailing NULL

* return -1 if length is less than hostname

* return -1 if length is 0

Set a default name if gethostanme() returns error.
7 years ago
Michael Santos 6c4a14c712 sandbox/seccomp: fake close(2) return value
Some errors will cause the XMPP file descriptor to be closed before
xmppipe exits. Return EBADF if close is called since the process will
terminate anyway.
7 years ago
Michael Santos f51377428f Ignore invalid base64 messages
When base64 encoding is enabled, ignore any messages that fail base64
decoding.

Previously signed-unsigned integer conversion would cause the return
value of b64_pton() on error (a negative integer) to be converted to a
large value. The attempt to allocate this value would force xmppipe to
exit.
7 years ago
Michael Santos 85917f8ec4 sandbox/seccomp: print error message using err(3) 7 years ago
Michael Santos 7f0b5863c0 handle_stdin: use fd for nfds 7 years ago
Michael Santos 15926183a6 sandbox/seccomp: add more syscalls 7 years ago
Michael Santos 25f3441b33 README: add information about sandbox 7 years ago
Michael Santos 4a440def98 Enforce sandboxing 7 years ago
Michael Santos 2bf9415683 sandbox: enable capabilities sandbox on FreeBSD 7 years ago
Michael Santos 707d7cf19d Display enforced sandbox in verbose mode 7 years ago
Michael Santos 5917d03137 sandbox: Linux seccomp syscall filter
Add a BPF seccomp syscall filter on Linux. Not enabled by default. To
compile:

    XMPPIPE_SANDBOX=XMPPIPE_SANDBOX_SECCOMP make

The sandbox is derived from OpenSSH's seccomp sandbox by Will Drewry and
Kees Cook's tutorial on seccomp:

    http://outflux.net/teach-seccomp/
7 years ago
Michael Santos c346c863e4 sandbox: set number of allowed fd's
The number of file descriptors enforced by setrlimit() can now be set at
compile time using a flag. The flag defaults to 0 on Linux and -1
everywhere else:

    XMPPIPE_SANDBOX=XMPPIPE_SANDBOX_RLIMIT \
    XMPPIPE_SANDBOX_RLIMIT_NOFILE=-1 \
    make

The meaning of the XMPPIPE_SANDBOX_RLIMIT_NOFILE is:

* -1 : set rlim_cur/rlim_max to the lowest allocated file desciptor

* >=0: set rlim_cur/rlim_max to this number

On some platforms, setting rlim_cur below the value of the highest
allocated fd may interfere with polling. See commit a34d5766c5 for
details.
7 years ago
Michael Santos a34d5766c5 sandbox: basic rlimit sandbox
The rlimit sandbox disables forking processes and opening files.

The rlimit sandbox is not used by default yet. To compile it:

    XMPPIPE_SANDBOX=XMPPIPE_SANDBOX_RLIMIT make

The rlimit sandbox should work on any platform. However the interaction
of RLIMIT_NOFILE with poll(2) (and select(2)?) on some platforms (FreeBSD
but really any OS besides Linux) is problematic:

* opening a number of fd's, setting RLIMIT_NOFILE to 0, calling
  poll(2) on the fdset

  Linux: works
  FreeBSD: fails

* opening a number of fd's, setting RLIMIT_NOFILE to maxfd+1, calling
  poll(2) on the fdset

  Linux: works
  FreeBSD: works

The issue with the second option is that a library may have opened a
sequence of file descriptors then closed the lower numbered fd's:

    open() => 3
    open() => 4
    open() => 5
    close(3)
    close(4)
    maxfd = 5

RLIMIT_NOFILE would be set to 6 (stdin, stdout, stderr, 3, 4, 5) and the
sandbox would allow re-opening fd's 3 and 4.

One possible fix would be to run through the sequence of fd's before
entering the rlimit sandbox:

* test if the fd is closed
* if the fd is closed, dup2(STDIN_FILENO, fd)

Since the closed fd's are not part of the pollset, they will not be
polled and should be ignored.

Note we can't simply move maxfd to the lowest unused fd because
libstrophe maintains the fd number as internal, opaque state.

Empirically, the xmpp fd is always 3. Another option would be to abort
the process if the fd does not equal 3.
7 years ago
Michael Santos cc665538cb sandbox: stdio mode using pledge(2) on OpenBSD 7 years ago
Michael Santos a7d0ca7e47 Initial support for sandboxing
Prepare for sandboxing the xmppipe process by adding a function called
after all file descriptors are allocated.

The intent of the sandbox is to limit the xmppipe process to the role
of a component in a shell pipeline: reading from stdin, reading/writing
to the XMPP socket and writing to stdout. Any activity not involved with
using stdio should force the process to exit.

The sandbox function will vary based on the capabilities of the
platform. The default sandbox function does nothing.

Limitations of the sandbox:

Probably the biggest risk is in session establishment:
* the TLS handshake
* the XML parsing

The sandbox is enforced after the TLS connection is established, i.e.,
after the file descriptor for the XMPP session is allocated and so has no
effect on the TLS handshake or the initial XMPP handshake.

Possibly an initial sandbox could be setup for the connection phase
followed by a stricter sandbox for the stdio phase.
7 years ago
Michael Santos e20bca9bd1 const'ify all the things 7 years ago
Michael Santos 550eaf4e59 Check message id has been allocated 8 years ago
Michael Santos 04c05bd5f2 xmppipe: avoid memory leak from duplicate options 8 years ago
Michael Santos 2f2805d68a stdin: combine read error check 8 years ago
Michael Santos 16f03deff4 Fix typo 8 years ago
Michael Santos 877ecd5619 Flush stdout after print 8 years ago
Michael Santos 1e16b16c74 encoding: allow '@' and '/'
To make the JID easier to read, do not encode @ and /. Probably all the
RFC 3986 reserved characters can be passed through.
8 years ago
Michael Santos 07174101b4 encoding: remove useless lookup, sprintf 8 years ago
Michael Santos 072e8542ae alloc: log sizes on error 8 years ago
Michael Santos 7d4672a99a enomem: log function name
xmppipe occasionally crashes with an "allocation failure" message. Log
the function name for debugging.
8 years ago
Michael Santos e96427bb36 Fix signed/unsigned integer comparison 8 years ago
Michael Santos fc09ca4ff1 Be explicit with checks 9 years ago
Michael Santos d0792db2f8 darwin/sunos: include uuid.h 9 years ago
Michael Santos a392e836a0 Compile on BSDs
Use uuid_create(3) and uuid_to_string(3) to create the message id on
BSDs. Only tested on FreeBSD but should work on OpenBSD and NetBSD.

Add untested support for compiling on Solaris and Mac OS X:

* SmartOS has libuuid installed by default with rsyslog via pkgsrc

* Mac OS X has libuuid as part of libSystem:

http://lists.apple.com/archives/unix-porting/2009/Aug/msg00006.html
9 years ago
Michael Santos c814208bad Remove check for negative unsigned integer O.o 9 years ago
Michael Santos 69f97cd872 Add "to" field to message
Distinguish which user received a message. Possibly useful if multiple
sessions are logged in or someone directly messages the bot.
9 years ago
Michael Santos 4310fe0e6b Exit when disconnected 9 years ago
Michael Santos 633bc390cb Test string is NULL, not first character of string 9 years ago
Michael Santos e82fc91bfb xmpp_stanza_get_name: check for NULL 9 years ago
Michael Santos 37bd93160b Reset ping failures after any response
Use a fixed ping handler rather than scheduling a ping handler per ping
request.

If the XMPP client or server is busy, many outstanding ping requests
can be waiting, resulting in a number of ping handlers being queued.
Each of the ping handlers does the same thing (resets the failure count
to 0) but any dropped pings resulted in the handlers being queued
forever.
9 years ago
Michael Santos c3c83f9d72 Lower the default buffer size
For ejabberd and the mongooseim fork of ejabbered, 4097 bytes is around
the maximum message size before messages are dropped (the extra byte is
for libstrophe to put the terminating NULL. Otherwise, with a buffer
of 4096, the message will be broken up into 2 parts, with the second
consisting of the last byte of the message).

With base64 encoding, the message size will increase to 4097 * 4 / 3
bytes. When sending streams of binary data (cat'ing a file over XMPP,
ssh over XMPP, ...), stream management will get backlogged and messages
will disappear.

So halve the default buffer size. Is it too big for other jabber
servers? It is one of the mysteries but the "-b" option exists for those.
9 years ago
Michael Santos 1975057b6d -m -> -b
Rename the buffer size switch to something more memorable. Add a missing
break in the option processing switch statement.
9 years ago
Michael Santos 4c69352f19 stream management: flow control
Implement flow control by monitoring the number of unacked stream
requests and the number of unprocessed stanzas. If either exceeds a user
controlled limit, xmppipe will stop reading from stdin. Further stream
requests will be generated whenever the keepalive timeout is reached and
an XMPP ping is sent.

ejabberd/mongooseim will close the connection if there are about 5
unacked stream requests. xmppipe should probably do the same thing.

With these changes, binaries can be reliably (but slowly) sent over
XMPP. ssh over XMPP is also working reliably (with light testing). Because
of the increase in size due to base64 encoding, the buffer size needs
to be reduced so the encoded message is under 4k (-m 1024 is ok).
9 years ago
Michael Santos 5c14db6448 sm: request ack for every stanza
When stdin is closed with stream management enabled, xmppipe waits for
the ack to match the number of stanzas sent to ensure all the data has
been flushed to the server.

The server may not respond to every stream request and a stream request
message is generated only when a stanza is sent. Since stdin is closed,
the only traffic is the XMPP pings which are sent out every 60 seconds
by default. The result is, when stdin is closed, it may take several
minutes for xmppipe to exit.

Work around the problem by sending a stream management request for every
stanza.
9 years ago
Michael Santos 147e0d7c2b Consistenly check xmpp get ops for NULL values 9 years ago
Michael Santos 16ec036134 Fix format warnings 9 years ago
Michael Santos 3ae0a9f88f Transparently base64 encode data to MUC
Optionally base64 encode data sent to and from a conference, allowing
transfer of binary data.

This could also be accomplished by piping in base64 encoded data:

    base64 /bin/ls | xmppipe

However the base64 command does not work with streams of data.
9 years ago
Michael Santos bcf7c1b2e8 Check libstrophe funs for NULL return values
Ensure XML elements may exist in the stanza before attempting to use
them.
9 years ago
Michael Santos 4cbd8ec1c3 Add wrappers around libstrophe funs that may fail
Check for errors on libstrophe functions that return non-void. Some of
the funs have undocumented failure conditions (e.g.,
xmpp_stanza_copy()). Others return less than zero or enum values. To
account for future discoveries, use a wrapper instead of a macro.
9 years ago
Michael Santos ef7e414af6 Exit on connection failure
Should output a better error message based on the status returned to
handle_connection.
9 years ago
Michael Santos de7200819d Fix memory leak on NULL 9 years ago
Michael Santos cf501f5fde Optionally disable XMPP pings 9 years ago
Michael Santos fdab8a8901 Initial support for XEP-0198 stream management
Request stream management for the session by default. The rate of requests
and acks is controlled by the -I option. With stream management, the
client can check when all stanzas have been flushed to the server. For
example, the following works:

    cat /path/textfile | xmppipe -o transfer -r sender

    # on another system
    xmppipe -o transfer -r receiver > tmpfile

The behaviour is to always attempt enabling stream management. The XEP
is ambiguous about the response to an enable when stream management is
not offered. Presumably the server will respond with "failure".

So the code should probably have 2 checks:

* if stream management is offered as a feature, send an enable
* if the server responds with "enabled", handle requests
9 years ago
Michael Santos a233caf32c Set default keepalive failure limit 9 years ago
Michael Santos 246c148d0e Track ping replies
Set a limit on the number of pings without a reply and exit if the limit
is reached. Useful for detecing broken network connections due to
network changes.

The default configuration will take about 4 minutes to detect failure
(1 keepalive every 60 seconds, tolerate 3 failures).
9 years ago
Michael Santos 4cbee9c78c Optionally specify server address/port 9 years ago
Michael Santos c983814007 Exit on connection failure
No point shutting down nicely if the connection has failed: exit
immediately and print out an error.

The error is the same whether the connection has failed due to a DNS
lookup, TCP connection or authentication failure. Running in debug mode
can show the reason for failure. libstrophe has an enum indicating the
connection state; maybe this can be used for better error reporting.
9 years ago
Michael Santos 353951298e Generate a unique ID per message
Add a ID per message. Seems to fix the duplicate messages seen with MUCs
on the Conversations android client whenever the network is switched
(e.g., from wifi to cell).

Introduce a dependency on Linux's libuuid which introduces portability
issues. However, it should be simple to add support for the BSD UUID
interface.

libuuid was used because it is a fast and simple way of generating
a unique id.  Realistically, the ID only needs to be unique within
the MUC and so could probably be replaced with an sprintf() combining
time of day, PID and a random number.
9 years ago
Michael Santos 4325445536 Exit on presence errors
Force exiting on nick conflicts. xmppipe could generate a unique resource
(e.g., xmppipe-<pid>) but it prevents accidentally having two instances
running in a room.
9 years ago
Michael Santos 849507ad5d mucxxx -> muc_xxx; ERR -> XMPPIPE_ERR 9 years ago
Michael Santos 3d79e9fda8 Forward stdin over XMPP 9 years ago