1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 /*
27 * This file has all of the PAM related code for sys-suspend. It is
28 * part of it's own file, as these could be part of some bigger item
29 * that can handle generic PAM facilities (certainly the getinput()
30 * function could be in a common library). However, as that does not
31 * yet exist, we replicate it here so we can get the job done.
32 */
33
34 #define __EXTENSIONS__ /* to expose flockfile and friends in stdio.h */
35 #include <errno.h>
36 #include <libgen.h>
37 #include <malloc.h>
38 #include <signal.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <strings.h>
42 #include <stropts.h>
43 #include <unistd.h>
44 #include <termio.h>
45
46 #include <security/pam_appl.h>
47
48 static int ctl_c; /* was the conversation interrupted? */
49
50 /* ARGSUSED 1 */
51 static void
interrupt(int x)52 interrupt(int x)
53 {
54 ctl_c = 1;
55 }
56
57 /*
58 * getinput -- read user input from stdin abort on ^C
59 *
60 * Entry noecho == TRUE, don't echo input.
61 *
62 * Exit User's input.
63 * If interrupted, send SIGINT to caller for processing.
64 */
65 static char *
getinput(int noecho)66 getinput(int noecho)
67 {
68 struct termio tty;
69 unsigned short tty_flags = 0;
70 char input[PAM_MAX_RESP_SIZE + 1];
71 int c;
72 int i = 0;
73 void (*sig)(int);
74
75 ctl_c = 0;
76 sig = signal(SIGINT, interrupt);
77 if (noecho) {
78 (void) ioctl(fileno(stdin), TCGETA, &tty);
79 tty_flags = tty.c_lflag;
80 tty.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
81 (void) ioctl(fileno(stdin), TCSETAF, &tty);
82 }
83 /* go to end, but don't overflow PAM_MAX_RESP_SIZE */
84 flockfile(stdin);
85 while (ctl_c == 0 &&
86 (c = getchar_unlocked()) != '\n' &&
87 c != '\r' &&
88 c != EOF) {
89 if (i < PAM_MAX_RESP_SIZE) {
90 input[i++] = (char)c;
91 }
92 }
93 funlockfile(stdin);
94 input[i] = '\0';
95 if (noecho) {
96 tty.c_lflag = tty_flags;
97 (void) ioctl(fileno(stdin), TCSETAW, &tty);
98 (void) fputc('\n', stdout);
99 }
100 (void) signal(SIGINT, sig);
101 if (ctl_c == 1)
102 (void) kill(getpid(), SIGINT);
103
104 return (strdup(input));
105 }
106
107 /*
108 * Service modules don't clean up responses if an error is returned.
109 * Free responses here.
110 */
111 static void
free_resp(int num_msg,struct pam_response * pr)112 free_resp(int num_msg, struct pam_response *pr)
113 {
114 int i;
115 struct pam_response *r = pr;
116
117 if (pr == NULL)
118 return;
119
120 for (i = 0; i < num_msg; i++, r++) {
121
122 if (r->resp) {
123 /* clear before freeing -- may be a password */
124 bzero(r->resp, strlen(r->resp));
125 free(r->resp);
126 r->resp = NULL;
127 }
128 }
129 free(pr);
130 }
131
132 /* ARGSUSED */
133 int
pam_tty_conv(int num_msg,struct pam_message ** mess,struct pam_response ** resp,void * my_data)134 pam_tty_conv(int num_msg, struct pam_message **mess,
135 struct pam_response **resp, void *my_data)
136 {
137 struct pam_message *m = *mess;
138 struct pam_response *r = calloc(num_msg, sizeof (struct pam_response));
139 int i;
140
141 if (num_msg >= PAM_MAX_NUM_MSG) {
142 (void) fprintf(stderr, "too many messages %d >= %d\n",
143 num_msg, PAM_MAX_NUM_MSG);
144 free(r);
145 *resp = NULL;
146 return (PAM_CONV_ERR);
147 }
148
149 /* Talk it out */
150 *resp = r;
151 for (i = 0; i < num_msg; i++) {
152 int echo_off;
153
154 /* bad message from service module */
155 if (m->msg == NULL) {
156 (void) fprintf(stderr, "message[%d]: %d/NULL\n",
157 i, m->msg_style);
158 goto err;
159 }
160
161 /*
162 * fix up final newline:
163 * removed for prompts
164 * added back for messages
165 */
166 if (m->msg[strlen(m->msg)] == '\n')
167 m->msg[strlen(m->msg)] = '\0';
168
169 r->resp = NULL;
170 r->resp_retcode = 0;
171 echo_off = 0;
172 switch (m->msg_style) {
173
174 case PAM_PROMPT_ECHO_OFF:
175 echo_off = 1;
176 /*FALLTHROUGH*/
177
178 case PAM_PROMPT_ECHO_ON:
179 (void) fputs(m->msg, stdout);
180
181 r->resp = getinput(echo_off);
182 break;
183
184 case PAM_ERROR_MSG:
185 (void) fputs(m->msg, stderr);
186 (void) fputc('\n', stderr);
187 break;
188
189 case PAM_TEXT_INFO:
190 (void) fputs(m->msg, stdout);
191 (void) fputc('\n', stdout);
192 break;
193
194 default:
195 (void) fprintf(stderr, "message[%d]: unknown type "
196 "%d/val=\"%s\"\n",
197 i, m->msg_style, m->msg);
198 /* error, service module won't clean up */
199 goto err;
200 }
201 if (errno == EINTR)
202 goto err;
203
204 /* next message/response */
205 m++;
206 r++;
207 }
208 return (PAM_SUCCESS);
209
210 err:
211 free_resp(i, r);
212 *resp = NULL;
213 return (PAM_CONV_ERR);
214 }
215