0 /* See LICENSE file for license details. */

1 #define _XOPEN_SOURCE 500

2 #if HAVE_SHADOW_H

3 #include <shadow.h>

4 #endif

5

6 #include <ctype.h>

7 #include <errno.h>

8 #include <grp.h>

9 #include <pwd.h>

10 #include <stdarg.h>

11 #include <stdlib.h>

12 #include <stdio.h>

13 #include <string.h>

14 #include <unistd.h>

15 #include <sys/types.h>

16 #include <sys/wait.h>

17 #include <X11/extensions/Xrandr.h>

18 #include <X11/extensions/scrnsaver.h>

19 #include <X11/extensions/dpms.h>

20 #include <X11/keysym.h>

21 #include <X11/Xlib.h>

22 #include <X11/Xutil.h>

23 #include <security/pam_appl.h>

24 #include <pthread.h>

25 #include <pwd.h>

26 #include <signal.h>

27 #include "slock.h"

28 #include "global.h"

29

30 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

31 Display *dpy;

32

33 #ifndef EXTERN_LOCK

34 static int pam_conv(int num_msg, const struct pam_message **msg, struct pam_response **resp, void *appdata_ptr);

35 struct pam_conv pamc = {pam_conv, NULL};

36 char passwd[256];

37 char username[256];

38 GC lockgc;

39

40 enum {

41 INIT,

42 INPUT,

43 FAILED,

44 PAM,

45 TEXT,

46 NUMCOLS

47 };

48

49 static const int colors[NUMCOLS] = {

50 [INIT] = 0x111111, /* after initialization */

51 [INPUT] = 0x333333, /* during input */

52 [FAILED] = 0x771111, /* wrong password */

53 [PAM] = 0x222222, /* waiting for PAM */

54 [TEXT] = 0xDDDDDD,

55 };

56

57 /* treat a cleared input like a wrong password (color) */

58 static const int failonclear = 0;

59

60 /* PAM service that's used for authentication */

61 static const char* pam_service = "slock";

62

63 struct lock {

64 int screen;

65 Window root, win;

66 Pixmap pmap;

67 };

68

69 struct xrandr {

70 int active;

71 int evbase;

72 int errbase;

73 };

74

75 static void

76 die(const char *errstr, ...)

77 {

78 va_list ap;

79

80 va_start(ap, errstr);

81 vfprintf(stderr, errstr, ap);

82 va_end(ap);

83 exit(1);

84 }

85

86 static int

87 pam_conv(int num_msg, const struct pam_message **msg, struct pam_response **resp, void *appdata_ptr)

88 {

89 int retval = PAM_CONV_ERR;

90 for(int i=0; i<num_msg; i++) {

91 if (msg[i]->msg_style == PAM_PROMPT_ECHO_OFF &&

92 strncmp(msg[i]->msg, "Password: ", 10) == 0) {

93 struct pam_response *resp_msg = malloc(sizeof(struct pam_response));

94 if (!resp_msg)

95 die("malloc failed\n");

96 char *password = malloc(strlen(passwd) + 1);

97 if (!password)

98 die("malloc failed\n");

99 memset(password, 0, strlen(passwd) + 1);

100 strcpy(password, passwd);

101 resp_msg->resp_retcode = 0;

102 resp_msg->resp = password;

103 resp[i] = resp_msg;

104 retval = PAM_SUCCESS;

105 }

106 }

107 return retval;

108 }

109

110 #ifdef __linux__

111 #include <fcntl.h>

112 #include <linux/oom.h>

113

114 static void

115 dontkillme(void)

116 {

117 FILE *f;

118 const char oomfile[] = "/proc/self/oom_score_adj";

119

120 if (!(f = fopen(oomfile, "w"))) {

121 if (errno == ENOENT)

122 return;

123 die("slock: fopen %s: %s\n", oomfile, strerror(errno));

124 }

125 fprintf(f, "%d", OOM_SCORE_ADJ_MIN);

126 if (fclose(f)) {

127 if (errno == EACCES)

128 printf("slock: unable to disable OOM killer. "

129 "Make sure to suid or sgid slock.\n");

130 else

131 die("slock: fclose %s: %s\n", oomfile, strerror(errno));

132 }

133 }

134 #endif

135

136 static const char *

137 gethash(void)

138 {

139 const char *hash;

140 struct passwd *pw;

141

142 /* Check if the current user has a password entry */

143 errno = 0;

144 if (!(pw = getpwuid(getuid()))) {

145 if (errno)

146 die("slock: getpwuid: %s\n", strerror(errno));

147 else

148 die("slock: cannot retrieve password entry\n");

149 }

150 hash = pw->pw_passwd;

151

152 #if HAVE_SHADOW_H

153 if (!strcmp(hash, "x")) {

154 struct spwd *sp;

155 if (!(sp = getspnam(pw->pw_name)))

156 die("slock: getspnam: cannot retrieve shadow entry. "

157 "Make sure to suid or sgid slock.\n");

158 hash = sp->sp_pwdp;

159 }

160 #else

161 if (!strcmp(hash, "*")) {

162 #ifdef __OpenBSD__

163 if (!(pw = getpwuid_shadow(getuid())))

164 die("slock: getpwnam_shadow: cannot retrieve shadow entry. "

165 "Make sure to suid or sgid slock.\n");

166 hash = pw->pw_passwd;

167 #else

168 die("slock: getpwuid: cannot retrieve shadow entry. "

169 "Make sure to suid or sgid slock.\n");

170 #endif /* __OpenBSD__ */

171 }

172 #endif /* HAVE_SHADOW_H */

173

174 /* pam, store user name */

175 hash = pw->pw_name;

176 return hash;

177 }

178

179 static void

180 readpw(struct xrandr *rr, struct lock **locks, int nscreens,

181 const char *hash)

182 {

183 XRRScreenChangeNotifyEvent *rre;

184 char buf[32];

185 int num, screen, running, failure, retval; //oldc

186 unsigned int len, color;

187 KeySym ksym;

188 XEvent ev;

189 pam_handle_t *pamh;

190

191 len = 0;

192 running = 1;

193 failure = 0;

194 //oldc = INIT;

195

196 while (running && !XNextEvent(dpy, &ev)) {

197 if (ev.type == KeyPress) {

198 memset(&buf, 0, sizeof(buf));

199 num = XLookupString(&ev.xkey, buf, sizeof(buf), &ksym, 0);

200 if (IsKeypadKey(ksym)) {

201 if (ksym == XK_KP_Enter)

202 ksym = XK_Return;

203 else if (ksym >= XK_KP_0 && ksym <= XK_KP_9)

204 ksym = (ksym - XK_KP_0) + XK_0;

205 }

206 if (IsFunctionKey(ksym) ||

207 IsKeypadKey(ksym) ||

208 IsMiscFunctionKey(ksym) ||

209 IsPFKey(ksym) ||

210 IsPrivateKeypadKey(ksym))

211 continue;

212 switch (ksym) {

213 case XK_Return:

214 passwd[len] = '\0';

215 errno = 0;

216 retval = pam_start(pam_service, hash, &pamc, &pamh);

217 color = PAM;

218 for (screen = 0; screen < nscreens; screen++) {

219 XSetWindowBackground(dpy, locks[screen]->win, colors[color]);

220 XClearWindow(dpy, locks[screen]->win);

221 XRaiseWindow(dpy, locks[screen]->win);

222 }

223 XSync(dpy, False);

224

225 if (retval == PAM_SUCCESS)

226 retval = pam_authenticate(pamh, 0);

227 if (retval == PAM_SUCCESS)

228 retval = pam_acct_mgmt(pamh, 0);

229

230 running = 1;

231 if (retval == PAM_SUCCESS) {

232 running = 0;

233 XCloseDisplay(dpy);

234 if (!(dpy = XOpenDisplay(0))) {

235 printf("Cannot open display.\n");

236 }

237 } else

238 fprintf(stderr, "slock: %s\n", pam_strerror(pamh, retval));

239 pam_end(pamh, retval);

240 if (running) {

241 XBell(dpy, 100);

242 failure = 1;

243 }

244 memset(&passwd, 0, sizeof(passwd));

245 len = 0;

246 break;

247 case XK_Escape:

248 memset(&passwd, 0, sizeof(passwd));

249 len = 0;

250 break;

251 case XK_BackSpace:

252 if (len)

253 passwd[--len] = '\0';

254 failure = 0;

255 break;

256 default:

257 if (num && !iscntrl((int)buf[0]) &&

258 (len + num < sizeof(passwd))) {

259 memcpy(passwd + len, buf, num);

260 len += num;

261 }

262 break;

263 }

264 color = len ? INPUT : ((failure || failonclear) ? FAILED : INIT);

265 if (running) {

266 for (screen = 0; screen < nscreens; screen++) {

267 XSetWindowBackground(dpy,

268 locks[screen]->win,

269 colors[color]);

270 XClearWindow(dpy, locks[screen]->win);

271 char hidden[256];

272 memset(hidden, '*', len);

273 Screen* scr = XScreenOfDisplay(dpy, screen);

274 XSetForeground(dpy, lockgc, colors[TEXT]);

275

276 XSetFont(dpy, lockgc, font->fid);

277 XDrawString(dpy, locks[screen]->win, lockgc, XWidthOfScreen(scr)/2-XTextWidth(font, hidden, len)/2, XHeightOfScreen(scr)/2, hidden, len);

278 XDrawString(dpy, locks[screen]->win, lockgc, XWidthOfScreen(scr)/2-XTextWidth(font, username, strlen(username))/2, XHeightOfScreen(scr)/2-32, username, strlen(username));

279 }

280 }

281 } else if (rr->active && ev.type == rr->evbase + RRScreenChangeNotify) {

282 rre = (XRRScreenChangeNotifyEvent*)&ev;

283 for (screen = 0; screen < nscreens; screen++) {

284 if (locks[screen]->win == rre->window) {

285 if (rre->rotation == RR_Rotate_90 ||

286 rre->rotation == RR_Rotate_270)

287 XResizeWindow(dpy, locks[screen]->win,

288 rre->height, rre->width);

289 else

290 XResizeWindow(dpy, locks[screen]->win,

291 rre->width, rre->height);

292 XClearWindow(dpy, locks[screen]->win);

293 break;

294 }

295 }

296 } else {

297 for (screen = 0; screen < nscreens; screen++)

298 XRaiseWindow(dpy, locks[screen]->win);

299 }

300 }

301 }

302

303 static struct lock* lockscreen(struct xrandr *rr, int screen)

304 {

305 char curs[] = {0, 0, 0, 0, 0, 0, 0, 0};

306 int i, ptgrab, kbgrab;

307 struct lock *lock;

308 XColor color, dummy;

309 XSetWindowAttributes wa;

310 Cursor invisible;

311

312 if (dpy == NULL || screen < 0 || !(lock = malloc(sizeof(struct lock))))

313 return NULL;

314

315 lock->screen = screen;

316 lock->root = RootWindow(dpy, lock->screen);

317

318 /* init */

319 wa.override_redirect = 1;

320 wa.background_pixel = colors[INIT];

321 lock->win = XCreateWindow(dpy, lock->root, 0, 0,

322 DisplayWidth(dpy, lock->screen),

323 DisplayHeight(dpy, lock->screen),

324 0, DefaultDepth(dpy, lock->screen),

325 CopyFromParent,

326 DefaultVisual(dpy, lock->screen),

327 CWOverrideRedirect | CWBackPixel, &wa);

328 lock->pmap = XCreateBitmapFromData(dpy, lock->win, curs, 8, 8);

329 invisible = XCreatePixmapCursor(dpy, lock->pmap, lock->pmap,

330 &color, &color, 0, 0);

331 XDefineCursor(dpy, lock->win, invisible);

332 lockgc = XCreateGC(dpy, lock->win, 0, NULL);

333

334 /* Try to grab mouse pointer *and* keyboard for 600ms, else fail the lock */

335 for (i = 0, ptgrab = kbgrab = -1; i < 6; i++) {

336 if (ptgrab != GrabSuccess) {

337 ptgrab = XGrabPointer(dpy, lock->root, False,

338 ButtonPressMask | ButtonReleaseMask |

339 PointerMotionMask, GrabModeAsync,

340 GrabModeAsync, None, invisible, CurrentTime);

341 }

342 if (kbgrab != GrabSuccess) {

343 kbgrab = XGrabKeyboard(dpy, lock->root, True,

344 GrabModeAsync, GrabModeAsync, CurrentTime);

345 }

346

347 /* input is grabbed: we can lock the screen */

348 if (ptgrab == GrabSuccess && kbgrab == GrabSuccess) {

349 XMapRaised(dpy, lock->win);

350 if (rr->active)

351 XRRSelectInput(dpy, lock->win, RRScreenChangeNotifyMask);

352

353 XSelectInput(dpy, lock->root, SubstructureNotifyMask);

354 return lock;

355 }

356

357 /* retry on AlreadyGrabbed but fail on other errors */

358 if ((ptgrab != AlreadyGrabbed && ptgrab != GrabSuccess) ||

359 (kbgrab != AlreadyGrabbed && kbgrab != GrabSuccess))

360 break;

361

362 usleep(100000);

363 }

364

365 /* we couldn't grab all input: fail out */

366 if (ptgrab != GrabSuccess)

367 fprintf(stderr, "slock: unable to grab mouse pointer for screen %d\n",

368 screen);

369 if (kbgrab != GrabSuccess)

370 fprintf(stderr, "slock: unable to grab keyboard for screen %d\n",

371 screen);

372 return NULL;

373 }

374 #endif

375

376 int slock() {

377 #ifdef EXTERN_LOCK

378 int pid = spawn(LOCK);

379 int status;

380 waitpid(pid, &status, 0);

381 return 0;

382 #else

383 struct xrandr rr;

384 struct lock **locks;

385 struct passwd *pwd;

386 const char *hash;

387 int s, nlocks, nscreens;

388

389 #ifdef __linux__

390 dontkillme();

391 #endif

392

393 hash = gethash();

394 errno = 0;

395

396 rr.active = XRRQueryExtension(dpy, &rr.evbase, &rr.errbase);

397

398 /* get number of screens in display "dpy" and blank them */

399 nscreens = ScreenCount(dpy);

400 if (!(locks = calloc(nscreens, sizeof(struct lock *))))

401 printf("slock: out of memory\n");

402 for (nlocks = 0, s = 0; s < nscreens; s++) {

403 if ((locks[s] = lockscreen(&rr, s)) != NULL)

404 nlocks++;

405 else

406 break;

407 }

408 XSync(dpy, 0);

409

410 /* did we manage to lock everything? */

411 if (nlocks != nscreens)

412 return 1;

413

414 /* everything is now blank. Wait for the correct password */

415 readpw(&rr, locks, nscreens, hash);

416

417 return 0;

418 #endif

419 }

420

421 int screensaver() {

422 #ifndef EXTERN_LOCK

423 username[0] = '\0';

424 struct passwd *pw;

425 uid_t uid;

426 int c;

427 uid = geteuid();

428 pw = getpwuid(uid);

429 if (pw)

430 strncpy(username, pw->pw_name, 256);

431 #endif

432 if (!(dpy = XOpenDisplay(0))) {

433 printf("Cannot open display.\n");

434 return -1;

435 }

436

437 DPMSEnable(dpy);

438

439 XScreenSaverInfo *info = XScreenSaverAllocInfo();

440 while (1) {

441 pthread_mutex_lock(&mutex);

442 XScreenSaverQueryInfo(dpy, DefaultRootWindow(dpy), info);

443 if (info->idle>=(LOCK_TIME-15)*1000)

444 slock();

445 pthread_mutex_unlock(&mutex);

446 sleep(1);

447 }

448 XFree(info);

449 return 0;

450 }

451