login_skey.c revision 1.19
1192991Srmacklem/* $OpenBSD: login_skey.c,v 1.19 2007/07/26 17:48:41 millert Exp $ */ 2192991Srmacklem 3221032Srmacklem/* 4192991Srmacklem * Copyright (c) 2000, 2001, 2004 Todd C. Miller <Todd.Miller@courtesan.com> 5192991Srmacklem * 6192991Srmacklem * Permission to use, copy, modify, and distribute this software for any 7192991Srmacklem * purpose with or without fee is hereby granted, provided that the above 8192991Srmacklem * copyright notice and this permission notice appear in all copies. 9192991Srmacklem * 10192991Srmacklem * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11192991Srmacklem * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12192991Srmacklem * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13192991Srmacklem * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14192991Srmacklem * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15192991Srmacklem * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16192991Srmacklem * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17223483Suqs */ 18192991Srmacklem 19192991Srmacklem#include <sys/param.h> 20223483Suqs#include <sys/socket.h> 21223483Suqs#include <sys/stat.h> 22223483Suqs#include <sys/time.h> 23192991Srmacklem#include <sys/resource.h> 24223483Suqs 25192991Srmacklem#include <ctype.h> 26221032Srmacklem#include <errno.h> 27221032Srmacklem#include <fcntl.h> 28221032Srmacklem#include <paths.h> 29221032Srmacklem#include <pwd.h> 30221032Srmacklem#include <readpassphrase.h> 31221032Srmacklem#include <signal.h> 32221032Srmacklem#include <stdio.h> 33221032Srmacklem#include <stdlib.h> 34221032Srmacklem#include <string.h> 35221032Srmacklem#include <syslog.h> 36221032Srmacklem#include <unistd.h> 37221032Srmacklem 38221032Srmacklem#include <login_cap.h> 39221032Srmacklem#include <bsd_auth.h> 40221032Srmacklem#include <skey.h> 41221032Srmacklem 42221032Srmacklem#define MODE_LOGIN 0 43221032Srmacklem#define MODE_CHALLENGE 1 44221032Srmacklem#define MODE_RESPONSE 2 45221032Srmacklem 46221032Srmacklemvoid quit(int); 47221032Srmacklemvoid send_fd(int); 48221032Srmacklemvoid suspend(int); 49221032Srmacklem 50221032Srmacklemvolatile sig_atomic_t resumed; 51221032Srmacklemstruct skey skey; 52221032Srmacklem 53221032Srmacklemint 54221032Srmacklemmain(int argc, char *argv[]) 55221032Srmacklem{ 56221032Srmacklem FILE *back = NULL; 57221032Srmacklem char *user = NULL, *cp, *ep; 58192991Srmacklem char challenge[SKEY_MAX_CHALLENGE+17], response[SKEY_MAX_PW_LEN+1]; 59 const char *errstr; 60 int ch, fd = -1, haskey = 0, mode = MODE_LOGIN; 61 62 (void)signal(SIGINT, quit); 63 (void)signal(SIGQUIT, quit); 64 (void)signal(SIGALRM, quit); 65 (void)signal(SIGTSTP, suspend); 66 (void)setpriority(PRIO_PROCESS, 0, 0); 67 68 openlog(NULL, LOG_ODELAY, LOG_AUTH); 69 70 while ((ch = getopt(argc, argv, "ds:v:")) != -1) { 71 switch (ch) { 72 case 'd': 73 back = stdout; 74 break; 75 case 's': /* service */ 76 if (strcmp(optarg, "login") == 0) 77 mode = MODE_LOGIN; 78 else if (strcmp(optarg, "challenge") == 0) 79 mode = MODE_CHALLENGE; 80 else if (strcmp(optarg, "response") == 0) 81 mode = MODE_RESPONSE; 82 else { 83 syslog(LOG_ERR, "%s: invalid service", optarg); 84 exit(1); 85 } 86 break; 87 case 'v': 88 if (strncmp(optarg, "fd=", 3) == 0) { 89 fd = strtonum(optarg + 3, 0, INT_MAX, &errstr); 90 if (errstr != NULL) { 91 syslog(LOG_ERR, "fd is %s: %s", 92 errstr, optarg + 3); 93 fd = -1; 94 } 95 } 96 /* silently ignore unsupported variables */ 97 break; 98 default: 99 syslog(LOG_ERR, "usage error"); 100 exit(1); 101 } 102 } 103 argc -= optind; 104 argv += optind; 105 106 switch (argc) { 107 case 2: /* silently ignore class */ 108 case 1: 109 user = *argv; 110 break; 111 default: 112 syslog(LOG_ERR, "usage error"); 113 exit(1); 114 } 115 116 if (back == NULL && (back = fdopen(3, "r+")) == NULL) { 117 syslog(LOG_ERR, "reopening back channel: %m"); 118 exit(1); 119 } 120 121 /* 122 * Note: our skeychallenge2() will always fill in the challenge, 123 * even if it has to create a fake one. 124 */ 125 switch (mode) { 126 case MODE_LOGIN: 127 haskey = (skeychallenge2(fd, &skey, user, challenge) == 0); 128 strlcat(challenge, "\nS/Key Password:", sizeof(challenge)); 129 130 /* time out getting passphrase after 2 minutes to avoid a DoS */ 131 if (haskey) 132 alarm(120); 133 resumed = 0; 134 if (!readpassphrase(challenge, response, sizeof(response), 0)) 135 exit(1); 136 if (response[0] == '\0') 137 readpassphrase("S/Key Password [echo on]: ", 138 response, sizeof(response), RPP_ECHO_ON); 139 alarm(0); 140 if (resumed) { 141 /* 142 * We were suspended by the user. Our lock is 143 * no longer valid so we must regain it so 144 * an attacker cannot do a partial guess of 145 * an S/Key response already in progress. 146 */ 147 haskey = (skeylookup(&skey, user) == 0); 148 } 149 break; 150 151 case MODE_CHALLENGE: 152 haskey = (skeychallenge2(fd, &skey, user, challenge) == 0); 153 strlcat(challenge, "\nS/Key Password:", sizeof(challenge)); 154 fprintf(back, BI_VALUE " challenge %s\n", 155 auth_mkvalue(challenge)); 156 fprintf(back, BI_CHALLENGE "\n"); 157 fprintf(back, BI_FDPASS "\n"); 158 fflush(back); 159 send_fd(fileno(back)); 160 exit(0); 161 162 case MODE_RESPONSE: 163 /* read challenge */ 164 mode = -1; 165 cp = challenge; 166 ep = challenge + sizeof(challenge); 167 while (cp < ep && read(fileno(back), cp, 1) == 1) { 168 if (*cp++ == '\0') { 169 mode = MODE_CHALLENGE; 170 break; 171 } 172 } 173 if (mode != MODE_CHALLENGE) { 174 syslog(LOG_ERR, 175 "protocol error: bad/missing challenge"); 176 exit(1); 177 } 178 179 /* read response */ 180 cp = response; 181 ep = response + sizeof(response); 182 while (cp < ep && read(fileno(back), cp, 1) == 1) { 183 if (*cp++ == '\0') { 184 mode = MODE_RESPONSE; 185 break; 186 } 187 } 188 if (mode != MODE_RESPONSE) { 189 syslog(LOG_ERR, 190 "protocol error: bad/missing response"); 191 exit(1); 192 } 193 194 /* 195 * Since the entry is locked we do not need to compare 196 * the passed in challenge to the S/Key database but 197 * maybe we should anyway? 198 */ 199 haskey = (skeychallenge2(fd, &skey, user, challenge) == 0); 200 break; 201 } 202 203 /* 204 * Ignore keyboard interrupt/suspend during database update. 205 */ 206 signal(SIGINT, SIG_IGN); 207 signal(SIGQUIT, SIG_IGN); 208 signal(SIGTSTP, SIG_IGN); 209 210 if (haskey && skeyverify(&skey, response) == 0) { 211 if (mode == MODE_LOGIN) { 212 if (skey.n <= 1) 213 printf("Warning! You MUST change your " 214 "S/Key password now!\n"); 215 else if (skey.n < 5) 216 printf("Warning! Change S/Key password soon\n"); 217 } 218 fprintf(back, BI_AUTH "\n"); 219 fprintf(back, BI_SECURE "\n"); 220 exit(0); 221 } 222 fprintf(back, BI_REJECT "\n"); 223 exit(1); 224} 225 226/* ARGSUSED */ 227void 228quit(int signo) 229{ 230 231 _exit(1); 232} 233 234/* ARSGUSED */ 235void 236suspend(int signo) 237{ 238 sigset_t nset; 239 int save_errno = errno; 240 241 /* 242 * Unlock the skey record so we don't sleep holding the lock. 243 * Unblock SIGTSTP, set it to the default action and then 244 * resend it so we are suspended properly. 245 * When we resume, reblock SIGTSTP, reset the signal handler, 246 * set a flag and restore errno. 247 */ 248 alarm(0); 249 skey_unlock(&skey); 250 (void)signal(signo, SIG_DFL); 251 (void)sigemptyset(&nset); 252 (void)sigaddset(&nset, signo); 253 (void)sigprocmask(SIG_UNBLOCK, &nset, NULL); 254 (void)kill(getpid(), signo); 255 (void)sigprocmask(SIG_BLOCK, &nset, NULL); 256 (void)signal(signo, suspend); 257 resumed = 1; 258 errno = save_errno; 259} 260 261void 262send_fd(int sock) 263{ 264 struct msghdr msg; 265 struct cmsghdr *cmp; 266 char cmsgbuf[CMSG_SPACE(sizeof(int))]; 267 268 memset(&msg, 0, sizeof(msg)); 269 msg.msg_control = cmsgbuf; 270 msg.msg_controllen = CMSG_LEN(sizeof(int)); 271 272 cmp = CMSG_FIRSTHDR(&msg); 273 cmp->cmsg_len = CMSG_LEN(sizeof(int)); 274 cmp->cmsg_level = SOL_SOCKET; 275 cmp->cmsg_type = SCM_RIGHTS; 276 277 *(int *)CMSG_DATA(cmp) = fileno(skey.keyfile); 278 279 if (sendmsg(sock, &msg, 0) < 0) 280 syslog(LOG_ERR, "sendmsg: %m"); 281} 282