MyString convert_ipaddr_to_hostname(const condor_sockaddr& addr) { MyString ret; MyString default_domain; if (!param(default_domain, "DEFAULT_DOMAIN_NAME")) { dprintf(D_HOSTNAME, "NO_DNS: DEFAULT_DOMAIN_NAME must be defined in your " "top-level config file\n"); return ret; } ret = addr.to_ip_string(); for (int i = 0; i < ret.Length(); ++i) { if (ret[i] == '.' || ret[i] == ':') ret.setChar(i, '-'); } ret += "."; ret += default_domain; // Hostnames can't begin with -, as per RFC 1123 // ipv6 zero-compression could cause this, esp. for the loopback addr if (ret[0] == '-') { ret = "0" + ret; } return ret; }
std::vector<MyString> get_hostname_with_alias(const condor_sockaddr& addr) { std::vector<MyString> prelim_ret; std::vector<MyString> actual_ret; MyString hostname = get_hostname(addr); if (hostname.IsEmpty()) return prelim_ret; // we now start to construct a list (prelim_ret) of the hostname and all // the aliases. first the name itself. prelim_ret.push_back(hostname); if (nodns_enabled()) // don't need to verify this... the string is actually an IP here return prelim_ret; // no need to call further DNS functions. // now, add the aliases hostent* ent; //int aftype = addr.get_aftype(); //ent = gethostbyname2(hostname.Value(), addr.get_aftype()); // really should call gethostbyname2() however most platforms do not // support. (Solaris, HP-UX, IRIX) // complete DNS aliases can be only obtained by gethostbyname. // however, what happens if we call it in IPv6-only system? // can we get DNS aliases for the hostname that only contains // IPv6 addresses? ent = gethostbyname(hostname.Value()); if (ent) { char** alias = ent->h_aliases; for (; *alias; ++alias) { prelim_ret.push_back(MyString(*alias)); } } // WARNING! there is a reason this is implimented as two separate loops, // so please don't try to combine them. // // calling verify_name_has_ip() will potentially overwrite static data that // is referred to by ent->h_aliases (man 3 gethostbyname, see notes). so // first, we push the name and then all aliases into the MyString vector // prelim_ret, and then verify them in the following loop. for (unsigned int i = 0; i < prelim_ret.size(); i++) { if(verify_name_has_ip(prelim_ret[i], addr)) { actual_ret.push_back(prelim_ret[i]); } else { dprintf(D_ALWAYS, "WARNING: forward resolution of %s doesn't match %s!\n", prelim_ret[i].Value(), addr.to_ip_string().Value()); } } return actual_ret; }
bool verify_name_has_ip(MyString name, condor_sockaddr addr){ std::vector<condor_sockaddr> addrs; bool found = false; addrs = resolve_hostname(name); dprintf(D_FULLDEBUG, "IPVERIFY: checking %s against %s\n", name.Value(), addr.to_ip_string().Value()); for(unsigned int i = 0; i < addrs.size(); i++) { // compare MyStrings // addr.to_ip_string if(addrs[i].to_ip_string() == addr.to_ip_string()) { dprintf(D_FULLDEBUG, "IPVERIFY: matched %s to %s\n", addrs[i].to_ip_string().Value(), addr.to_ip_string().Value()); found = true; } else { dprintf(D_FULLDEBUG, "IPVERIFY: comparing %s to %s\n", addrs[i].to_ip_string().Value(), addr.to_ip_string().Value()); } } dprintf(D_FULLDEBUG, "IPVERIFY: ip found is %i\n", found); return found; }
static void replace_higher_scoring_addr(const char * reason, condor_sockaddr & current, int & current_score, const condor_sockaddr & potential, int potential_score) { const char * result = "skipped for low score"; if(current_score < potential_score) { current = potential; current_score = potential_score; result = "new winner"; } dprintf(D_HOSTNAME, "%s: %s (score %d) %s\n", reason, potential.to_ip_string().Value(), potential_score, result); }
void reset_local_hostname() { if( ! init_local_hostname_impl() ) { dprintf( D_ALWAYS, "Something went wrong identifying my hostname and IP address.\n" ); hostname_initialized = false; } else { dprintf( D_HOSTNAME, "I am: hostname: %s, fully qualified doman name: %s, IP: %s, IPv4: %s, IPv6: %s\n", local_hostname.Value(), local_fqdn.Value(), local_ipaddr.to_ip_string().Value(), local_ipv4addr.to_ip_string().Value(), local_ipv6addr.to_ip_string().Value() ); hostname_initialized = true; } }
void init_local_hostname() { // [m.] // initializing local hostname, ip address, fqdn was // super complex. // // implementation was scattered over condor_netdb and // my_hostname, get_full_hostname. // // above them has duplicated code in many ways. // so I aggregated all of them into here. bool ipaddr_inited = false; char hostname[MAXHOSTNAMELEN]; int ret; // [TODO:IPV6] condor_gethostname is not IPv6 safe. // reimplement it. ret = condor_gethostname(hostname, sizeof(hostname)); if (ret) { dprintf(D_ALWAYS, "condor_gethostname() failed. Cannot initialize " "local hostname, ip address, FQDN.\n"); return; } dprintf(D_HOSTNAME, "condor_gethostname() claims we are %s\n", hostname); // Fallback case. local_hostname = hostname; // if NETWORK_INTERFACE is defined, we use that as a local ip addr. MyString network_interface; if (param(network_interface, "NETWORK_INTERFACE", "*")) { if (local_ipaddr.from_ip_string(network_interface)) ipaddr_inited = true; } // Dig around for an IP address in the interfaces // TODO WARNING: Will only return IPv4 addresses! if( ! ipaddr_inited ) { std::string ip; if( ! network_interface_to_ip("NETWORK_INTERFACE", network_interface.Value(), ip, NULL)) { dprintf(D_ALWAYS, "Unable to identify IP address from interfaces. None matches NETWORK_INTERFACE=%s. Problems are likely.\n", network_interface.Value()); return; } if ( ! local_ipaddr.from_ip_string(ip)) { // Should not happen; means network_interface_to_ip returned // invalid IP address. ASSERT(FALSE); } ipaddr_inited = true; } // now initialize hostname and fqdn if (nodns_enabled()) { // if nodns is enabled, we can cut some slack. // condor_gethostname() returns a hostname with // DEFAULT_DOMAIN_NAME. Thus, it is always fqdn local_fqdn = hostname; if (!ipaddr_inited) { local_ipaddr = convert_hostname_to_ipaddr(local_hostname); } return; } addrinfo_iterator ai; ret = ipv6_getaddrinfo(hostname, NULL, ai); if (ret) { // write some error message dprintf(D_HOSTNAME, "hostname %s cannot be resolved by getaddrinfo\n", hostname); return; } int local_hostname_desireability = 0; while (addrinfo* info = ai.next()) { const char* name = info->ai_canonname; if (!name) continue; condor_sockaddr addr(info->ai_addr); int desireability = 0; if (addr.is_loopback()) { desireability = 1; } else if(addr.is_private_network()) { desireability = 2; } else { desireability = 3; } dprintf(D_HOSTNAME, "Considering %s (Ranked at %d) as possible local hostname versus %s/%s (%d)\n", name, desireability, local_hostname.Value(), local_fqdn.Value(), local_hostname_desireability); if(desireability < local_hostname_desireability) { continue; } local_hostname_desireability = desireability; if (!ipaddr_inited) local_ipaddr = addr; const char* dotpos = strchr(name, '.'); if (dotpos) { // consider it as a FQDN local_fqdn = name; local_hostname = local_fqdn.Substr(0, dotpos-name-1); } else { local_hostname = name; local_fqdn = local_hostname; MyString default_domain; if (param(default_domain, "DEFAULT_DOMAIN_NAME")) { if (default_domain[0] != '.') local_fqdn += "."; local_fqdn += default_domain; } } } dprintf(D_HOSTNAME, "Identifying myself as: Short:: %s, Long: %s, IP: %s\n", local_hostname.Value(), local_fqdn.Value(), local_ipaddr.to_ip_string().Value()); hostname_initialized = true; }
int IpVerify::Verify( DCpermission perm, const condor_sockaddr& addr, const char * user, MyString *allow_reason, MyString *deny_reason ) { perm_mask_t mask; in6_addr sin6_addr; const char *thehost; const char * who = user; MyString peer_description; // we build this up as we go along (DNS etc.) if( !did_init ) { Init(); } /* * Be Warned: careful about parameter "sin" being NULL. It could be, in * which case we should return FALSE (unless perm is ALLOW) * */ switch ( perm ) { case ALLOW: return USER_AUTH_SUCCESS; break; default: break; } sin6_addr = addr.to_ipv6_address(); mask = 0; // must initialize to zero because we logical-or bits into this if (who == NULL || *who == '\0') { who = TotallyWild; } if ( perm >= LAST_PERM || !PermTypeArray[perm] ) { EXCEPT("IpVerify::Verify: called with unknown permission %d\n",perm); } // see if a authorization hole has been dyamically punched (via // PunchHole) for this perm / user / IP // Note that the permission hierarchy is dealt with in // PunchHole(), by punching a hole for all implied levels. // Therefore, if there is a hole or an implied hole, we will // always find it here before we get into the subsequent code // which recursively calls Verify() to traverse the hierarchy. // This is important, because we do not want holes to find // there way into the authorization cache. // if ( PunchedHoleArray[perm] != NULL ) { HolePunchTable_t* hpt = PunchedHoleArray[perm]; MyString ip_str_buf = addr.to_ip_string(); const char* ip_str = ip_str_buf.Value(); MyString id_with_ip; MyString id; int count; if ( who != TotallyWild ) { id_with_ip.sprintf("%s/%s", who, ip_str); id = who; if ( hpt->lookup(id, count) != -1 ) { if( allow_reason ) { allow_reason->sprintf( "%s authorization has been made automatic for %s", PermString(perm), id.Value() ); } return USER_AUTH_SUCCESS; } if ( hpt->lookup(id_with_ip, count) != -1 ) { if( allow_reason ) { allow_reason->sprintf( "%s authorization has been made automatic for %s", PermString(perm), id_with_ip.Value() ); } return USER_AUTH_SUCCESS; } } id = ip_str; if ( hpt->lookup(id, count) != -1 ) { if( allow_reason ) { allow_reason->sprintf( "%s authorization has been made automatic for %s", PermString(perm), id.Value() ); } return USER_AUTH_SUCCESS; } } if ( PermTypeArray[perm]->behavior == USERVERIFY_ALLOW ) { // allow if no HOSTALLOW_* or HOSTDENY_* restrictions // specified. if( allow_reason ) { allow_reason->sprintf( "%s authorization policy allows access by anyone", PermString(perm)); } return USER_AUTH_SUCCESS; } if ( PermTypeArray[perm]->behavior == USERVERIFY_DENY ) { // deny if( deny_reason ) { deny_reason->sprintf( "%s authorization policy denies all access", PermString(perm)); } return USER_AUTH_FAILURE; } if( LookupCachedVerifyResult(perm,sin6_addr,who,mask) ) { if( deny_reason && (mask&deny_mask(perm)) ) { deny_reason->sprintf( "cached result for %s; see first case for the full reason", PermString(perm)); } else if( allow_reason && (mask&allow_mask(perm)) ) { allow_reason->sprintf( "cached result for %s; see first case for the full reason", PermString(perm)); } } else { mask = 0; // if the deny bit is already set, skip further DENY analysis perm_mask_t const deny_resolved = deny_mask(perm); // if the allow or deny bit is already set, // skip further ALLOW analysis perm_mask_t const allow_resolved = allow_mask(perm)|deny_mask(perm); // check for matching subnets in ip/mask style char ipstr[INET6_ADDRSTRLEN] = { 0, }; addr.to_ip_string(ipstr, INET6_ADDRSTRLEN); peer_description = addr.to_ip_string(); if ( !(mask&deny_resolved) && lookup_user_ip_deny(perm,who,ipstr)) { mask |= deny_mask(perm); if( deny_reason ) { deny_reason->sprintf( "%s authorization policy denies IP address %s", PermString(perm), addr.to_ip_string().Value() ); } } if ( !(mask&allow_resolved) && lookup_user_ip_allow(perm,who,ipstr)) { mask |= allow_mask(perm); if( allow_reason ) { allow_reason->sprintf( "%s authorization policy allows IP address %s", PermString(perm), addr.to_ip_string().Value() ); } } std::vector<MyString> hostnames; // now scan through hostname strings if( !(mask&allow_resolved) || !(mask&deny_resolved) ) { hostnames = get_hostname_with_alias(addr); } for (unsigned int i = 0; i < hostnames.size(); ++i) { thehost = hostnames[i].Value(); peer_description.append_to_list(thehost); if ( !(mask&deny_resolved) && lookup_user_host_deny(perm,who,thehost) ) { mask |= deny_mask(perm); if( deny_reason ) { deny_reason->sprintf( "%s authorization policy denies hostname %s", PermString(perm), thehost ); } } if ( !(mask&allow_resolved) && lookup_user_host_allow(perm,who,thehost) ) { mask |= allow_mask(perm); if( allow_reason ) { allow_reason->sprintf( "%s authorization policy allows hostname %s", PermString(perm), thehost ); } } } // if we found something via our hostname or subnet mactching, we now have // a mask, and we should add it into our table so we need not // do a gethostbyaddr() next time. if we still do not have a mask // (perhaps because this host doesn't appear in any list), create one // and then add to the table. // But first, check our parent permission levels in the // authorization heirarchy. // DAEMON and ADMINISTRATOR imply WRITE. // WRITE, NEGOTIATOR, and CONFIG_PERM imply READ. bool determined_by_parent = false; if ( mask == 0 ) { if ( PermTypeArray[perm]->behavior == USERVERIFY_ONLY_DENIES ) { dprintf(D_SECURITY,"IPVERIFY: %s at %s not matched to deny list, so allowing.\n",who, addr.to_sinful().Value()); if( allow_reason ) { allow_reason->sprintf( "%s authorization policy does not deny, so allowing", PermString(perm)); } mask |= allow_mask(perm); } else { DCpermissionHierarchy hierarchy( perm ); DCpermission const *parent_perms = hierarchy.getPermsIAmDirectlyImpliedBy(); bool parent_allowed = false; for( ; *parent_perms != LAST_PERM; parent_perms++ ) { if( Verify( *parent_perms, addr, user, allow_reason, NULL ) == USER_AUTH_SUCCESS ) { determined_by_parent = true; parent_allowed = true; dprintf(D_SECURITY,"IPVERIFY: allowing %s at %s for %s because %s is allowed\n",who, addr.to_sinful().Value(),PermString(perm),PermString(*parent_perms)); if( allow_reason ) { MyString tmp = *allow_reason; allow_reason->sprintf( "%s is implied by %s; %s", PermString(perm), PermString(*parent_perms), tmp.Value()); } break; } } if( parent_allowed ) { mask |= allow_mask(perm); } else { mask |= deny_mask(perm); if( !determined_by_parent && deny_reason ) { // We don't just allow anyone, and this request // did not match any of the entries we do allow. // In case the reason we didn't match is // because of a typo or a DNS problem, record // all the hostnames we searched for. deny_reason->sprintf( "%s authorization policy contains no matching " "ALLOW entry for this request" "; identifiers used for this host: %s, hostname size = %lu, " "original ip address = %s", PermString(perm), peer_description.Value(), (unsigned long)hostnames.size(), ipstr); } } } } if( !determined_by_parent && (mask&allow_mask(perm)) ) { // In case we are allowing because of not matching a DENY // entry that the user expected us to match (e.g. because // of typo or DNS problem), record all the hostnames we // searched for. if( allow_reason && !peer_description.IsEmpty() ) { allow_reason->sprintf_cat( "; identifiers used for this remote host: %s", peer_description.Value()); } } // finally, add the mask we computed into the table with this IP addr add_hash_entry(sin6_addr, who, mask); } // end of if find_match is FALSE // decode the mask and return True or False to the user. if ( mask & deny_mask(perm) ) { return USER_AUTH_FAILURE; } if ( mask & allow_mask(perm) ) { return USER_AUTH_SUCCESS; } return USER_AUTH_FAILURE; }