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