1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
|
// Demo wrapper for the PAM module. This is part of the Google Authenticator
// project.
//
// Copyright 2011 Google Inc.
// Author: Markus Gutschke
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#include "config.h"
#include <assert.h>
#include <fcntl.h>
#include <security/pam_appl.h>
#include <security/pam_modules.h>
#include <setjmp.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/resource.h>
#include <sys/time.h>
#include <termios.h>
#include <unistd.h>
#if !defined(PAM_BAD_ITEM)
// FreeBSD does not know about PAM_BAD_ITEM. And PAM_SYMBOL_ERR is an "enum",
// we can't test for it at compile-time.
#define PAM_BAD_ITEM PAM_SYMBOL_ERR
#endif
static struct termios old_termios;
static int jmpbuf_valid;
static sigjmp_buf jmpbuf;
static int conversation(int num_msg, PAM_CONST struct pam_message **msg,
struct pam_response **resp, void *appdata_ptr) {
if (num_msg == 1 &&
(msg[0]->msg_style == PAM_PROMPT_ECHO_OFF ||
msg[0]->msg_style == PAM_PROMPT_ECHO_ON)) {
*resp = malloc(sizeof(struct pam_response));
assert(*resp);
(*resp)->resp = calloc(1024, 1);
struct termios termios = old_termios;
if (msg[0]->msg_style == PAM_PROMPT_ECHO_OFF) {
termios.c_lflag &= ~(ECHO|ECHONL);
}
sigsetjmp(jmpbuf, 1);
jmpbuf_valid = 1;
sigset_t mask;
sigemptyset(&mask);
sigaddset(&mask, SIGTSTP);
assert(!sigprocmask(SIG_UNBLOCK, &mask, NULL));
printf("%s ", msg[0]->msg);
assert(!tcsetattr(0, TCSAFLUSH, &termios));
assert(fgets((*resp)->resp, 1024, stdin));
assert(!tcsetattr(0, TCSAFLUSH, &old_termios));
puts("");
assert(!sigprocmask(SIG_BLOCK, &mask, NULL));
jmpbuf_valid = 0;
char *ptr = strrchr((*resp)->resp, '\n');
if (ptr) {
*ptr = '\000';
}
(*resp)->resp_retcode = 0;
return PAM_SUCCESS;
}
if (num_msg == 1 && msg[0]->msg_style == PAM_ERROR_MSG) {
printf("Error message to user: %s\n", msg[0]->msg);
return PAM_SUCCESS;
}
return PAM_CONV_ERR;
}
#ifdef sun
#define PAM_CONST
#else
#define PAM_CONST const
#endif
int pam_get_user(pam_handle_t *pamh, PAM_CONST char **user,
const char *prompt) {
return pam_get_item(pamh, PAM_USER, (void *)user);
}
int pam_get_item(const pam_handle_t *pamh, int item_type,
PAM_CONST void **item) {
switch (item_type) {
case PAM_SERVICE: {
static const char service[] = "google_authenticator_demo";
*item = service;
return PAM_SUCCESS;
}
case PAM_USER: {
char *user = getenv("USER");
*item = user;
return PAM_SUCCESS;
}
case PAM_CONV: {
static struct pam_conv conv = { .conv = conversation }, *p_conv = &conv;
*item = p_conv;
return PAM_SUCCESS;
}
default:
return PAM_BAD_ITEM;
}
}
int pam_set_item(pam_handle_t *pamh, int item_type,
const void *item) {
switch (item_type) {
case PAM_AUTHTOK:
return PAM_SUCCESS;
default:
return PAM_BAD_ITEM;
}
}
static void print_diagnostics(int signo) {
extern const char *get_error_msg(void);
assert(!tcsetattr(0, TCSAFLUSH, &old_termios));
fprintf(stderr, "%s\n", get_error_msg());
_exit(1);
}
static void reset_console(int signo) {
assert(!tcsetattr(0, TCSAFLUSH, &old_termios));
puts("");
_exit(1);
}
static void stop(int signo) {
assert(!tcsetattr(0, TCSAFLUSH, &old_termios));
puts("");
raise(SIGSTOP);
}
static void cont(int signo) {
if (jmpbuf_valid) {
siglongjmp(jmpbuf, 0);
}
}
int main(int argc, char *argv[]) {
extern int pam_sm_authenticate(pam_handle_t *, int, int, const char **);
// Try to redirect stdio to /dev/tty
int fd = open("/dev/tty", O_RDWR);
if (fd >= 0) {
dup2(fd, 0);
dup2(fd, 1);
dup2(fd, 2);
close(fd);
}
// Disable core files
assert(!setrlimit(RLIMIT_CORE, (struct rlimit []){ { 0, 0 } }));
// Set up error and job control handlers
assert(!tcgetattr(0, &old_termios));
sigset_t mask;
sigemptyset(&mask);
sigaddset(&mask, SIGTSTP);
assert(!sigprocmask(SIG_BLOCK, &mask, NULL));
assert(!signal(SIGABRT, print_diagnostics));
assert(!signal(SIGINT, reset_console));
assert(!signal(SIGTSTP, stop));
assert(!signal(SIGCONT, cont));
// Attempt login
if (pam_sm_authenticate(NULL, 0, argc-1, (const char **)argv+1)
!= PAM_SUCCESS) {
fprintf(stderr, "Login failed\n");
abort();
}
return 0;
}
|