/* * Copyright conserver.com, 2000 * * Maintainer/Enhancer: Bryan Stansell (bryan@conserver.com) * * Copyright GNAC, Inc., 1998 */ /* * Copyright (c) 1990 The Ohio State University. * All rights reserved. * * Redistribution and use in source and binary forms are permitted * provided that: (1) source distributions retain this entire copyright * notice and comment, and (2) distributions including binaries display * the following acknowledgement: ``This product includes software * developed by The Ohio State University and its contributors'' * in the documentation or other materials provided with the distribution * and in all advertising materials mentioning features or use of this * software. Neither the name of the University nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #include #include #include #include #include #include #include #include #include #include #include #if HAVE_OPENSSL # include #endif #if HAVE_GSSAPI # include #endif int fAll = 0, fNoinit = 0, fVersion = 0, fStrip = 0, fReopen = 0, fNoautoreup = 0, fSyntaxOnly = 0; char *pcConfig = CONFIGFILE; int cMaxMemb = MAXMEMB; #if USE_IPV6 struct addrinfo *bindAddr; struct addrinfo *bindBaseAddr; #else in_addr_t bindAddr = INADDR_ANY; unsigned short bindPort; unsigned short bindBasePort; struct sockaddr_in in_port; #endif static STRING *startedMsg = (STRING *)0; CONFIG *optConf = (CONFIG *)0; CONFIG *config = (CONFIG *)0; char *interface = (char *)0; CONFIG defConfig = { (STRING *)0, FLAGTRUE, 'r', FLAGFALSE, LOGFILEPATH, PASSWDFILE, DEFPORT, FLAGTRUE, FLAGTRUE, 0, DEFBASEPORT, (char *)0, 0 #if HAVE_SETPROCTITLE , FLAGFALSE #endif #if HAVE_OPENSSL , (char *)0, FLAGTRUE, FLAGFALSE, (char *)0 #endif }; CONSFILE *unifiedlog = (CONSFILE *)0; #if HAVE_DMALLOC && DMALLOC_MARK_MAIN unsigned long dmallocMarkMain = 0; #endif #if HAVE_OPENSSL # if OPENSSL_VERSION_NUMBER < 0x10100000L int DH_set0_pqg(DH *dh, BIGNUM * p, BIGNUM * q, BIGNUM * g) { /* If the fields p and g in d are NULL, the corresponding input * parameters MUST be non-NULL. q may remain NULL. */ if ((dh->p == NULL && p == NULL) || (dh->g == NULL && g == NULL)) return 0; if (p != NULL) { BN_free(dh->p); dh->p = p; } if (q != NULL) { BN_free(dh->q); dh->q = q; } if (g != NULL) { BN_free(dh->g); dh->g = g; } if (q != NULL) { dh->length = BN_num_bits(q); } return 1; } # endif/* OPENSSL_VERSION_NUMBER < 0x10100000L */ SSL_CTX *ctx = (SSL_CTX *)0; DH *dh512 = (DH *)0; DH *dh1024 = (DH *)0; DH *dh2048 = (DH *)0; DH *dh4096 = (DH *)0; DH * DHFromArray(unsigned char *dh_p, size_t dh_p_size, unsigned char *dh_g, size_t dh_g_size) { DH *dh; BIGNUM *p, *g; p = BN_bin2bn(dh_p, dh_p_size, NULL); if (p == NULL) { return (NULL); } g = BN_bin2bn(dh_g, dh_g_size, NULL); if (g == NULL) { BN_free(g); return (NULL); } if ((dh = DH_new()) == NULL) { BN_free(p); BN_free(g); return (NULL); } if (!DH_set0_pqg(dh, p, NULL, g)) { BN_free(p); BN_free(g); DH_free(dh); return (NULL); } return (dh); } DH * GetDH512(void) { static unsigned char dh512_p[] = { 0xF5, 0x2A, 0xFF, 0x3C, 0xE1, 0xB1, 0x29, 0x40, 0x18, 0x11, 0x8D, 0x7C, 0x84, 0xA7, 0x0A, 0x72, 0xD6, 0x86, 0xC4, 0x03, 0x19, 0xC8, 0x07, 0x29, 0x7A, 0xCA, 0x95, 0x0C, 0xD9, 0x96, 0x9F, 0xAB, 0xD0, 0x0A, 0x50, 0x9B, 0x02, 0x46, 0xD3, 0x08, 0x3D, 0x66, 0xA4, 0x5D, 0x41, 0x9F, 0x9C, 0x7C, 0xBD, 0x89, 0x4B, 0x22, 0x19, 0x26, 0xBA, 0xAB, 0xA2, 0x5E, 0xC3, 0x55, 0xE9, 0x2A, 0x05, 0x5F, }; static unsigned char dh512_g[] = { 0x02, }; return DHFromArray(dh512_p, sizeof(dh512_p), dh512_g, sizeof(dh512_g)); } DH * GetDH1024(void) { static unsigned char dh1024_p[] = { 0xF4, 0x88, 0xFD, 0x58, 0x4E, 0x49, 0xDB, 0xCD, 0x20, 0xB4, 0x9D, 0xE4, 0x91, 0x07, 0x36, 0x6B, 0x33, 0x6C, 0x38, 0x0D, 0x45, 0x1D, 0x0F, 0x7C, 0x88, 0xB3, 0x1C, 0x7C, 0x5B, 0x2D, 0x8E, 0xF6, 0xF3, 0xC9, 0x23, 0xC0, 0x43, 0xF0, 0xA5, 0x5B, 0x18, 0x8D, 0x8E, 0xBB, 0x55, 0x8C, 0xB8, 0x5D, 0x38, 0xD3, 0x34, 0xFD, 0x7C, 0x17, 0x57, 0x43, 0xA3, 0x1D, 0x18, 0x6C, 0xDE, 0x33, 0x21, 0x2C, 0xB5, 0x2A, 0xFF, 0x3C, 0xE1, 0xB1, 0x29, 0x40, 0x18, 0x11, 0x8D, 0x7C, 0x84, 0xA7, 0x0A, 0x72, 0xD6, 0x86, 0xC4, 0x03, 0x19, 0xC8, 0x07, 0x29, 0x7A, 0xCA, 0x95, 0x0C, 0xD9, 0x96, 0x9F, 0xAB, 0xD0, 0x0A, 0x50, 0x9B, 0x02, 0x46, 0xD3, 0x08, 0x3D, 0x66, 0xA4, 0x5D, 0x41, 0x9F, 0x9C, 0x7C, 0xBD, 0x89, 0x4B, 0x22, 0x19, 0x26, 0xBA, 0xAB, 0xA2, 0x5E, 0xC3, 0x55, 0xE9, 0x2F, 0x78, 0xC7, }; static unsigned char dh1024_g[] = { 0x02, }; return DHFromArray(dh1024_p, sizeof(dh1024_p), dh1024_g, sizeof(dh1024_g)); } DH * GetDH2048(void) { static unsigned char dh2048_p[] = { 0xF6, 0x42, 0x57, 0xB7, 0x08, 0x7F, 0x08, 0x17, 0x72, 0xA2, 0xBA, 0xD6, 0xA9, 0x42, 0xF3, 0x05, 0xE8, 0xF9, 0x53, 0x11, 0x39, 0x4F, 0xB6, 0xF1, 0x6E, 0xB9, 0x4B, 0x38, 0x20, 0xDA, 0x01, 0xA7, 0x56, 0xA3, 0x14, 0xE9, 0x8F, 0x40, 0x55, 0xF3, 0xD0, 0x07, 0xC6, 0xCB, 0x43, 0xA9, 0x94, 0xAD, 0xF7, 0x4C, 0x64, 0x86, 0x49, 0xF8, 0x0C, 0x83, 0xBD, 0x65, 0xE9, 0x17, 0xD4, 0xA1, 0xD3, 0x50, 0xF8, 0xF5, 0x59, 0x5F, 0xDC, 0x76, 0x52, 0x4F, 0x3D, 0x3D, 0x8D, 0xDB, 0xCE, 0x99, 0xE1, 0x57, 0x92, 0x59, 0xCD, 0xFD, 0xB8, 0xAE, 0x74, 0x4F, 0xC5, 0xFC, 0x76, 0xBC, 0x83, 0xC5, 0x47, 0x30, 0x61, 0xCE, 0x7C, 0xC9, 0x66, 0xFF, 0x15, 0xF9, 0xBB, 0xFD, 0x91, 0x5E, 0xC7, 0x01, 0xAA, 0xD3, 0x5B, 0x9E, 0x8D, 0xA0, 0xA5, 0x72, 0x3A, 0xD4, 0x1A, 0xF0, 0xBF, 0x46, 0x00, 0x58, 0x2B, 0xE5, 0xF4, 0x88, 0xFD, 0x58, 0x4E, 0x49, 0xDB, 0xCD, 0x20, 0xB4, 0x9D, 0xE4, 0x91, 0x07, 0x36, 0x6B, 0x33, 0x6C, 0x38, 0x0D, 0x45, 0x1D, 0x0F, 0x7C, 0x88, 0xB3, 0x1C, 0x7C, 0x5B, 0x2D, 0x8E, 0xF6, 0xF3, 0xC9, 0x23, 0xC0, 0x43, 0xF0, 0xA5, 0x5B, 0x18, 0x8D, 0x8E, 0xBB, 0x55, 0x8C, 0xB8, 0x5D, 0x38, 0xD3, 0x34, 0xFD, 0x7C, 0x17, 0x57, 0x43, 0xA3, 0x1D, 0x18, 0x6C, 0xDE, 0x33, 0x21, 0x2C, 0xB5, 0x2A, 0xFF, 0x3C, 0xE1, 0xB1, 0x29, 0x40, 0x18, 0x11, 0x8D, 0x7C, 0x84, 0xA7, 0x0A, 0x72, 0xD6, 0x86, 0xC4, 0x03, 0x19, 0xC8, 0x07, 0x29, 0x7A, 0xCA, 0x95, 0x0C, 0xD9, 0x96, 0x9F, 0xAB, 0xD0, 0x0A, 0x50, 0x9B, 0x02, 0x46, 0xD3, 0x08, 0x3D, 0x66, 0xA4, 0x5D, 0x41, 0x9F, 0x9C, 0x7C, 0xBD, 0x89, 0x4B, 0x22, 0x19, 0x26, 0xBA, 0xAB, 0xA2, 0x5E, 0xC3, 0x55, 0xE9, 0x32, 0x0B, 0x3B, }; static unsigned char dh2048_g[] = { 0x02, }; return DHFromArray(dh2048_p, sizeof(dh2048_p), dh2048_g, sizeof(dh2048_g)); } DH * GetDH4096(void) { static unsigned char dh4096_p[] = { 0xFA, 0x14, 0x72, 0x52, 0xC1, 0x4D, 0xE1, 0x5A, 0x49, 0xD4, 0xEF, 0x09, 0x2D, 0xC0, 0xA8, 0xFD, 0x55, 0xAB, 0xD7, 0xD9, 0x37, 0x04, 0x28, 0x09, 0xE2, 0xE9, 0x3E, 0x77, 0xE2, 0xA1, 0x7A, 0x18, 0xDD, 0x46, 0xA3, 0x43, 0x37, 0x23, 0x90, 0x97, 0xF3, 0x0E, 0xC9, 0x03, 0x50, 0x7D, 0x65, 0xCF, 0x78, 0x62, 0xA6, 0x3A, 0x62, 0x22, 0x83, 0xA1, 0x2F, 0xFE, 0x79, 0xBA, 0x35, 0xFF, 0x59, 0xD8, 0x1D, 0x61, 0xDD, 0x1E, 0x21, 0x13, 0x17, 0xFE, 0xCD, 0x38, 0x87, 0x9E, 0xF5, 0x4F, 0x79, 0x10, 0x61, 0x8D, 0xD4, 0x22, 0xF3, 0x5A, 0xED, 0x5D, 0xEA, 0x21, 0xE9, 0x33, 0x6B, 0x48, 0x12, 0x0A, 0x20, 0x77, 0xD4, 0x25, 0x60, 0x61, 0xDE, 0xF6, 0xB4, 0x4F, 0x1C, 0x63, 0x40, 0x8B, 0x3A, 0x21, 0x93, 0x8B, 0x79, 0x53, 0x51, 0x2C, 0xCA, 0xB3, 0x7B, 0x29, 0x56, 0xA8, 0xC7, 0xF8, 0xF4, 0x7B, 0x08, 0x5E, 0xA6, 0xDC, 0xA2, 0x45, 0x12, 0x56, 0xDD, 0x41, 0x92, 0xF2, 0xDD, 0x5B, 0x8F, 0x23, 0xF0, 0xF3, 0xEF, 0xE4, 0x3B, 0x0A, 0x44, 0xDD, 0xED, 0x96, 0x84, 0xF1, 0xA8, 0x32, 0x46, 0xA3, 0xDB, 0x4A, 0xBE, 0x3D, 0x45, 0xBA, 0x4E, 0xF8, 0x03, 0xE5, 0xDD, 0x6B, 0x59, 0x0D, 0x84, 0x1E, 0xCA, 0x16, 0x5A, 0x8C, 0xC8, 0xDF, 0x7C, 0x54, 0x44, 0xC4, 0x27, 0xA7, 0x3B, 0x2A, 0x97, 0xCE, 0xA3, 0x7D, 0x26, 0x9C, 0xAD, 0xF4, 0xC2, 0xAC, 0x37, 0x4B, 0xC3, 0xAD, 0x68, 0x84, 0x7F, 0x99, 0xA6, 0x17, 0xEF, 0x6B, 0x46, 0x3A, 0x7A, 0x36, 0x7A, 0x11, 0x43, 0x92, 0xAD, 0xE9, 0x9C, 0xFB, 0x44, 0x6C, 0x3D, 0x82, 0x49, 0xCC, 0x5C, 0x6A, 0x52, 0x42, 0xF8, 0x42, 0xFB, 0x44, 0xF9, 0x39, 0x73, 0xFB, 0x60, 0x79, 0x3B, 0xC2, 0x9E, 0x0B, 0xDC, 0xD4, 0xA6, 0x67, 0xF7, 0x66, 0x3F, 0xFC, 0x42, 0x3B, 0x1B, 0xDB, 0x4F, 0x66, 0xDC, 0xA5, 0x8F, 0x66, 0xF9, 0xEA, 0xC1, 0xED, 0x31, 0xFB, 0x48, 0xA1, 0x82, 0x7D, 0xF8, 0xE0, 0xCC, 0xB1, 0xC7, 0x03, 0xE4, 0xF8, 0xB3, 0xFE, 0xB7, 0xA3, 0x13, 0x73, 0xA6, 0x7B, 0xC1, 0x0E, 0x39, 0xC7, 0x94, 0x48, 0x26, 0x00, 0x85, 0x79, 0xFC, 0x6F, 0x7A, 0xAF, 0xC5, 0x52, 0x35, 0x75, 0xD7, 0x75, 0xA4, 0x40, 0xFA, 0x14, 0x74, 0x61, 0x16, 0xF2, 0xEB, 0x67, 0x11, 0x6F, 0x04, 0x43, 0x3D, 0x11, 0x14, 0x4C, 0xA7, 0x94, 0x2A, 0x39, 0xA1, 0xC9, 0x90, 0xCF, 0x83, 0xC6, 0xFF, 0x02, 0x8F, 0xA3, 0x2A, 0xAC, 0x26, 0xDF, 0x0B, 0x8B, 0xBE, 0x64, 0x4A, 0xF1, 0xA1, 0xDC, 0xEE, 0xBA, 0xC8, 0x03, 0x82, 0xF6, 0x62, 0x2C, 0x5D, 0xB6, 0xBB, 0x13, 0x19, 0x6E, 0x86, 0xC5, 0x5B, 0x2B, 0x5E, 0x3A, 0xF3, 0xB3, 0x28, 0x6B, 0x70, 0x71, 0x3A, 0x8E, 0xFF, 0x5C, 0x15, 0xE6, 0x02, 0xA4, 0xCE, 0xED, 0x59, 0x56, 0xCC, 0x15, 0x51, 0x07, 0x79, 0x1A, 0x0F, 0x25, 0x26, 0x27, 0x30, 0xA9, 0x15, 0xB2, 0xC8, 0xD4, 0x5C, 0xCC, 0x30, 0xE8, 0x1B, 0xD8, 0xD5, 0x0F, 0x19, 0xA8, 0x80, 0xA4, 0xC7, 0x01, 0xAA, 0x8B, 0xBA, 0x53, 0xBB, 0x47, 0xC2, 0x1F, 0x6B, 0x54, 0xB0, 0x17, 0x60, 0xED, 0x79, 0x21, 0x95, 0xB6, 0x05, 0x84, 0x37, 0xC8, 0x03, 0xA4, 0xDD, 0xD1, 0x06, 0x69, 0x8F, 0x4C, 0x39, 0xE0, 0xC8, 0x5D, 0x83, 0x1D, 0xBE, 0x6A, 0x9A, 0x99, 0xF3, 0x9F, 0x0B, 0x45, 0x29, 0xD4, 0xCB, 0x29, 0x66, 0xEE, 0x1E, 0x7E, 0x3D, 0xD7, 0x13, 0x4E, 0xDB, 0x90, 0x90, 0x58, 0xCB, 0x5E, 0x9B, 0xCD, 0x2E, 0x2B, 0x0F, 0xA9, 0x4E, 0x78, 0xAC, 0x05, 0x11, 0x7F, 0xE3, 0x9E, 0x27, 0xD4, 0x99, 0xE1, 0xB9, 0xBD, 0x78, 0xE1, 0x84, 0x41, 0xA0, 0xDF, }; static unsigned char dh4096_g[] = { 0x02, }; return DHFromArray(dh4096_p, sizeof(dh4096_p), dh4096_g, sizeof(dh4096_g)); } DH * TmpDHCallback(SSL *ssl, int is_export, int keylength) { CONDDEBUG((1, "TmpDHCallback(): asked for a DH key length %u", keylength)); switch (keylength) { case 512: if (dh512 == (DH *)0) dh512 = GetDH512(); return dh512; case 1024: if (dh1024 == (DH *)0) dh1024 = GetDH1024(); return dh1024; case 2048: if (dh2048 == (DH *)0) dh2048 = GetDH2048(); return dh2048; default: if (dh4096 == (DH *)0) dh4096 = GetDH4096(); return dh4096; } } void SetupSSL(void) { if (ctx == (SSL_CTX *)0) { char *ciphers; int verifymode; # if OPENSSL_VERSION_NUMBER < 0x10100000L SSL_load_error_strings(); if (!SSL_library_init()) { Error("SetupSSL(): SSL_library_init() failed"); Bye(EX_SOFTWARE); } # endif/* OPENSSL_VERSION_NUMBER < 0x10100000L */ if ((ctx = SSL_CTX_new(TLS_method())) == (SSL_CTX *)0) { Error("SetupSSL(): SSL_CTX_new() failed"); Bye(EX_SOFTWARE); } if (SSL_CTX_set_default_verify_paths(ctx) != 1) { Error ("SetupSSL(): could not load SSL default CA file and/or directory"); Bye(EX_SOFTWARE); } if (config->sslcredentials != (char *)0) { if (SSL_CTX_use_certificate_chain_file (ctx, config->sslcredentials) != 1) { Error ("SetupSSL(): could not load SSL certificate from `%s'", config->sslcredentials); Bye(EX_SOFTWARE); } if (SSL_CTX_use_PrivateKey_file (ctx, config->sslcredentials, SSL_FILETYPE_PEM) != 1) { Error ("SetupSSL(): could not load SSL private key from `%s'", config->sslcredentials); Bye(EX_SOFTWARE); } ciphers = "ALL:!LOW:!EXP:!MD5:!aNULL:@STRENGTH"; } else { ciphers = "ALL:aNULL:!LOW:!EXP:!MD5:@STRENGTH" CIPHER_SEC0; } if (config->sslcacertificatefile != (char *)0) { STACK_OF(X509_NAME) * cert_names; cert_names = SSL_load_client_CA_file(config->sslcacertificatefile); if (cert_names != NULL) { SSL_CTX_set_client_CA_list(ctx, cert_names); if (SSL_CTX_load_verify_locations (ctx, config->sslcacertificatefile, NULL) != 1) { Error("Could not setup CA certificate file to '%s'", config->sslcacertificatefile); Bye(EX_UNAVAILABLE); } } else { Error ("SetupSSL(): could not load SSL client CA list from `%s'", config->sslcacertificatefile); Bye(EX_SOFTWARE); } } verifymode = SSL_VERIFY_PEER; if (config->sslreqclientcert == FLAGTRUE) verifymode |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT; SSL_CTX_set_verify(ctx, verifymode, SSLVerifyCallback); SSL_CTX_set_options(ctx, SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_SINGLE_DH_USE); SSL_CTX_set_mode(ctx, SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER | SSL_MODE_AUTO_RETRY); SSL_CTX_set_tmp_dh_callback(ctx, TmpDHCallback); if (SSL_CTX_set_cipher_list(ctx, ciphers) != 1) { Error("SetupSSL(): setting SSL cipher list failed"); Bye(EX_SOFTWARE); } /* might want to turn this back on at some point, but i can't * see why right now. */ SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF); } } #endif #if HAVE_GSSAPI gss_name_t gss_myname = GSS_C_NO_NAME; gss_cred_id_t gss_mycreds = GSS_C_NO_CREDENTIAL; void SetupGSSAPI(void) { OM_uint32 stmaj, stmin; char namestr[128]; gss_buffer_desc namebuf; snprintf(namestr, 128, "host@%s", myHostname); namebuf.value = namestr; namebuf.length = strlen(namestr) + 1; stmaj = gss_import_name(&stmin, &namebuf, GSS_C_NT_HOSTBASED_SERVICE, &gss_myname); /* XXX: handle error */ if (stmaj != GSS_S_COMPLETE) { Error("gss_import_name failed"); } /* Get some initial credentials */ stmaj = gss_acquire_cred(&stmin, gss_myname, 0, GSS_C_NULL_OID_SET, GSS_C_ACCEPT, &gss_mycreds, NULL, NULL); if (stmaj != GSS_S_COMPLETE) { Error("Could not acquire GSS-API credentials"); } } #endif void ReopenLogfile(void) { static int tag = 1; /* redirect stdout and stderr to the logfile. * * first time through any problems will show up (stderr still there). * after that, all bets are off...probably not see the errors (well, * aside from the tail of the old logfile, if it was rolled). */ if (config->daemonmode != FLAGTRUE) return; close(1); /* so, if we aren't in daemon mode, we just return before closing * anything. if we are, there are two possibilities. first, if * logfile is set, we close fd 1, open a file, etc. all should be * well. if logfile isn't set, we end up closing fd 1 and 2 and * returning (in case the logfile was set and then unset [config * file change]). */ if (config->logfile == (char *)0) { close(2); return; } if (1 != open(config->logfile, O_WRONLY | O_CREAT | O_APPEND, 0644)) { tag = 0; Error("ReopenLogfile(): open(%s): %s", config->logfile, strerror(errno)); Bye(EX_TEMPFAIL); } close(2); dup(1); if (isMaster && tag) { Msg(MyVersion()); Msg(startedMsg->string); } tag = 0; } void ReopenUnifiedlog(void) { /* close any existing */ if (unifiedlog != (CONSFILE *)0) FileClose(&unifiedlog); /* return if we aren't opening again */ if (config->unifiedlog == (char *)0) return; /* open a new one */ if ((unifiedlog = FileOpen(config->unifiedlog, O_WRONLY | O_CREAT | O_APPEND, 0644)) == (CONSFILE *)0) { Error("ReopenUnifiedlog(): open(%s): %s", config->unifiedlog, strerror(errno)); return; } } /* become a daemon (ksb) */ static void Daemonize(void) { int res; #if !HAVE_SETSID int td; #endif Msg("daemonizing"); SimpleSignal(SIGQUIT, SIG_IGN); SimpleSignal(SIGINT, SIG_IGN); #if defined(SIGTTOU) SimpleSignal(SIGTTOU, SIG_IGN); #endif #if defined(SIGTTIN) SimpleSignal(SIGTTIN, SIG_IGN); #endif #if defined(SIGTSTP) SimpleSignal(SIGTSTP, SIG_IGN); #endif #if defined(SIGXFSZ) SimpleSignal(SIGXFSZ, SIG_IGN); #endif fflush(stdout); fflush(stderr); switch (res = fork()) { case -1: Error("Daemonize(): fork(): %s", strerror(errno)); Bye(EX_TEMPFAIL); case 0: thepid = getpid(); break; default: Bye(EX_OK); } ReopenLogfile(); /* Further disassociate this process from the terminal * Maybe this will allow you to start a daemon from rsh, * i.e. with no controlling terminal. */ #if HAVE_SETSID setsid(); #else tcsetpgrp(0, getpid()); /* lose our controlling terminal */ if (-1 != (td = open("/dev/tty", O_RDWR, 0600))) { ioctl(td, TIOCNOTTY, (char *)0); close(td); } #endif } /* output a long message to the user (ksb) */ static void Usage(int wantfull) { static char u_terse[] = "[-7dDEFhinoRSuvV] [-a type] [-m max] [-M master] [-p port] [-b port] [-c cred] [-C config] [-P passwd] [-L logfile] [-O min] [-U logfile]"; static char *full[] = { "7 strip the high bit off all console data", "a type set the default access type", "b port base port for secondary channel (any by default)", #if HAVE_OPENSSL "c cred load an SSL certificate and key from the PEM encoded file", #else "c cred ignored - encryption not compiled into code", #endif "C config give a new config file to the server process", "d become a daemon, redirecting stdout/stderr to logfile", "D enable debug output, sent to stderr", #if HAVE_OPENSSL "E don't require encrypted client connections", #else "E ignored - encryption not compiled into code", #endif "F do not automatically reinitialize failed consoles", "h output this message", "i initialize console connections on demand", "L logfile give a new logfile path to the server process", "m max maximum consoles managed per process", #if USE_UNIX_DOMAIN_SOCKETS "M master directory that holds the Unix domain sockets", #else "M master address to listen on (all addresses by default)", #endif "n obsolete - see -u", "o reopen downed console on client connect", "O min reopen all downed consoles every minutes", #if USE_UNIX_DOMAIN_SOCKETS "p port ignored - Unix domain sockets compiled into code", #else "p port port to listen on", #endif "P passwd give a new passwd file to the server process", "R disable automatic client redirection", "S syntax check of configuration file", "u copy \"unloved\" console data to stdout", "U logfile copy all console data to the \"unified\" logfile", "v be verbose on startup", "V output version info", (char *)0 }; fprintf(stderr, "usage: %s %s\n", progname, u_terse); if (wantfull) { int i; for (i = 0; full[i] != (char *)0; i++) fprintf(stderr, "\t%s\n", full[i]); } } /* show the user our version info (ksb) */ static void Version(void) { static STRING *acA1 = (STRING *)0; static STRING *acA2 = (STRING *)0; int i; char *optionlist[] = { #if HAVE_DMALLOC "dmalloc", #endif #if HAVE_FREEIPMI "freeipmi", #endif #if USE_LIBWRAP "libwrap", #endif #if HAVE_OPENSSL "openssl", #endif #if HAVE_PAM "pam", #endif #if TRUST_REVERSE_DNS "trustrevdns", #endif #if USE_UNIX_DOMAIN_SOCKETS "uds", #endif (char *)0 }; if (acA1 == (STRING *)0) acA1 = AllocString(); if (acA2 == (STRING *)0) acA2 = AllocString(); isMultiProc = 0; Msg(MyVersion()); Msg("default access type `%c'", defConfig.defaultaccess); Msg("default escape sequence `%s%s'", FmtCtl(DEFATTN, acA1), FmtCtl(DEFESC, acA2)); Msg("default configuration in `%s'", CONFIGFILE); Msg("default password in `%s'", defConfig.passwdfile); Msg("default logfile is `%s'", defConfig.logfile); Msg("default pidfile is `%s'", PIDFILE); Msg("default limit is %d member%s per group", MAXMEMB, MAXMEMB == 1 ? "" : "s"); #if USE_UNIX_DOMAIN_SOCKETS Msg("default socket directory `%s'", UDSDIR); #else Msg("default primary port referenced as `%s'", defConfig.primaryport); Msg("default secondary base port referenced as `%s'", defConfig.secondaryport); #endif BuildString((char *)0, acA1); if (optionlist[0] == (char *)0) BuildString("none", acA1); for (i = 0; optionlist[i] != (char *)0; i++) { if (i == 0) BuildString(optionlist[i], acA1); else { BuildString(", ", acA1); BuildString(optionlist[i], acA1); } } Msg("options: %s", acA1->string); #if HAVE_DMALLOC BuildString((char *)0, acA1); BuildStringChar('0' + DMALLOC_VERSION_MAJOR, acA1); BuildStringChar('.', acA1); BuildStringChar('0' + DMALLOC_VERSION_MINOR, acA1); BuildStringChar('.', acA1); BuildStringChar('0' + DMALLOC_VERSION_PATCH, acA1); # if defined(DMALLOC_VERSION_BETA) if (DMALLOC_VERSION_BETA != 0) { BuildString("-b", acA1); BuildStringChar('0' + DMALLOC_VERSION_BETA, acA1); } # endif Msg("dmalloc version: %s", acA1->string); #endif #if HAVE_FREEIPMI BuildString((char *)0, acA1); BuildStringChar('0' + LIBIPMICONSOLE_VERSION_MAJOR, acA1); BuildStringChar('.', acA1); BuildStringChar('0' + LIBIPMICONSOLE_VERSION_MINOR, acA1); BuildStringChar('.', acA1); BuildStringChar('0' + LIBIPMICONSOLE_VERSION_PATCH, acA1); Msg("freeipmi version: %s", acA1->string); #endif #if HAVE_OPENSSL Msg("openssl version: %s", OPENSSL_VERSION_TEXT); #endif Msg("built with `%s'", CONFIGINVOCATION); if (fVerbose) printf(COPYRIGHT); Bye(EX_OK); } void DestroyDataStructures(void) { GRPENT *pGE; REMOTE *pRC; ACCESS *pAC; while (pGroups != (GRPENT *)0) { pGE = pGroups->pGEnext; DestroyGroup(pGroups); pGroups = pGE; } while (pRCList != (REMOTE *)0) { pRC = pRCList->pRCnext; DestroyRemoteConsole(pRCList); pRCList = pRC; } while (pACList != (ACCESS *)0) { pAC = pACList->pACnext; DestroyAccessList(pACList); pACList = pAC; } DestroyConsentUsers(&pADList); DestroyConsentUsers(&pLUList); DestroyConfig(pConfig); DestroyConfig(optConf); DestroyConfig(config); #if HAVE_OPENSSL if (ctx != (SSL_CTX *)0) SSL_CTX_free(ctx); if (dh512 != (DH *)0) DH_free(dh512); if (dh1024 != (DH *)0) DH_free(dh1024); if (dh2048 != (DH *)0) DH_free(dh2048); if (dh4096 != (DH *)0) DH_free(dh4096); #endif #if USE_IPV6 /* clean up addrinfo stucts */ freeaddrinfo(bindAddr); freeaddrinfo(bindBaseAddr); #else if (myAddrs != (struct in_addr *)0) free(myAddrs); #endif DestroyBreakList(); DestroyTaskList(); DestroyStrings(); DestroyUserList(); if (substData != (SUBST *)0) free(substData); } void SummarizeDataStructures(void) { GRPENT *pGE; REMOTE *pRC; ACCESS *pAC; STRING *str; CONSENT *pCE; NAMES *usr; int count; long size; long total = 0; extern STRING *allStrings; if (!fDebug) return; for (size = 0, count = 0, pGE = pGroups; pGE != (GRPENT *)0; pGE = pGE->pGEnext, count++) { size += sizeof(GRPENT); } CONDDEBUG((1, "Memory Usage (GRPENT objects): %ld (%d)", size, count)); total += size; for (size = 0, count = 0, pGE = pGroups; pGE != (GRPENT *)0; pGE = pGE->pGEnext) { for (pCE = pGE->pCElist; pCE != (CONSENT *)0; pCE = pCE->pCEnext, count++) { size += strlen(pCE->server) + sizeof(CONSENT); if (pCE->host != (char *)0) size += strlen(pCE->server); if (pCE->device != (char *)0) size += strlen(pCE->device); if (pCE->exec != (char *)0) size += strlen(pCE->exec); if (pCE->master != (char *)0) size += strlen(pCE->master); if (pCE->logfile != (char *)0) size += strlen(pCE->logfile); if (pCE->initcmd != (char *)0) size += strlen(pCE->initcmd); if (pCE->execSlave != (char *)0) size += strlen(pCE->execSlave); if (pCE->motd != (char *)0) size += strlen(pCE->motd); if (pCE->idlestring != (char *)0) size += strlen(pCE->idlestring); if (pCE->replstring != (char *)0) size += strlen(pCE->replstring); if (pCE->tasklist != (char *)0) size += strlen(pCE->tasklist); if (pCE->breaklist != (char *)0) size += strlen(pCE->breaklist); #if HAVE_FREEIPMI if (pCE->username != (char *)0) size += strlen(pCE->username); if (pCE->password != (char *)0) size += strlen(pCE->password); #endif if (pCE->fdlog != (CONSFILE *)0) size += sizeof(CONSFILE); if (pCE->cofile != (CONSFILE *)0) size += sizeof(CONSFILE); if (pCE->initfile != (CONSFILE *)0) size += sizeof(CONSFILE); if (pCE->taskfile != (CONSFILE *)0) size += sizeof(CONSFILE); if (pCE->aliases != (NAMES *)0) { NAMES *n; for (n = pCE->aliases; n != (NAMES *)0; n = n->next) { size += sizeof(NAMES) + strlen(n->name); } } if (pCE->ro) { CONSENTUSERS *u; for (u = pCE->ro; u != (CONSENTUSERS *)0; u = u->next) { size += sizeof(CONSENTUSERS) + strlen(u->user->name); } } if (pCE->rw) { CONSENTUSERS *u; for (u = pCE->rw; u != (CONSENTUSERS *)0; u = u->next) { size += sizeof(CONSENTUSERS) + strlen(u->user->name); } } } } CONDDEBUG((1, "Memory Usage (CONSENT objects): %ld (%d)", size, count)); total += size; for (size = 0, count = 0, pRC = pRCList; pRC != (REMOTE *)0; pRC = pRC->pRCnext, count++) { size += strlen(pRC->rserver) + strlen(pRC->rhost) + sizeof(REMOTE); if (pRC->aliases != (NAMES *)0) { NAMES *n; for (n = pRC->aliases; n != (NAMES *)0; n = n->next) { size += sizeof(NAMES) + strlen(n->name); } } } CONDDEBUG((1, "Memory Usage (REMOTE objects): %ld (%d)", size, count)); total += size; for (size = 0, count = 0, pAC = pACList; pAC != (ACCESS *)0; pAC = pAC->pACnext, count++) { size += strlen(pAC->pcwho) + sizeof(ACCESS); } CONDDEBUG((1, "Memory Usage (ACCESS objects): %ld (%d)", size, count)); total += size; for (size = 0, count = 0, str = allStrings; str != (STRING *)0; str = str->next, count++) { size += str->allocated + sizeof(STRING); } CONDDEBUG((1, "Memory Usage (STRING objects): %ld (%d)", size, count)); total += size; for (size = 0, count = 0, usr = userList; usr != (NAMES *)0; usr = usr->next, count++) { size += strlen(usr->name) + sizeof(NAMES); } CONDDEBUG((1, "Memory Usage (userList objects): %ld (%d)", size, count)); total += size; CONDDEBUG((1, "Memory Usage (total): %ld", total)); } void DumpDataStructures(void) { GRPENT *pGE; CONSENT *pCE; REMOTE *pRC; int i; TASKS *t; #if HAVE_FREEIPMI static STRING *tmpString = (STRING *)0; if (tmpString == (STRING *)0) tmpString = AllocString(); #endif #if HAVE_DMALLOC && DMALLOC_MARK_MAIN CONDDEBUG((1, "DumpDataStructures(): dmalloc / MarkMain")); dmalloc_log_changed(dmallocMarkMain, 1, 0, 1); #endif #define EMPTYSTR(x) x == (char *)0 ? "(null)" : x #define FLAGSTR(x) x == FLAGTRUE ? "true" : (x == FLAGFALSE ? "false" : "unset") if (!fDebug) return; SummarizeDataStructures(); for (pGE = pGroups; pGE != (GRPENT *)0; pGE = pGE->pGEnext) { CONDDEBUG((1, "DumpDataStructures(): group: id=%u port=%hu, pid=%lu, imembers=%d", pGE->id, pGE->port, (unsigned long)pGE->pid, pGE->imembers)); for (pCE = pGE->pCElist; pCE != (CONSENT *)0; pCE = pCE->pCEnext) { switch (pCE->type) { case DEVICE: CONDDEBUG((1, "DumpDataStructures(): server=%s, type=DEVICE", EMPTYSTR(pCE->server))); CONDDEBUG((1, "DumpDataStructures(): baud=%s, parity=%s, device=%s", pCE->baud->acrate, pCE->parity->key, EMPTYSTR(pCE->device))); break; case EXEC: CONDDEBUG((1, "DumpDataStructures(): server=%s, type=EXEC", EMPTYSTR(pCE->server))); CONDDEBUG((1, "DumpDataStructures(): execSlave=%s, exec=%s, ipid=%lu", EMPTYSTR(pCE->execSlave), EMPTYSTR(pCE->exec), (unsigned long)pCE->ipid)); CONDDEBUG((1, "DumpDataStructures(): execuid=%d, execgid=%d", pCE->execuid, pCE->execgid)); break; #if HAVE_FREEIPMI case IPMI: CONDDEBUG((1, "DumpDataStructures(): server=%s, type=IPMI", EMPTYSTR(pCE->server))); CONDDEBUG((1, "DumpDataStructures(): host=%s, username=%s, password=%s, ipmiprivlevel=%d", EMPTYSTR(pCE->host), EMPTYSTR(pCE->username), EMPTYSTR(pCE->password), pCE->ipmiprivlevel)); CONDDEBUG((1, "DumpDataStructures(): ipmiwrkset=%d, ipmiworkaround=%u, ipmiciphersuite=%d", pCE->ipmiwrkset, pCE->ipmiworkaround, pCE->ipmiciphersuite)); FmtCtlStr(pCE->ipmikg->string, pCE->ipmikg->used - 1, tmpString); CONDDEBUG((1, "DumpDataStructures(): ipmikg=%s", EMPTYSTR(tmpString->string))); break; #endif case HOST: CONDDEBUG((1, "DumpDataStructures(): server=%s, type=HOST", EMPTYSTR(pCE->server))); CONDDEBUG((1, "DumpDataStructures(): host=%s, raw=%s, netport=%hu, port=%hu, telnetState=%d", EMPTYSTR(pCE->host), FLAGSTR(pCE->raw), pCE->netport, pCE->port, pCE->telnetState)); break; case NOOP: CONDDEBUG((1, "DumpDataStructures(): server=%s, type=NOOP", EMPTYSTR(pCE->server))); break; case UDS: CONDDEBUG((1, "DumpDataStructures(): server=%s, type=UDS", EMPTYSTR(pCE->server))); CONDDEBUG((1, "DumpDataStructures(): uds=%s", EMPTYSTR(pCE->uds))); break; case UNKNOWNTYPE: CONDDEBUG((1, "DumpDataStructures(): server=%s, type=UNKNOWNTYPE", EMPTYSTR(pCE->server))); break; } if (pCE->aliases != (NAMES *)0) { NAMES *n; for (n = pCE->aliases; n != (NAMES *)0; n = n->next) { CONDDEBUG((1, "DumpDataStructures(): alias=%s", n->name)); } } CONDDEBUG((1, "DumpDataStructures(): fup=%d, fronly=%d, logfile=%s, breakNum=%d", pCE->fup, pCE->fronly, EMPTYSTR(pCE->logfile), pCE->breakNum)); CONDDEBUG((1, "DumpDataStructures(): mark=%d, nextMark=%ld, autoReup=%hu, downHard=%s", pCE->mark, pCE->nextMark, pCE->autoReUp, FLAGSTR(pCE->downHard))); CONDDEBUG((1, "DumpDataStructures(): nolog=%d, cofile=%d, activitylog=%s, breaklog=%s", pCE->nolog, FileFDNum(pCE->cofile), FLAGSTR(pCE->activitylog), FLAGSTR(pCE->breaklog))); CONDDEBUG((1, "DumpDataStructures(): tasklog=%s, ixon=%s, ixany=%s, ixoff=%s", FLAGSTR(pCE->tasklog), FLAGSTR(pCE->ixon), FLAGSTR(pCE->ixany), FLAGSTR(pCE->ixoff))); CONDDEBUG((1, "DumpDataStructures(): autoreinit=%s, hupcl=%s, cstopb=%s, ondemand=%s", FLAGSTR(pCE->autoreinit), FLAGSTR(pCE->hupcl), FLAGSTR(pCE->cstopb), FLAGSTR(pCE->ondemand))); #if defined(CRTSCTS) CONDDEBUG((1, "DumpDataStructures(): crtscts=%s", FLAGSTR(pCE->crtscts))); #endif CONDDEBUG((1, "DumpDataStructures(): reinitoncc=%s, striphigh=%s", FLAGSTR(pCE->reinitoncc), FLAGSTR(pCE->striphigh))); CONDDEBUG((1, "DumpDataStructures(): unloved=%s, login=%s", FLAGSTR(pCE->unloved), FLAGSTR(pCE->login))); CONDDEBUG((1, "DumpDataStructures(): initpid=%lu, initcmd=%s, initfile=%d", (unsigned long)pCE->initpid, EMPTYSTR(pCE->initcmd), FileFDNum(pCE->initfile))); CONDDEBUG((1, "DumpDataStructures(): inituid=%d, initgid=%d", pCE->inituid, pCE->initgid)); CONDDEBUG((1, "DumpDataStructures(): motd=%s, idletimeout=%d, idlestring=%s, replstring=%s", EMPTYSTR(pCE->motd), pCE->idletimeout, EMPTYSTR(pCE->idlestring), EMPTYSTR(pCE->replstring))); CONDDEBUG((1, "DumpDataStructures(): tasklist=%s, breaklist=%s, taskpid=%lu, taskfile=%d", EMPTYSTR(pCE->tasklist), EMPTYSTR(pCE->breaklist), (unsigned long)pCE->taskpid, FileFDNum(pCE->taskfile))); if (pCE->ro) { CONSENTUSERS *u; for (u = pCE->ro; u != (CONSENTUSERS *)0; u = u->next) { CONDDEBUG((1, "DumpDataStructures(): ro=%s%s", (u->not ? "!" : ""), u->user->name)); } } if (pCE->rw) { CONSENTUSERS *u; for (u = pCE->rw; u != (CONSENTUSERS *)0; u = u->next) { CONDDEBUG((1, "DumpDataStructures(): rw=%s%s", (u->not ? "!" : ""), u->user->name)); } } CONDDEBUG((1, "DumpDataStructures(): ------")); } } for (pRC = pRCList; (REMOTE *)0 != pRC; pRC = pRC->pRCnext) { CONDDEBUG((1, "DumpDataStructures(): remote: rserver=%s, rhost=%s", EMPTYSTR(pRC->rserver), EMPTYSTR(pRC->rhost))); if (pRC->aliases != (NAMES *)0) { NAMES *n; for (n = pRC->aliases; n != (NAMES *)0; n = n->next) { CONDDEBUG((1, "DumpDataStructures(): alias=%s", n->name)); } } } for (i = 0; i < BREAKLISTSIZE; i++) { CONDDEBUG((1, "DumpDataStructures(): break: #%c, string=%s, delay=%d, confirm=%s", '1' + i + (i > 8 ? BREAKALPHAOFFSET : 0), EMPTYSTR(breakList[i].seq->string), breakList[i].delay, FLAGSTR(breakList[i].confirm))); } for (t = taskList; t != (TASKS *)0; t = t->next) { CONDDEBUG((1, "DumpDataStructures(): task: id=%c, cmd=%s, descr=%s, uid=%d, gid=%d, subst=%s, confirm=%s", t->id, EMPTYSTR(t->cmd->string), EMPTYSTR(t->descr->string), t->uid, t->gid, EMPTYSTR(t->subst), FLAGSTR(t->confirm))); } } /* This makes sure a directory exists and tries to create it if it * doesn't. returns 0 for success, -1 for error */ #if USE_UNIX_DOMAIN_SOCKETS int VerifyEmptyDirectory(char *d) { struct stat dstat; DIR *dir; struct dirent *de; # if 0 /* See below */ STRING *path = (STRING *)0; # endif int retval = 0; while (1) { if (stat(d, &dstat) == -1) { if (errno == ENOENT) { if (mkdir(d, 0755) == -1) { Error("mkdir(%s): %s", d, strerror(errno)); return -1; } CONDDEBUG((1, "VerifyEmptyDirectory: created `%s'", d)); continue; } else { Error("stat(%s): %s", d, strerror(errno)); return -1; } } if (S_ISDIR(dstat.st_mode)) break; return -1; } /* now make sure it's empty...erase anything you see, etc */ if ((dir = opendir(d)) == (DIR *) 0) { Error("opendir(%s): %s", d, strerror(errno)); return -1; } while ((de = readdir(dir)) != (struct dirent *)0) { if ((strcmp(de->d_name, ".") == 0) || (strcmp(de->d_name, "..") == 0)) continue; /* we're going to just let the user deal with non-empty directories */ Error("non-empty directory `%s'", d); retval = -1; break; /* this is probably too extreme. if someone happens to point conserver * at /etc, for example, it could (if running as root) nuke the password * database, config files, etc. too many important files could be * shredded with a small typo. */ # if 0 if (path == (STRING *)0) path = AllocString(); BuildStringPrint(path, "%s/%s", d, de->d_name); if (stat(path->string, &dstat) == -1) { Error("stat(%s): %s", path->string, strerror(errno)); retval = -1; break; } if (S_ISDIR(dstat.st_mode)) { if (rmdir(path->string) != 0) { Error("rmdir(%s): %s", path->string, strerror(errno)); retval = -1; break; } } else { if (unlink(path->string) != 0) { Error("unlink(%s): %s", path->string, strerror(errno)); retval = -1; break; } } # endif } # if 0 /* See above */ if (path != (STRING *)0) DestroyString(path); # endif /* free dir data structure */ closedir(dir); return retval; } #endif /* find out where/who we are (ksb) * parse optons * read in the config file, open the log file * spawn the kids to drive the console groups * become the master server * shutdown with grace * exit happy */ int main(int argc, char **argv) { int i; FILE *fpConfig = (FILE *)0; static char acOpts[] = "7a:b:c:C:dDEFhiL:m:M:noO:p:P:RSuU:Vv"; extern int optopt; extern char *optarg; struct passwd *pwd; char *origuser = (char *)0; char *curuser = (char *)0; int curuid = 0; GRPENT *pGE = (GRPENT *)0; #if !USE_UNIX_DOMAIN_SOCKETS # if USE_IPV6 int s; struct addrinfo hints; # else # if HAVE_INET_ATON struct in_addr inetaddr; # endif # endif #endif isMultiProc = 1; /* make sure stuff has the pid */ thepid = getpid(); if ((char *)0 == (progname = strrchr(argv[0], '/'))) { progname = argv[0]; } else { ++progname; } setpwent(); /* if we read from stdin (by accident) we don't wanna block. * we just don't want any more input at this point. */ close(0); if (0 != open("/dev/null", O_RDWR, 0644)) { Error("open(/dev/null): %s", strerror(errno)); Bye(EX_OSFILE); } #if HAVE_SETLINEBUF setlinebuf(stdout); setlinebuf(stderr); #endif #if HAVE_SETVBUF setvbuf(stdout, NULL, _IOLBF, BUFSIZ); setvbuf(stderr, NULL, _IOLBF, BUFSIZ); #endif /* Initialize the break list */ InitBreakList(); /* prep the config options */ if ((optConf = (CONFIG *)calloc(1, sizeof(CONFIG))) == (CONFIG *)0) OutOfMem(); if ((config = (CONFIG *)calloc(1, sizeof(CONFIG))) == (CONFIG *)0) OutOfMem(); while (EOF != (i = getopt(argc, argv, acOpts))) { switch (i) { case '7': fStrip = 1; break; case 'a': optConf->defaultaccess = *optarg; if (isupper((int)(optConf->defaultaccess))) optConf->defaultaccess = tolower(optConf->defaultaccess); switch (optConf->defaultaccess) { case 'r': case 'a': case 't': break; default: Error("unknown access type `%s'", optarg); Bye(EX_USAGE); } break; case 'b': if ((optConf->secondaryport = StrDup(optarg)) == (char *)0) OutOfMem(); break; case 'c': #if HAVE_OPENSSL if ((optConf->sslcredentials = StrDup(optarg)) == (char *)0) OutOfMem(); #endif break; case 'C': pcConfig = optarg; break; case 'd': optConf->daemonmode = FLAGTRUE; break; case 'D': fDebug++; break; case 'E': #if HAVE_OPENSSL optConf->sslrequired = FLAGFALSE; #endif break; case 'F': fNoautoreup = 1; break; case 'h': Usage(1); Bye(EX_OK); case 'i': fNoinit = 1; break; case 'L': if ((optConf->logfile = StrDup(optarg)) == (char *)0) OutOfMem(); break; case 'm': cMaxMemb = atoi(optarg); if (cMaxMemb <= 0) { Error("ignoring invalid -m option (%d <= 0)", cMaxMemb); cMaxMemb = MAXMEMB; } break; case 'M': interface = StrDup(optarg); break; case 'n': /* noop now */ break; case 'o': /* try reopening downed consoles on connect */ fReopen = 1; break; case 'O': /* How often to try opening all down consoles, in minutes */ optConf->reinitcheck = atoi(optarg); break; case 'p': if ((optConf->primaryport = StrDup(optarg)) == (char *)0) OutOfMem(); break; case 'P': if ((optConf->passwdfile = StrDup(optarg)) == (char *)0) OutOfMem(); break; case 'R': optConf->redirect = FLAGFALSE; break; case 'S': fSyntaxOnly++; break; case 'u': fAll = 1; break; case 'U': if ((optConf->unifiedlog = StrDup(optarg)) == (char *)0) OutOfMem(); break; case 'V': fVersion = 1; break; case 'v': fVerbose = 1; break; case '\?': Usage(0); Bye(EX_USAGE); default: Error("option %c needs a parameter", optopt); Bye(EX_USAGE); } } if (fVersion) { Version(); Bye(EX_OK); } Msg(MyVersion()); #if HAVE_GETLOGIN origuser = getlogin(); #endif curuid = getuid(); if ((struct passwd *)0 != (pwd = getpwuid(curuid))) curuser = pwd->pw_name; /* chuck any empty username */ if (curuser != (char *)0 && curuser[0] == '\000') curuser = (char *)0; if (startedMsg == (STRING *)0) startedMsg = AllocString(); if (curuser == (char *)0) if (origuser == (char *)0) BuildStringPrint(startedMsg, "started as uid %d by uid %d", curuid, curuid); else BuildStringPrint(startedMsg, "started as uid %d by `%s'", curuid, origuser); else BuildStringPrint(startedMsg, "started as `%s' by `%s'", curuser, (origuser == (char *)0) ? curuser : origuser); endpwent(); Msg("%s", startedMsg->string); #if HAVE_GETSPNAM && !HAVE_PAM if (!fSyntaxOnly && (geteuid() != 0)) { Msg("warning: running as a non-root user - any shadow password usage will most likely fail!"); } #endif if (fSyntaxOnly) Msg("performing configuration file syntax check"); /* must do all this so IsMe() works right */ if (gethostname(myHostname, MAXHOSTNAME) != 0) { Error("gethostname(): %s", strerror(errno)); Bye(EX_OSERR); } #if !USE_IPV6 ProbeInterfaces(bindAddr); #endif /* initialize the timers */ for (i = 0; i < T_MAX; i++) timers[i] = (time_t)0; /* read the config file */ if ((FILE *)0 == (fpConfig = fopen(pcConfig, "r"))) { Error("fopen(%s): %s", pcConfig, strerror(errno)); Bye(EX_NOINPUT); } ReadCfg(pcConfig, fpConfig); fclose(fpConfig); #if !USE_UNIX_DOMAIN_SOCKETS /* set up the port to bind to */ if (optConf->primaryport != (char *)0) config->primaryport = StrDup(optConf->primaryport); else if (pConfig->primaryport != (char *)0) config->primaryport = StrDup(pConfig->primaryport); else config->primaryport = StrDup(defConfig.primaryport); if (config->primaryport == (char *)0) OutOfMem(); # if !USE_IPV6 /* Look for non-numeric characters */ for (i = 0; config->primaryport[i] != '\000'; i++) if (!isdigit((int)config->primaryport[i])) break; if (config->primaryport[i] == '\000') { /* numeric only */ bindPort = atoi(config->primaryport); } else { /* non-numeric only */ struct servent *pSE; if ((struct servent *)0 == (pSE = getservbyname(config->primaryport, "tcp"))) { Error("getservbyname(%s) failed", config->primaryport); Bye(EX_OSERR); } else { bindPort = ntohs((unsigned short)pSE->s_port); } } # endif /* set up the secondary port to bind to */ if (optConf->secondaryport != (char *)0) config->secondaryport = StrDup(optConf->secondaryport); else if (pConfig->secondaryport != (char *)0) config->secondaryport = StrDup(pConfig->secondaryport); else config->secondaryport = StrDup(defConfig.secondaryport); if (config->secondaryport == (char *)0) OutOfMem(); # if !USE_IPV6 /* Look for non-numeric characters */ for (i = 0; config->secondaryport[i] != '\000'; i++) if (!isdigit((int)config->secondaryport[i])) break; if (config->secondaryport[i] == '\000') { /* numeric only */ bindBasePort = atoi(config->secondaryport); } else { /* non-numeric only */ struct servent *pSE; if ((struct servent *)0 == (pSE = getservbyname(config->secondaryport, "tcp"))) { Error("getservbyname(%s) failed", config->secondaryport); Bye(EX_OSERR); } else { bindBasePort = ntohs((unsigned short)pSE->s_port); } } # endif #endif #if USE_IPV6 /* set up the address to bind to */ memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags |= AI_PASSIVE; /* create list or IPs suitable for primaryport */ s = getaddrinfo(interface, config->primaryport, &hints, &bindAddr); if (s) { Error("getaddrinfo(%s): %s", interface, gai_strerror(s)); Bye(EX_OSERR); } /* create list or IPs suitable for secondaryport */ s = getaddrinfo(interface, config->secondaryport, &hints, &bindBaseAddr); if (s) { Error("getaddrinfo(%s): %s", interface, gai_strerror(s)); Bye(EX_OSERR); } #elif USE_UNIX_DOMAIN_SOCKETS /* Don't do any redirects if we're purely local * (but it allows them to see where remote consoles are) */ optConf->redirect = FLAGFALSE; if (interface == (char *)0) interface = UDSDIR; #else /* set up the address to bind to */ if (interface == (char *)0 || (interface[0] == '*' && interface[1] == '\000')) bindAddr = INADDR_ANY; else { # if HAVE_INET_ATON if (inet_aton(interface, &inetaddr) == 0) { Error("inet_aton(%s): %s", interface, "invalid IP address"); Bye(EX_OSERR); } bindAddr = inetaddr.s_addr; # else bindAddr = inet_addr(interface); if (bindAddr == (in_addr_t) (-1)) { Error("inet_addr(%s): %s", interface, "invalid IP address"); Bye(EX_OSERR); } # endif } if (fDebug) { struct in_addr ba; ba.s_addr = bindAddr; CONDDEBUG((1, "main(): bind address set to `%s'", inet_ntoa(ba))); } #endif if (optConf->passwdfile != (char *)0) config->passwdfile = StrDup(optConf->passwdfile); else if (pConfig->passwdfile != (char *)0) config->passwdfile = StrDup(pConfig->passwdfile); else config->passwdfile = StrDup(defConfig.passwdfile); if (config->passwdfile == (char *)0) OutOfMem(); if (optConf->logfile != (char *)0) config->logfile = StrDup(optConf->logfile); else if (pConfig->logfile != (char *)0) config->logfile = StrDup(pConfig->logfile); else config->logfile = StrDup(defConfig.logfile); if (config->logfile == (char *)0) OutOfMem(); if (optConf->reinitcheck != 0) config->reinitcheck = optConf->reinitcheck; else if (pConfig->reinitcheck != 0) config->reinitcheck = pConfig->reinitcheck; else config->reinitcheck = defConfig.reinitcheck; if (optConf->defaultaccess != '\000') config->defaultaccess = optConf->defaultaccess; else if (pConfig->defaultaccess != '\000') config->defaultaccess = pConfig->defaultaccess; else config->defaultaccess = defConfig.defaultaccess; if (optConf->daemonmode != FLAGUNKNOWN) config->daemonmode = optConf->daemonmode; else if (pConfig->daemonmode != FLAGUNKNOWN) config->daemonmode = pConfig->daemonmode; else config->daemonmode = defConfig.daemonmode; if (optConf->redirect != FLAGUNKNOWN) config->redirect = optConf->redirect; else if (pConfig->redirect != FLAGUNKNOWN) config->redirect = pConfig->redirect; else config->redirect = defConfig.redirect; if (optConf->autocomplete != FLAGUNKNOWN) config->autocomplete = optConf->autocomplete; else if (pConfig->autocomplete != FLAGUNKNOWN) config->autocomplete = pConfig->autocomplete; else config->autocomplete = defConfig.autocomplete; if (optConf->loghostnames != FLAGUNKNOWN) config->loghostnames = optConf->loghostnames; else if (pConfig->loghostnames != FLAGUNKNOWN) config->loghostnames = pConfig->loghostnames; else config->loghostnames = defConfig.loghostnames; if (optConf->unifiedlog != (char *)0) { config->unifiedlog = StrDup(optConf->unifiedlog); if (config->unifiedlog == (char *)0) OutOfMem(); } else if (pConfig->unifiedlog != (char *)0) { config->unifiedlog = StrDup(pConfig->unifiedlog); if (config->unifiedlog == (char *)0) OutOfMem(); } else if (defConfig.unifiedlog != (char *)0) { config->unifiedlog = StrDup(defConfig.unifiedlog); if (config->unifiedlog == (char *)0) OutOfMem(); } if (optConf->initdelay != 0) config->initdelay = optConf->initdelay; else if (pConfig->initdelay != 0) config->initdelay = pConfig->initdelay; else config->initdelay = defConfig.initdelay; #if HAVE_OPENSSL if (optConf->sslrequired != FLAGUNKNOWN) config->sslrequired = optConf->sslrequired; else if (pConfig->sslrequired != FLAGUNKNOWN) config->sslrequired = pConfig->sslrequired; else config->sslrequired = defConfig.sslrequired; if (optConf->sslreqclientcert != FLAGUNKNOWN) config->sslreqclientcert = optConf->sslreqclientcert; else if (pConfig->sslreqclientcert != FLAGUNKNOWN) config->sslreqclientcert = pConfig->sslreqclientcert; else config->sslreqclientcert = defConfig.sslreqclientcert; if (optConf->sslcredentials != (char *)0) config->sslcredentials = StrDup(optConf->sslcredentials); else if (pConfig->sslcredentials != (char *)0) config->sslcredentials = StrDup(pConfig->sslcredentials); else config->sslcredentials = StrDup(defConfig.sslcredentials); if (optConf->sslcacertificatefile != (char *)0) config->sslcacertificatefile = StrDup(optConf->sslcacertificatefile); else if (pConfig->sslcacertificatefile != (char *)0) config->sslcacertificatefile = StrDup(pConfig->sslcacertificatefile); else config->sslcacertificatefile = StrDup(defConfig.sslcacertificatefile); #endif #if HAVE_SETPROCTITLE if (optConf->setproctitle != FLAGUNKNOWN) config->setproctitle = optConf->setproctitle; else if (pConfig->setproctitle != FLAGUNKNOWN) config->setproctitle = pConfig->setproctitle; else config->setproctitle = defConfig.setproctitle; #endif #if HAVE_DMALLOC && DMALLOC_MARK_MAIN dmallocMarkMain = dmalloc_mark(); #endif if (pGroups == (GRPENT *)0 && pRCList == (REMOTE *)0) { Error("no consoles found in configuration file"); } else if (fSyntaxOnly) { /* short-circuit */ #if USE_UNIX_DOMAIN_SOCKETS } else if (VerifyEmptyDirectory(interface) == -1) { Error("Master(): %s: unusable socket directory", interface); #endif } else { #if HAVE_OPENSSL /* Prep the SSL layer */ SetupSSL(); #endif #if HAVE_GSSAPI SetupGSSAPI(); #endif if (config->daemonmode == FLAGTRUE) Daemonize(); ReopenUnifiedlog(); /* if no one can use us we need to come up with a default */ if (pACList == (ACCESS *)0) #if USE_IPV6 SetDefAccess(); #else SetDefAccess(myAddrs, myHostname); #endif /* spawn all the children, so fix kids has an initial pid */ for (pGE = pGroups; pGE != (GRPENT *)0; pGE = pGE->pGEnext) { if (pGE->imembers == 0) continue; Spawn(pGE, -1); Verbose("group #%d pid %lu on port %hu", pGE->id, (unsigned long)pGE->pid, pGE->port); } #if HAVE_SETPROCTITLE if (config->setproctitle == FLAGTRUE) { REMOTE *pRC; GRPENT *pGE; int local = 0, remote = 0; for (pGE = pGroups; pGE != (GRPENT *)0; pGE = pGE->pGEnext) local += pGE->imembers; for (pRC = pRCList; (REMOTE *)0 != pRC; pRC = pRC->pRCnext) remote++; setproctitle("master: port %hu, %d local, %d remote", # if USE_IPV6 config->primaryport, # elif USE_UNIX_DOMAIN_SOCKETS 0, # else bindPort, # endif local, remote); } #endif if (fVerbose) { ACCESS *pACtmp; for (pACtmp = pACList; pACtmp != (ACCESS *)0; pACtmp = pACtmp->pACnext) { Verbose("access type `%c' for `%s'", pACtmp->ctrust, pACtmp->pcwho); } } pRCUniq = FindUniq(pRCList); /* output unique console server peers? */ if (fVerbose) { REMOTE *pRC; for (pRC = pRCUniq; (REMOTE *)0 != pRC; pRC = pRC->pRCuniq) { Verbose("peer server on `%s'", pRC->rhost); } } fflush(stdout); fflush(stderr); Master(); /* stop putting kids back, and shoot them */ SimpleSignal(SIGCHLD, SIG_DFL); SignalKids(SIGTERM); } if (unifiedlog != (CONSFILE *)0) FileClose(&unifiedlog); DumpDataStructures(); Msg("terminated"); endpwent(); if (fSyntaxOnly && fErrorPrinted) Bye(EX_DATAERR); else Bye(EX_OK); return EX_OK; /* never gets here clears the compiler warning */ }