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 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 * 66 getinput(int noecho) 67 { 68 struct termio tty; 69 unsigned short tty_flags; 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 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 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