convert prompt functions to use GError, too
[vlock.git] / src / auth-pam.c
blob256e8d977e29344ffab31450cea56634cac74ac2
1 /* auth-pam.c -- PAM authentification routine for vlock,
2 * the VT locking program for linux
4 * This program is copyright (C) 2007 Frank Benkstein, and is free
5 * software which is freely distributable under the terms of the
6 * GNU General Public License version 2, included as the file COPYING in this
7 * distribution. It is NOT public domain software, and any
8 * redistribution not permitted by the GNU General Public License is
9 * expressly forbidden without prior written permission from
10 * the author.
13 * The conversation function (conversation) was inspired by/copied from
14 * openpam's openpam_ttyconv.c:
16 * Copyright (c) 2002-2003 Networks Associates Technology, Inc.
18 * Redistribution and use in source and binary forms, with or without
19 * modification, are permitted provided that the following conditions
20 * are met:
21 * 1. Redistributions of source code must retain the above copyright
22 * notice, this list of conditions and the following disclaimer.
23 * 2. Redistributions in binary form must reproduce the above copyright
24 * notice, this list of conditions and the following disclaimer in the
25 * documentation and/or other materials provided with the distribution.
26 * 3. The name of the author may not be used to endorse or promote
27 * products derived from this software without specific prior written
28 * permission.
30 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
31 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
32 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
34 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
35 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
36 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
37 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
38 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
39 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
40 * SUCH DAMAGE.
44 #include <stdbool.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <unistd.h>
50 #include <security/pam_appl.h>
52 #include "auth.h"
53 #include "prompt.h"
55 GQuark vlock_auth_error_quark(void)
57 return g_quark_from_static_string("vlock-auth-pam-error-quark");
60 struct conversation_data {
61 GError *error;
62 struct timespec *timeout;
65 /* PAM conversation function. Assumes that a pointer to struct
66 * conversation_data is passed as the as appdata_ptr argument. In case of a
67 * normal error conversation_data's error field is set accordingly and
68 * PAM_CONV_ERR is returned while in case of a memory allocation error
69 * PAM_BUF_ERR is returned and the error field is left untouched. On success
70 * PAM_SUCCESS is returned.
72 static int conversation(int num_msg, const struct pam_message **msg, struct
73 pam_response **resp, void *appdata_ptr)
75 struct pam_response *aresp;
76 struct conversation_data *conv_data = appdata_ptr;
78 g_return_val_if_fail(conv_data->error == NULL, PAM_CONV_ERR);
79 g_return_val_if_fail(num_msg > 0 && num_msg < PAM_MAX_NUM_MSG, PAM_CONV_ERR);
81 if ((aresp = calloc((size_t) num_msg, sizeof *aresp)) == NULL)
82 return PAM_BUF_ERR;
84 for (int i = 0; i < num_msg; i++) {
85 switch (msg[i]->msg_style) {
86 case PAM_PROMPT_ECHO_OFF:
87 aresp[i].resp = prompt_echo_off(msg[i]->msg, conv_data->timeout, &conv_data->error);
88 if (aresp[i].resp == NULL)
89 goto fail;
90 break;
91 case PAM_PROMPT_ECHO_ON:
92 aresp[i].resp = prompt(msg[i]->msg, conv_data->timeout, &conv_data->error);
93 if (aresp[i].resp == NULL)
94 goto fail;
95 break;
96 case PAM_TEXT_INFO:
97 case PAM_ERROR_MSG:
99 size_t msg_len = strlen(msg[i]->msg);
100 (void) fputs(msg[i]->msg, stderr);
101 if (msg_len > 0 && msg[i]->msg[msg_len - 1] != '\n')
102 (void) fputc('\n', stderr);
104 break;
105 default:
106 goto fail;
110 *resp = aresp;
111 return PAM_SUCCESS;
113 fail:
114 for (int i = 0; i < num_msg; ++i) {
115 if (aresp[i].resp != NULL) {
116 memset(aresp[i].resp, 0, strlen(aresp[i].resp));
117 free(aresp[i].resp);
121 memset(aresp, 0, num_msg * sizeof *aresp);
122 free(aresp);
123 *resp = NULL;
125 g_warn_if_fail(conv_data->error != NULL, PAM_CONV_ERR);
127 return PAM_CONV_ERR;
130 bool auth(const char *user, struct timespec *timeout, GError **error)
132 char *pam_tty;
133 pam_handle_t *pamh;
134 int pam_status;
135 int pam_end_status;
136 struct conversation_data conv_data = {
137 .error = NULL,
138 .timeout = timeout,
140 struct pam_conv pamc = {
141 .conv = conversation,
142 .appdata_ptr = &conv_data,
145 g_return_val_if_fail(error == NULL || *error == NULL, false);
147 /* initialize pam */
148 pam_status = pam_start("vlock", user, &pamc, &pamh);
150 if (pam_status != PAM_SUCCESS) {
151 g_propagate_error(error,
152 g_error_new_literal(
153 VLOCK_AUTH_ERROR,
154 VLOCK_AUTH_ERROR_FAILED,
155 pam_strerror(pamh, pam_status)));
156 goto end;
159 /* get the name of stdin's tty device, if any */
160 pam_tty = ttyname(STDIN_FILENO);
162 /* set PAM_TTY */
163 if (pam_tty != NULL) {
164 pam_status = pam_set_item(pamh, PAM_TTY, pam_tty);
166 if (pam_status != PAM_SUCCESS) {
167 g_propagate_error(error,
168 g_error_new_literal(
169 VLOCK_AUTH_ERROR,
170 VLOCK_AUTH_ERROR_FAILED,
171 pam_strerror(pamh, pam_status)));
172 goto end;
176 /* put the username before the password prompt */
177 fprintf(stderr, "%s's ", user);
178 fflush(stderr);
180 /* authenticate the user */
181 pam_status = pam_authenticate(pamh, 0);
183 if (pam_status == PAM_CONV_ERR) {
184 g_assert(conv_data.error != NULL);
185 g_propagate_error(error, conv_data.error);
186 } else if (pam_status != PAM_SUCCESS) {
187 g_assert(conv_data.error == NULL);
189 g_propagate_error(error,
190 g_error_new_literal(
191 VLOCK_AUTH_ERROR,
192 VLOCK_AUTH_ERROR_FAILED,
193 pam_strerror(pamh, pam_status)));
196 end:
197 /* finish pam */
198 pam_end_status = pam_end(pamh, pam_status);
200 if (pam_end_status != PAM_SUCCESS && error != NULL && *error == NULL)) {
201 g_propagate_error(error,
202 g_error_new_literal(
203 VLOCK_AUTH_ERROR,
204 VLOCK_AUTH_ERROR_FAILED,
205 pam_strerror(pamh, pam_status)));
208 return (pam_end_status == PAM_SUCCESS && pam_status == PAM_SUCCESS);