Compare commits

...

103 Commits
0.7 ... master

Author SHA1 Message Date
Hiltjo Posthuma a34d8fb432 slock.1: use standard wording for options
Remove the OPTIONS section and add an EXIT STATUS section.
2023-10-06 11:57:31 +02:00
Hiltjo Posthuma e8bca65d62 write version to stdout like the man page says 2023-10-06 11:50:11 +02:00
Hiltjo Posthuma ca6f30f621 slock.1: improve man page
* Fix all lint warnings.
* Remove "Op Ar arg..." in the description. It looks ugly.
* No need to set -offset left for .Bd literal.
2023-10-06 11:48:40 +02:00
Hiltjo Posthuma 2fec14c567 config.mk: no need to set CC 2023-10-06 11:41:19 +02:00
Hiltjo Posthuma 5678764412 Makefile: be verbose and remove options
Some things to improve at some point:

* Respect system/packaging CFLAGS/LDFLAGS (don't hardcode -Os -Wall -pedantic,
  -s, etc).
2023-10-06 11:39:40 +02:00
Hiltjo Posthuma aecfb3f680 update LICENSE 2023-10-06 11:39:31 +02:00
Hiltjo Posthuma 4f045545a2 bump version to 1.5 2022-10-04 19:45:14 +02:00
Hiltjo Posthuma 265704d736 Makefile: explicit_bzero.c was copied twice (GNU make gives a warning) 2022-10-04 19:44:47 +02:00
Tobias Stoeckmann 35633d4567 Properly clear the last entered character
When enter is pressed, passwd[len] will be set to '\0'. Pressing
backspace is supposed to remove the last entered character.

But currently, the clearing has an off-by-one, as in setting
passwd[len] to '\0' just like enter would do.

You can also verify it by imagining len=1 and that it's impossible to
clear passwd[0] by pressing backspace with the current code.

Signed-off-by: Tobias Stoeckmann <tobias@stoeckmann.org>
2017-03-25 21:51:29 +01:00
Markus Teich 2d2a21a90a rm trailing whitespace in README 2016-11-23 00:29:18 +01:00
Markus Teich 325581b935 syntax fix 2016-11-23 00:28:43 +01:00
Markus Teich 0ff0d9f7a7 there can only be one window in the event 2016-11-23 00:28:25 +01:00
Bob Uhl 7a604ec1fa Fix resize with multiple monitors and portrait mode
When connecting/disconnecting a portrait monitor, the
XRRScreenChangeNotifyEvent height & width are reversed due to the XRandR
rotation; detect this and DTRT.
2016-11-23 00:26:51 +01:00
Markus Teich fa11589584 bump version 2016-11-20 01:07:11 +01:00
Markus Teich d5da84cf5a add arg.h and util.h to Makefile 2016-11-20 01:01:47 +01:00
Markus Teich ae96836f90 clarify colors in config.def.h 2016-11-20 00:29:41 +01:00
Markus Teich 7d31ff65a0 move config.h inclusion after type declarations 2016-11-20 00:29:01 +01:00
Laslo Hunhold 597469541c Unboolify slock.c 2016-10-18 00:29:41 +02:00
FRIGN c96e725df0 Use explicit strcmp() instead of inlining it
Makes it a tad more readable; the previous "optimization" will be done
by the compiler anyway.
2016-10-10 20:40:13 +02:00
FRIGN 6a1bd89635 No need for oldc to be static 2016-10-10 20:40:13 +02:00
FRIGN b099d2fd18 Use NUL character constant explicitly 2016-10-10 20:40:13 +02:00
FRIGN 0a43b78d00 Keep the line-lengths at bay
This makes the code more readable and prevents wraparounds in the
editor.
2016-10-10 20:39:33 +02:00
FRIGN bd069b08c5 Add a section on security considerations
The section on security considerations sheds some light on the problems that we
can't solve within slock but which the user has to solve in his X configuration.
2016-09-28 22:01:58 +02:00
Markus Teich 2d85c5baed remove confusing DPMS comment
FRIGN on hackers@suckless.org:
What has been bugging me for quite a while is this DPMS comment that was added
there for no reason. Every sane mind would agree that fiddling with DPMS makes
no sense whatsoever. When I slock, my screen turns off after 10 minutes. So, if
I don't like that, I disable DPMS. If I do, I just fiddle around with my mouse a
bit and get the slock promt.
2016-09-26 13:28:09 +02:00
Markus Teich a98fba8971 error out early on crypt() fail 2016-09-23 19:08:39 +02:00
FRIGN dc2e8e839e Stop using $USER for shadow entries
This was extremely bad practice, effectively making the program behave
different depending on which architecture you are running it on.

OpenBSD offers getpwuid_shadow, but there is no getspuid for getspnam,
so we resort to using the pw_name entry in the struct passwd we filled
earlier.

This prevents slock from crashing when $USER is empty (easy to do). If
you want to run slock as a different user, don't use

	$ USER="tom" slock

but doas or sudo which were designed for this purpose.
2016-09-23 18:54:56 +02:00
FRIGN 9a617db716 Rename getpw() and pws to gethash() and hash 2016-09-23 18:51:40 +02:00
FRIGN b00f444a4e Remove cleanup and deglobalize and rework data structures
The cleanup removal is a joint-venture with Markus. We assume the X server does
the cleanup, so we don't need it. The idea is that the fds are closed at exit
and thus already indicate to the X server that the client has quit. Analogously
the same applies to freeing memory sections previously allocated for the X
server.

We love XXXXXL burgers and therefore removed
XUngrabPointer
XUngrabKeyboard
XFreeColors
XFreePixmap
XDestroyWindow
Lines of Code.

For a project like slock there is no need to carry around global state. By
moving the three structures to main() it is now clear which functions modify
which state, greatly improving the readability of the code, especially given
slock is a suid program.
2016-09-23 18:39:01 +02:00
Quentin Rameau cd3c546c37 config.mk: be more explicative about FLAGS
Group each *FLAG with its description and add a NetBSD specific.
2016-09-08 23:36:07 +02:00
FRIGN 22eba05f36 Ensure Polyphemus-Mitigation and properly drop privileges
Don't hide privilege drops inside readpw() and actually make it
configurable what you are dropping to in config.h.

The privilege drop comes after opening the Display because the
user "nobody" with "nogroup" can't do that.

So why do I call this strategy the Polyphemus-Mitigation?

"""
After the giant returns in the evening and eats two more of the men,
Odysseus offers Polyphemus some strong and undiluted wine given to him
earlier on his journey. Drunk and unwary, the giant asks Odysseus his
name, promising him a guest-gift if he answers. Odysseus tells him
"Οὖτις", which means "nobody" and Polyphemus promises to eat this
"Nobody" last of all. With that, he falls into a drunken sleep. Odysseus
had meanwhile hardened a wooden stake in the fire and now drives it into
Polyphemus' eye. When Polyphemus shouts for help from his fellow giants,
saying that "Nobody" has hurt him, they think Polyphemus is being
afflicted by divine power and recommend prayer as the answer.
"""

(source: https://en.wikipedia.org/wiki/Polyphemus)
2016-09-08 00:36:45 +02:00
Quentin Rameau 04143fd68d Unify how we check passwords between different OSes 2016-09-07 13:10:25 +02:00
Markus Teich 9698224090 make error message prefix consistent 2016-09-07 10:04:06 +02:00
Markus Teich a55594fdd6 increasing for loops are idiomatic 2016-09-02 11:49:02 +02:00
Quentin Rameau 03a8717991 Localize running and failure inside readpw()
They are only needed there, so don't make them global.
2016-09-02 10:50:32 +02:00
Quentin Rameau 39fb855aa1 Move screen unlocking inside cleanup() 2016-09-02 10:50:32 +02:00
Quentin Rameau e378f735d8 Re-introduce the waiting loop for input grabbing
We actually “need” to wait a little for input to be released before
locking for cases where slock is spawned from other graphical
applications using keybindings.
This undoes the misbehaviour I introduced in c2f9757, sorry for the mess.
2016-09-02 10:50:32 +02:00
Quentin Rameau 1f66885fbf Add cleanup() to do free(locks) + XCloseDisplay() 2016-09-02 10:50:32 +02:00
FRIGN 137f0076c2 Refactor dontkillme()
- Use file pointers instead of raw I/O, inspired by Kernel code.
- Use OOM_SCORE_ADJ_MIN from linux/oom.h instead of working with
  magic values.
- Stricter error checking and descriptive error messages.

The reasoning for using the constant rather than magic values lies
in the fact that this ensures people get the message.
With "-1000", a code reviewer would question if that is really the
lowest possible number or just an arbitrary value.
The kernel ABI probably won't change, but even in the case, we wouldn't
have to modify the code. The OOM killer only is guaranteed to not
kill you if you have OOM_SCORE_ADJ_MIN.
2016-08-31 01:06:44 +02:00
FRIGN a9eddbd94f Convert manpage to mandoc and fix usage
In all honor, the previous usage was formally more correct, but for the
sake of consistency across all the tools having the v-flag, I separated
it from the command-string.

Also, make use of the mandoc macros for the manpage. This makes it
easier to maintain, extend and change in the future.
2016-08-31 01:04:11 +02:00
Markus Teich d8bec0f6fd fix CVE-2016-6866 2016-08-31 00:59:06 +02:00
Markus Teich b87bfa2343 Update bsd-auth string.
Thanks to Hiltjo for discovering this.
2016-08-31 00:56:13 +02:00
Quentin Rameau c2f975773d Exit as soon as possible on input grabbing error
We want to know at once if slock failed or not to lock the screen, not
seing a black screen for a whole second (or two) and then die.
Thanks to ^7heo for reporting this.
2016-08-30 19:54:26 +02:00
FRIGN 3bb868e408 Refactor main()
- Add arg.h and fix usage
	Given slock is suid we don't want to have half-measures in place to
	parse the arguments in case the code is changed in the future with
	somebody not paying enough attention.

	Also, fix the usage string output to be more consistent across the
	suckless toolbase and make it reflect the manpage entry.

- Comments
	Use proper block comments and add/change them where necessary
	to help in studying the code.

- Error messages
	Consistently prepend them with "slock:" and fix wording and
	do a proper cleanup before quitting (XCloseDisplay and free
	the locks), making the die() semantics consistent with st's.

- getpwuid() error reporting
	Properly present an error message if getpwuid() fails.

- fork() error reporting
	Properly present an error message if fork() fails. If we cannot
	close the connection within the fork context we abort the
	operation and report an error.

- execvp() error handling
	If execvp fails, we cannot call die() afterwards as this implies
	calling exit(). We must use _exit() to prevent the libc from
	doing now "illegal" cleanup-work.
2016-08-22 23:22:20 +02:00
Hiltjo Posthuma a7afade170 clear passwords with explicit_bzero
Make sure to explicitly clear memory that is used for password input. memset
is often optimized out by the compiler.

Brought to attention by the OpenBSD community, see:
https://marc.info/?t=146989502600003&r=1&w=2
Thread subject: x11/slock: clear passwords with explicit_bzero

Changes:

- explicit_bzero.c import from libressl-portable.
- Makefile: add COMPATSRC for compatibility src.
- config.mk: add separate *BSD section in config.mk to simply uncomment it on
  these platforms.
2016-08-13 09:58:00 +02:00
Markus Teich 65b8d52788 Revert "No need for usage()"
This reverts most of commit a6dc051e37 and fixes
some related stuff:

- keep spelling fixes from original commit
- make -h and -v also work when followed by more arguments
- any unknown flag prints usage
- fix output of -v to display "slock: version 1.3" instead of "slock: slock-1.3"
2016-02-15 14:25:17 +01:00
Markus Teich ec46680fe1 revert using argv0 and minor fixup
- use hardcoded "slock" instead of argv[0]
- add "slock: " to fprintf calls, where it was missing
- revert `argc--, argv++` shifting
2016-02-15 14:00:56 +01:00
FRIGN b02c4d452a Use argv0 instead of passing "slock:" to die every time 2016-02-14 02:13:54 +01:00
FRIGN a6dc051e37 No need for usage()
There are 2 arguments why -v and -h are broken:

	1) if you are running off git, -v will show the last stable
           release, effectively making this option useless.
           people running stable versions leave open an attack surface
           this way in case there are vulnerabilities found.
           99% of the people are also using package managers to keep
           their software up to date, instead of running $TOOL -v to
           check how old it is.
        2) -h is a sad excuse for not just looking at the manual page
           (man 1 slock). Given we accept a post_lock_command, we can't
           be as liberal and just intercept certain flags.

I changed the manpage to reflect this change.
2016-02-14 02:00:14 +01:00
FRIGN bdcbeab0d8 Clarify config.def.h
Clear up the wording a bit and explain what failonclear means.
2016-02-14 01:33:18 +01:00
FRIGN 3abbffa493 Simplify the oom-taming-function
There really is no need to source a defined variable from a linux
header. The OOM-rank ranges from -1000 to 1000, so we can safely
hardcode -1000, which is a sane thing to do given slock is suid and
we don't want to play around too much here anyway.

On another notice, let's not forget that this still is a shitty
heuristic. The OOM-killer still can kill us (thus I also changed
the wording in the error-message. We do not disable the OOM-killer,
we're just hiding.
2016-02-14 01:28:37 +01:00
Markus Teich 6a52a85a1a add slock.1 man page 2016-02-11 16:51:12 +01:00
FRIGN c28ac1ebf1 Update license year
It actually was 2014 and not 2015.
2016-02-11 16:30:52 +01:00
FRIGN aa6a385341 Don't forget the E-Mail 2016-02-11 16:29:26 +01:00
FRIGN 6adeb1281e Add myself to License
forgot that a while ago
2016-02-11 16:28:41 +01:00
Markus Teich f6582b68b0 update copyright year in -v output 2016-02-11 16:26:12 +01:00
Markus Teich bfafc91da7 prepare 1.3 release 2016-02-11 16:23:48 +01:00
Markus Teich 32149e4043 remove .hgtags 2016-02-11 16:22:34 +01:00
Markus Teich 1766ecdfd4 add hint for suid to oom error message 2016-01-29 22:11:18 +01:00
Markus Teich 9dfe0ce531 error messages on grab failure 2016-01-18 16:49:15 +01:00
Markus Teich 55e827af0f code style fix 2016-01-18 16:47:41 +01:00
Markus Teich e867c38123 add option to run command after screen is locked 2015-12-26 19:27:01 +01:00
David Phillips b95ee111c7 Don't change to failure colour on success 2015-08-27 06:16:25 +02:00
David Phillips 0edbd2e016 Slightly safer OOM killer disablement in linux 2015-07-06 11:15:07 +02:00
Markus Teich 754195f8d7 rework setting window color 2015-05-08 18:07:05 +02:00
Markus Teich 10d4e479c5 consistently use () with sizeof 2015-05-08 16:44:18 +02:00
Nick Currier b1289f30b7 Option to not show failure color on clear 2015-05-08 16:44:06 +02:00
Markus Teich f5ef1b8eb5 resize lockscreen window after Xrandr resize 2015-04-01 23:25:47 +02:00
David Phillips f2ea92c3dd Blank the screen with color 0, add third color for failed logins
- Adds another color in config.def.h, COLOR_INIT
- Renames the colours from numerical ones to ones with meaningful names;
  COLOR_INPUT for when there is content in the input buffer and COLOR_EMPTY
  for when the input buffer has been cleared (backspaced or a failed attempt).
- Ensures XFreeColors frees the right number of colours. This is now derived
  from the size of `Lock->colors` rather than being an integer literal.
- Makes slock exhibit the behaviour described by Markus

The default colours are the same as the ones slock currently uses, with the
exception of the new color, which I have set to red, as it indicates someone
has either failed an attempt to unlock, or that they have entered input and
erased it all.
2015-04-01 23:13:11 +02:00
Anselm R Garbe a31b919111 applied Dimitris' style patch from Dec'14, with some minor modifications 2015-01-27 22:16:52 +01:00
Anselm R Garbe 66e31556db fixed usage string 2014-12-22 11:17:45 +01:00
Anselm R Garbe 44ce161c13 applied sin's patch and prepared new release 2014-12-22 11:16:26 +01:00
sin 4339b507af end{pw,sp}ent() can only be called after get{pw,sp}ent()
Calling them unconditionally can result in memory corruption.
2014-11-09 13:10:02 +00:00
sin 8745098fa4 Only check errno if getpwuid() fails
Checking errno otherwise is unspecified.
2014-07-09 14:41:32 +01:00
FRIGN 9db14b10dd Add /etc/passwd support
Fix slock to work with /etc/passwd without /etc/shadow.
while we're at it, remove an occurence of trailing whitespace.
2014-06-09 19:14:31 +01:00
sin 6a55128354 Set errno to 0 before getpwuid() and check it afterwards 2014-01-29 14:19:35 +00:00
Anselm R Garbe ba3acfc0dc applied Robert Schneider's Linux suggestions, also bumped version and updated LICENSE file's copyright notice 2013-08-02 22:11:18 +02:00
anselm@garbe.us 3092d3b314 prepared 1.1 2012-10-25 20:59:50 +02:00
garbeam@gmail.com d276b9b0e0 applied andres' multi-slock fix, thanks for spotting this issue 2012-08-02 21:54:18 +02:00
anselm@garbe.us 4b4fcca1bc added Ben's password placeholder entry kludge 2012-04-15 11:58:16 +02:00
anselm@garbe.us c1507cd225 applied Eckehard Bern's dualcolor patch to slock 2012-03-17 18:03:25 +01:00
anselm@garbe.us 88d9684966 Added tag 1.0 for changeset 05b949016e85 2012-02-11 10:51:31 +01:00
anselm@garbe.us 34f5edb29e preparing 1.0 2012-02-11 10:51:26 +01:00
anselm@garbe.us 0b13628462 select for SubstructureRedirectMask as well 2012-02-09 20:56:34 +01:00
anselm@garbe.us 7a217cb7c4 fixed missing new lines 2012-02-09 20:18:49 +01:00
anselm@garbe.us 5d433c7cf5 typo fix 2012-02-05 16:41:31 +01:00
anselm@garbe.us d6e9e7d9e3 added XRaiseWindow workaround when new clients are launched 2012-02-05 16:38:58 +01:00
garbeam@gmail.com f013cb264e applied Michaels patch 2012-01-24 22:10:02 +01:00
anselm@garbe.us 5a5c581285 added dummy error handler to prevent X from terminating slock just in case 2012-01-22 18:58:10 +01:00
Anselm R Garbe ede2935e10 applied Markus' patch to die in case /etc/passwd is unavailable 2011-04-21 08:22:47 +00:00
Anselm R Garbe 732d2b3bf5 applied Fernando Silveira's multiscreen patch for old style multihead setups 2009-11-26 12:53:26 +00:00
a@null 0cb05bdb75 applied Ali Gholami Rudi's patch regarding DPMS timeout customization and persistence 2008-12-12 19:34:43 +00:00
Anselm R Garbe cadc8a7472 Added tag 0.9 for changeset 1e8a77601cb9 2008-07-29 19:23:21 +01:00
Anselm R Garbe a6a3471dd9 fix 2008-07-29 19:17:24 +01:00
Anselm R Garbe 75dd779245 prepared release 2008-07-29 19:14:53 +01:00
Anselm R Garbe 1460b14f45 removed useless chars, prepared release 2008-07-29 19:08:18 +01:00
Anselm R Garbe c0fdcb8afc s/d// 2008-04-09 23:42:50 +01:00
Anselm R Garbe cc36050e44 yet another DPMS fix, still trying to prevent the error handler 2008-04-09 23:42:19 +01:00
Anselm R Garbe 049ccf9a90 fixed DPMS crashing issue 2008-04-08 09:55:46 +01:00
Anselm R Garbe 4ed35d22dc Added tag 0.8 for changeset c0eb8221ba49 2008-03-13 16:59:24 +00:00
Anselm R Garbe 98e2fef63d applied two patches, BSD_AUTH patch and Gottox' DPMS support patch 2008-02-22 10:13:12 +00:00
Anselm R. Garbe a430a647e4 removed Sander from -v by request 2007-11-25 12:17:33 +01:00
Anselm R. Garbe 18b2f4f34f next version will be 0.8 2007-11-24 21:27:44 +01:00
Anselm R. Garbe 925381bfd0 Added tag 0.7 for changeset c4635bb35a45 2007-11-24 21:18:06 +01:00
11 changed files with 538 additions and 151 deletions

View File

@ -1,6 +0,0 @@
0a95c73c7374fbc2342b6040d9f35ddf597729e1 0.1
da5cb1f0a685258d5315ea109860bacbc2871a57 0.2
f9157b1864388ad8f1920e5fde7c5849e73d8327 0.3
4c2cf4d6a2d0e08cbe280ec50ef76c9aecfc0fbe 0.4
bd24ea7fcca26b161225c464df23ecbfe85280e1 0.5
dd226a81c09adfa86db232419b3000b7e406df68 0.6

24
LICENSE
View File

@ -1,21 +1,25 @@
MIT/X Consortium License
© 2006-2007 Anselm R. Garbe <garbeam at gmail dot com>
© 2015-2016 Markus Teich <markus.teich@stusta.mhn.de>
© 2014 Dimitris Papastamos <sin@2f30.org>
© 2006-2014 Anselm R Garbe <anselm@garbe.us>
© 2014-2016 Laslo Hunhold <dev@frign.de>
© 2016-2023 Hiltjo Posthuma <hiltjo@codemadness.org>
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

View File

@ -1,52 +1,46 @@
# slock - simple screen locker
# © 2006-2007 Anselm R. Garbe, Sander van Dijk
# See LICENSE file for copyright and license details.
include config.mk
SRC = slock.c
SRC = slock.c ${COMPATSRC}
OBJ = ${SRC:.c=.o}
all: options slock
options:
@echo slock build options:
@echo "CFLAGS = ${CFLAGS}"
@echo "LDFLAGS = ${LDFLAGS}"
@echo "CC = ${CC}"
@echo "LD = ${LD}"
all: slock
.c.o:
@echo CC $<
@${CC} -c ${CFLAGS} $<
${CC} -c ${CFLAGS} $<
${OBJ}: config.mk
${OBJ}: config.h config.mk arg.h util.h
config.h:
cp config.def.h $@
slock: ${OBJ}
@echo LD $@
@${LD} -o $@ ${OBJ} ${LDFLAGS}
@strip $@
${CC} -o $@ ${OBJ} ${LDFLAGS}
clean:
@echo cleaning
@rm -f slock ${OBJ} slock-${VERSION}.tar.gz
rm -f slock ${OBJ} slock-${VERSION}.tar.gz
dist: clean
@echo creating dist tarball
@mkdir -p slock-${VERSION}
@cp -R LICENSE Makefile README config.mk ${SRC} slock-${VERSION}
@tar -cf slock-${VERSION}.tar slock-${VERSION}
@gzip slock-${VERSION}.tar
@rm -rf slock-${VERSION}
mkdir -p slock-${VERSION}
cp -R LICENSE Makefile README slock.1 config.mk \
${SRC} config.def.h arg.h util.h slock-${VERSION}
tar -cf slock-${VERSION}.tar slock-${VERSION}
gzip slock-${VERSION}.tar
rm -rf slock-${VERSION}
install: all
@echo installing executable file to ${DESTDIR}${PREFIX}/bin
@mkdir -p ${DESTDIR}${PREFIX}/bin
@cp -f slock ${DESTDIR}${PREFIX}/bin
@chmod 755 ${DESTDIR}${PREFIX}/bin/slock
@chmod u+s ${DESTDIR}${PREFIX}/bin/slock
mkdir -p ${DESTDIR}${PREFIX}/bin
cp -f slock ${DESTDIR}${PREFIX}/bin
chmod 755 ${DESTDIR}${PREFIX}/bin/slock
chmod u+s ${DESTDIR}${PREFIX}/bin/slock
mkdir -p ${DESTDIR}${MANPREFIX}/man1
sed "s/VERSION/${VERSION}/g" <slock.1 >${DESTDIR}${MANPREFIX}/man1/slock.1
chmod 644 ${DESTDIR}${MANPREFIX}/man1/slock.1
uninstall:
@echo removing executable file from ${DESTDIR}${PREFIX}/bin
@rm -f ${DESTDIR}${PREFIX}/bin/slock
rm -f ${DESTDIR}${PREFIX}/bin/slock
rm -f ${DESTDIR}${MANPREFIX}/man1/slock.1
.PHONY: all options clean dist install uninstall
.PHONY: all clean dist install uninstall

6
README
View File

@ -1,6 +1,6 @@
slock - simple screen locker
============================
simple screen locker utility for X.
simple screen locker utility for X.
Requirements
@ -13,8 +13,8 @@ Installation
Edit config.mk to match your local setup (slock is installed into
the /usr/local namespace by default).
Afterwards enter the following command to build and install slock (if
necessary as root):
Afterwards enter the following command to build and install slock
(if necessary as root):
make clean install

65
arg.h Normal file
View File

@ -0,0 +1,65 @@
/*
* Copy me if you can.
* by 20h
*/
#ifndef ARG_H__
#define ARG_H__
extern char *argv0;
/* use main(int argc, char *argv[]) */
#define ARGBEGIN for (argv0 = *argv, argv++, argc--;\
argv[0] && argv[0][0] == '-'\
&& argv[0][1];\
argc--, argv++) {\
char argc_;\
char **argv_;\
int brk_;\
if (argv[0][1] == '-' && argv[0][2] == '\0') {\
argv++;\
argc--;\
break;\
}\
for (brk_ = 0, argv[0]++, argv_ = argv;\
argv[0][0] && !brk_;\
argv[0]++) {\
if (argv_ != argv)\
break;\
argc_ = argv[0][0];\
switch (argc_)
/* Handles obsolete -NUM syntax */
#define ARGNUM case '0':\
case '1':\
case '2':\
case '3':\
case '4':\
case '5':\
case '6':\
case '7':\
case '8':\
case '9'
#define ARGEND }\
}
#define ARGC() argc_
#define ARGNUMF() (brk_ = 1, estrtonum(argv[0], 0, INT_MAX))
#define EARGF(x) ((argv[0][1] == '\0' && argv[1] == NULL)?\
((x), abort(), (char *)0) :\
(brk_ = 1, (argv[0][1] != '\0')?\
(&argv[0][1]) :\
(argc--, argv++, argv[0])))
#define ARGF() ((argv[0][1] == '\0' && argv[1] == NULL)?\
(char *)0 :\
(brk_ = 1, (argv[0][1] != '\0')?\
(&argv[0][1]) :\
(argc--, argv++, argv[0])))
#define LNGARG() &argv[0][0]
#endif

12
config.def.h Normal file
View File

@ -0,0 +1,12 @@
/* user and group to drop privileges to */
static const char *user = "nobody";
static const char *group = "nogroup";
static const char *colorname[NUMCOLS] = {
[INIT] = "black", /* after initialization */
[INPUT] = "#005577", /* during input */
[FAILED] = "#CC3333", /* wrong password */
};
/* treat a cleared input like a wrong password (color) */
static const int failonclear = 1;

View File

@ -1,27 +1,29 @@
# slock version
VERSION = 0.7
VERSION = 1.5
# Customize below to fit your system
# paths
PREFIX = /usr/local
MANPREFIX = ${PREFIX}/share/man
X11INC = /usr/X11R6/include
X11LIB = /usr/X11R6/lib
# includes and libs
INCS = -I. -I/usr/include -I${X11INC}
LIBS = -L/usr/lib -lc -lcrypt -L${X11LIB} -lX11
LIBS = -L/usr/lib -lc -lcrypt -L${X11LIB} -lX11 -lXext -lXrandr
# flags
CFLAGS = -Os ${INCS} -DVERSION=\"${VERSION}\" -DHAVE_SHADOW_H
LDFLAGS = ${LIBS}
#CFLAGS = -g -Wall -O2 ${INCS} -DVERSION=\"${VERSION}\" -DHAVE_SHADOW_H
#LDFLAGS = -g ${LIBS}
CPPFLAGS = -DVERSION=\"${VERSION}\" -D_DEFAULT_SOURCE -DHAVE_SHADOW_H
CFLAGS = -std=c99 -pedantic -Wall -Os ${INCS} ${CPPFLAGS}
LDFLAGS = -s ${LIBS}
COMPATSRC = explicit_bzero.c
# On *BSD remove -DHAVE_SHADOW_H from CFLAGS
# On OpenBSD and Darwin remove -lcrypt from LIBS
# compiler and linker
CC = cc
LD = ${CC}
#LIBS = -L/usr/lib -lc -L${X11LIB} -lX11 -lXext -lXrandr
# On *BSD remove -DHAVE_SHADOW_H from CPPFLAGS
# On NetBSD add -D_NETBSD_SOURCE to CPPFLAGS
#CPPFLAGS = -DVERSION=\"${VERSION}\" -D_BSD_SOURCE -D_NETBSD_SOURCE
# On OpenBSD set COMPATSRC to empty
#COMPATSRC =

19
explicit_bzero.c Normal file
View File

@ -0,0 +1,19 @@
/* $OpenBSD: explicit_bzero.c,v 1.3 2014/06/21 02:34:26 matthew Exp $ */
/*
* Public domain.
* Written by Matthew Dempsky.
*/
#include <string.h>
__attribute__((weak)) void
__explicit_bzero_hook(void *buf, size_t len)
{
}
void
explicit_bzero(void *buf, size_t len)
{
memset(buf, 0, len);
__explicit_bzero_hook(buf, len);
}

45
slock.1 Normal file
View File

@ -0,0 +1,45 @@
.Dd October 6, 2023
.Dt SLOCK 1
.Os
.Sh NAME
.Nm slock
.Nd simple X screen locker
.Sh SYNOPSIS
.Nm
.Op Fl v
.Op Ar cmd Op Ar arg ...
.Sh DESCRIPTION
.Nm
is a simple X screen locker.
If provided,
.Ar cmd
is executed after the screen has been locked.
.Pp
The options are as follows:
.Bl -tag -width Ds
.It Fl v
Print version information to stdout and exit.
.El
.Sh EXIT STATUS
.Ex -std
.Sh EXAMPLES
$
.Nm
/usr/sbin/s2ram
.Sh SECURITY CONSIDERATIONS
To make sure a locked screen can not be bypassed by switching VTs
or killing the X server with Ctrl+Alt+Backspace, it is recommended
to disable both in
.Xr xorg.conf 5
for maximum security:
.Bd -literal
Section "ServerFlags"
Option "DontVTSwitch" "True"
Option "DontZap" "True"
EndSection
.Ed
.Sh CUSTOMIZATION
.Nm
can be customized by creating a custom config.h from config.def.h and
(re)compiling the source code.
This keeps it fast, secure and simple.

428
slock.c
View File

@ -1,11 +1,12 @@
/* © 2006-2007 Anselm R. Garbe <garbeam at gmail dot com>
* See LICENSE file for license details. */
/* See LICENSE file for license details. */
#define _XOPEN_SOURCE 500
#if HAVE_SHADOW_H
#include <shadow.h>
#endif
#include <ctype.h>
#include <errno.h>
#include <grp.h>
#include <pwd.h>
#include <stdarg.h>
#include <stdlib.h>
@ -13,133 +14,382 @@
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <X11/extensions/Xrandr.h>
#include <X11/keysym.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
void
eprint(const char *errstr, ...) {
#include "arg.h"
#include "util.h"
char *argv0;
enum {
INIT,
INPUT,
FAILED,
NUMCOLS
};
struct lock {
int screen;
Window root, win;
Pixmap pmap;
unsigned long colors[NUMCOLS];
};
struct xrandr {
int active;
int evbase;
int errbase;
};
#include "config.h"
static void
die(const char *errstr, ...)
{
va_list ap;
va_start(ap, errstr);
vfprintf(stderr, errstr, ap);
va_end(ap);
exit(EXIT_FAILURE);
exit(1);
}
const char *
get_password() { /* only run as root */
const char *rval;
#ifdef __linux__
#include <fcntl.h>
#include <linux/oom.h>
static void
dontkillme(void)
{
FILE *f;
const char oomfile[] = "/proc/self/oom_score_adj";
if (!(f = fopen(oomfile, "w"))) {
if (errno == ENOENT)
return;
die("slock: fopen %s: %s\n", oomfile, strerror(errno));
}
fprintf(f, "%d", OOM_SCORE_ADJ_MIN);
if (fclose(f)) {
if (errno == EACCES)
die("slock: unable to disable OOM killer. "
"Make sure to suid or sgid slock.\n");
else
die("slock: fclose %s: %s\n", oomfile, strerror(errno));
}
}
#endif
static const char *
gethash(void)
{
const char *hash;
struct passwd *pw;
if(geteuid() != 0)
eprint("slock: cannot retrieve password entry (make sure to suid slock)\n");
pw = getpwuid(getuid());
endpwent();
rval = pw->pw_passwd;
/* Check if the current user has a password entry */
errno = 0;
if (!(pw = getpwuid(getuid()))) {
if (errno)
die("slock: getpwuid: %s\n", strerror(errno));
else
die("slock: cannot retrieve password entry\n");
}
hash = pw->pw_passwd;
#if HAVE_SHADOW_H
{
if (!strcmp(hash, "x")) {
struct spwd *sp;
sp = getspnam(getenv("USER"));
endspent();
rval = sp->sp_pwdp;
if (!(sp = getspnam(pw->pw_name)))
die("slock: getspnam: cannot retrieve shadow entry. "
"Make sure to suid or sgid slock.\n");
hash = sp->sp_pwdp;
}
#endif
/* drop privileges */
if(setgid(pw->pw_gid) < 0 || setuid(pw->pw_uid) < 0)
eprint("slock: cannot drop privileges\n");
return rval;
#else
if (!strcmp(hash, "*")) {
#ifdef __OpenBSD__
if (!(pw = getpwuid_shadow(getuid())))
die("slock: getpwnam_shadow: cannot retrieve shadow entry. "
"Make sure to suid or sgid slock.\n");
hash = pw->pw_passwd;
#else
die("slock: getpwuid: cannot retrieve shadow entry. "
"Make sure to suid or sgid slock.\n");
#endif /* __OpenBSD__ */
}
#endif /* HAVE_SHADOW_H */
return hash;
}
int
main(int argc, char **argv) {
char curs[] = {0, 0, 0, 0, 0, 0, 0, 0};
char buf[32], passwd[256];
int num, screen;
const char *pws;
unsigned int len;
Bool running = True;
Cursor invisible;
Display *dpy;
static void
readpw(Display *dpy, struct xrandr *rr, struct lock **locks, int nscreens,
const char *hash)
{
XRRScreenChangeNotifyEvent *rre;
char buf[32], passwd[256], *inputhash;
int num, screen, running, failure, oldc;
unsigned int len, color;
KeySym ksym;
Pixmap pmap;
Window root, w;
XColor black, dummy;
XEvent ev;
XSetWindowAttributes wa;
if((argc > 1) && !strncmp(argv[1], "-v", 3))
eprint("slock-"VERSION", © 2006-2007 Anselm R. Garbe, Sander van Dijk\n");
pws = get_password();
if(!(dpy = XOpenDisplay(0)))
eprint("slock: cannot open display\n");
screen = DefaultScreen(dpy);
root = RootWindow(dpy, screen);
/* init */
wa.override_redirect = 1;
wa.background_pixel = BlackPixel(dpy, screen);
w = XCreateWindow(dpy, root, 0, 0, DisplayWidth(dpy, screen), DisplayHeight(dpy, screen),
0, DefaultDepth(dpy, screen), CopyFromParent,
DefaultVisual(dpy, screen), CWOverrideRedirect | CWBackPixel, &wa);
XAllocNamedColor(dpy, DefaultColormap(dpy, screen), "black", &black, &dummy);
pmap = XCreateBitmapFromData(dpy, w, curs, 8, 8);
invisible = XCreatePixmapCursor(dpy, pmap, pmap, &black, &black, 0, 0);
XDefineCursor(dpy, w, invisible);
XMapRaised(dpy, w);
for(len = 1000; len; len--) {
if(XGrabPointer(dpy, root, False, ButtonPressMask | ButtonReleaseMask | PointerMotionMask,
GrabModeAsync, GrabModeAsync, None, invisible, CurrentTime) == GrabSuccess)
break;
usleep(1000);
}
if((running = running && (len > 0))) {
for(len = 1000; len; len--) {
if(XGrabKeyboard(dpy, root, True, GrabModeAsync, GrabModeAsync, CurrentTime)
== GrabSuccess)
break;
usleep(1000);
}
running = (len > 0);
}
len = 0;
XSync(dpy, False);
running = 1;
failure = 0;
oldc = INIT;
/* main event loop */
while(running && !XNextEvent(dpy, &ev))
if(ev.type == KeyPress) {
buf[0] = 0;
num = XLookupString(&ev.xkey, buf, sizeof buf, &ksym, 0);
if(IsFunctionKey(ksym) || IsKeypadKey(ksym)
|| IsMiscFunctionKey(ksym) || IsPFKey(ksym)
|| IsPrivateKeypadKey(ksym))
while (running && !XNextEvent(dpy, &ev)) {
if (ev.type == KeyPress) {
explicit_bzero(&buf, sizeof(buf));
num = XLookupString(&ev.xkey, buf, sizeof(buf), &ksym, 0);
if (IsKeypadKey(ksym)) {
if (ksym == XK_KP_Enter)
ksym = XK_Return;
else if (ksym >= XK_KP_0 && ksym <= XK_KP_9)
ksym = (ksym - XK_KP_0) + XK_0;
}
if (IsFunctionKey(ksym) ||
IsKeypadKey(ksym) ||
IsMiscFunctionKey(ksym) ||
IsPFKey(ksym) ||
IsPrivateKeypadKey(ksym))
continue;
switch(ksym) {
switch (ksym) {
case XK_Return:
passwd[len] = 0;
if((running = strcmp(crypt(passwd, pws), pws)) != 0)
passwd[len] = '\0';
errno = 0;
if (!(inputhash = crypt(passwd, hash)))
fprintf(stderr, "slock: crypt: %s\n", strerror(errno));
else
running = !!strcmp(inputhash, hash);
if (running) {
XBell(dpy, 100);
failure = 1;
}
explicit_bzero(&passwd, sizeof(passwd));
len = 0;
break;
case XK_Escape:
explicit_bzero(&passwd, sizeof(passwd));
len = 0;
break;
case XK_BackSpace:
if(len)
--len;
if (len)
passwd[--len] = '\0';
break;
default:
if(num && !iscntrl((int) buf[0]) && (len + num < sizeof passwd)) {
if (num && !iscntrl((int)buf[0]) &&
(len + num < sizeof(passwd))) {
memcpy(passwd + len, buf, num);
len += num;
}
break;
}
color = len ? INPUT : ((failure || failonclear) ? FAILED : INIT);
if (running && oldc != color) {
for (screen = 0; screen < nscreens; screen++) {
XSetWindowBackground(dpy,
locks[screen]->win,
locks[screen]->colors[color]);
XClearWindow(dpy, locks[screen]->win);
}
oldc = color;
}
} else if (rr->active && ev.type == rr->evbase + RRScreenChangeNotify) {
rre = (XRRScreenChangeNotifyEvent*)&ev;
for (screen = 0; screen < nscreens; screen++) {
if (locks[screen]->win == rre->window) {
if (rre->rotation == RR_Rotate_90 ||
rre->rotation == RR_Rotate_270)
XResizeWindow(dpy, locks[screen]->win,
rre->height, rre->width);
else
XResizeWindow(dpy, locks[screen]->win,
rre->width, rre->height);
XClearWindow(dpy, locks[screen]->win);
break;
}
}
} else {
for (screen = 0; screen < nscreens; screen++)
XRaiseWindow(dpy, locks[screen]->win);
}
XUngrabPointer(dpy, CurrentTime);
XFreePixmap(dpy, pmap);
XDestroyWindow(dpy, w);
XCloseDisplay(dpy);
}
}
static struct lock *
lockscreen(Display *dpy, struct xrandr *rr, int screen)
{
char curs[] = {0, 0, 0, 0, 0, 0, 0, 0};
int i, ptgrab, kbgrab;
struct lock *lock;
XColor color, dummy;
XSetWindowAttributes wa;
Cursor invisible;
if (dpy == NULL || screen < 0 || !(lock = malloc(sizeof(struct lock))))
return NULL;
lock->screen = screen;
lock->root = RootWindow(dpy, lock->screen);
for (i = 0; i < NUMCOLS; i++) {
XAllocNamedColor(dpy, DefaultColormap(dpy, lock->screen),
colorname[i], &color, &dummy);
lock->colors[i] = color.pixel;
}
/* init */
wa.override_redirect = 1;
wa.background_pixel = lock->colors[INIT];
lock->win = XCreateWindow(dpy, lock->root, 0, 0,
DisplayWidth(dpy, lock->screen),
DisplayHeight(dpy, lock->screen),
0, DefaultDepth(dpy, lock->screen),
CopyFromParent,
DefaultVisual(dpy, lock->screen),
CWOverrideRedirect | CWBackPixel, &wa);
lock->pmap = XCreateBitmapFromData(dpy, lock->win, curs, 8, 8);
invisible = XCreatePixmapCursor(dpy, lock->pmap, lock->pmap,
&color, &color, 0, 0);
XDefineCursor(dpy, lock->win, invisible);
/* Try to grab mouse pointer *and* keyboard for 600ms, else fail the lock */
for (i = 0, ptgrab = kbgrab = -1; i < 6; i++) {
if (ptgrab != GrabSuccess) {
ptgrab = XGrabPointer(dpy, lock->root, False,
ButtonPressMask | ButtonReleaseMask |
PointerMotionMask, GrabModeAsync,
GrabModeAsync, None, invisible, CurrentTime);
}
if (kbgrab != GrabSuccess) {
kbgrab = XGrabKeyboard(dpy, lock->root, True,
GrabModeAsync, GrabModeAsync, CurrentTime);
}
/* input is grabbed: we can lock the screen */
if (ptgrab == GrabSuccess && kbgrab == GrabSuccess) {
XMapRaised(dpy, lock->win);
if (rr->active)
XRRSelectInput(dpy, lock->win, RRScreenChangeNotifyMask);
XSelectInput(dpy, lock->root, SubstructureNotifyMask);
return lock;
}
/* retry on AlreadyGrabbed but fail on other errors */
if ((ptgrab != AlreadyGrabbed && ptgrab != GrabSuccess) ||
(kbgrab != AlreadyGrabbed && kbgrab != GrabSuccess))
break;
usleep(100000);
}
/* we couldn't grab all input: fail out */
if (ptgrab != GrabSuccess)
fprintf(stderr, "slock: unable to grab mouse pointer for screen %d\n",
screen);
if (kbgrab != GrabSuccess)
fprintf(stderr, "slock: unable to grab keyboard for screen %d\n",
screen);
return NULL;
}
static void
usage(void)
{
die("usage: slock [-v] [cmd [arg ...]]\n");
}
int
main(int argc, char **argv) {
struct xrandr rr;
struct lock **locks;
struct passwd *pwd;
struct group *grp;
uid_t duid;
gid_t dgid;
const char *hash;
Display *dpy;
int s, nlocks, nscreens;
ARGBEGIN {
case 'v':
puts("slock-"VERSION);
return 0;
default:
usage();
} ARGEND
/* validate drop-user and -group */
errno = 0;
if (!(pwd = getpwnam(user)))
die("slock: getpwnam %s: %s\n", user,
errno ? strerror(errno) : "user entry not found");
duid = pwd->pw_uid;
errno = 0;
if (!(grp = getgrnam(group)))
die("slock: getgrnam %s: %s\n", group,
errno ? strerror(errno) : "group entry not found");
dgid = grp->gr_gid;
#ifdef __linux__
dontkillme();
#endif
hash = gethash();
errno = 0;
if (!crypt("", hash))
die("slock: crypt: %s\n", strerror(errno));
if (!(dpy = XOpenDisplay(NULL)))
die("slock: cannot open display\n");
/* drop privileges */
if (setgroups(0, NULL) < 0)
die("slock: setgroups: %s\n", strerror(errno));
if (setgid(dgid) < 0)
die("slock: setgid: %s\n", strerror(errno));
if (setuid(duid) < 0)
die("slock: setuid: %s\n", strerror(errno));
/* check for Xrandr support */
rr.active = XRRQueryExtension(dpy, &rr.evbase, &rr.errbase);
/* get number of screens in display "dpy" and blank them */
nscreens = ScreenCount(dpy);
if (!(locks = calloc(nscreens, sizeof(struct lock *))))
die("slock: out of memory\n");
for (nlocks = 0, s = 0; s < nscreens; s++) {
if ((locks[s] = lockscreen(dpy, &rr, s)) != NULL)
nlocks++;
else
break;
}
XSync(dpy, 0);
/* did we manage to lock everything? */
if (nlocks != nscreens)
return 1;
/* run post-lock command */
if (argc > 0) {
switch (fork()) {
case -1:
die("slock: fork failed: %s\n", strerror(errno));
case 0:
if (close(ConnectionNumber(dpy)) < 0)
die("slock: close: %s\n", strerror(errno));
execvp(argv[0], argv);
fprintf(stderr, "slock: execvp %s: %s\n", argv[0], strerror(errno));
_exit(1);
}
}
/* everything is now blank. Wait for the correct password */
readpw(dpy, &rr, locks, nscreens, hash);
return 0;
}

2
util.h Normal file
View File

@ -0,0 +1,2 @@
#undef explicit_bzero
void explicit_bzero(void *, size_t);