diff --git a/confluent_server/confluent/config/attributes.py b/confluent_server/confluent/config/attributes.py index 16258f24..52cc43ee 100644 --- a/confluent_server/confluent/config/attributes.py +++ b/confluent_server/confluent/config/attributes.py @@ -97,6 +97,15 @@ node = { 'description': ('Classification of node as server or switch'), 'validvalues': ('switch', 'server'), }, + 'api.key': { + 'description': ('Crypt of api key for self api requests by node'), + }, + 'api.armed': { + 'description': ('Indicates whether an insecure api key request is allowed. ' + 'The format is an expiration time in ISO8601 format. When ' + 'the indicated time passes or the first time a node claims ' + 'the key, key grants will not be allowed.'), + }, #'id': { # 'description': ('Numeric identifier for node') #}, diff --git a/confluent_server/confluent/credserver.py b/confluent_server/confluent/credserver.py new file mode 100644 index 00000000..9f7d5f14 --- /dev/null +++ b/confluent_server/confluent/credserver.py @@ -0,0 +1,87 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2019 Lenovo +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import confluent.config.configmanager as cfm +import datetime +import eventlet +import eventlet.green.socket as socket +import eventlet.greenpool +import os + +class CredServer(object): + def __init__(self, bindhost='::', bindport=301, ttl=1): + self.srv = socket.socket(socket.AF_INET6) + self.srv.setsockopt(socket.SOL_IP, socket.IP_TTL, ttl) + self.srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + self.srv.bind((bindhost, bindport)) + self.srv.listen(32) + self.gpool = eventlet.greenpool.GreenPool(256) + self.cfm = cfm.ConfigManager(None) + self.runtime = eventlet.spawn(self.listen) + + def listen(self): + while True: + client, info = self.srv.accept() + if info[1] > 1023: + client.close() + continue + self.gpool.spawn_n(self.handle_client, client) + + def handle_client(self, client): + client.send('\xc2\xd1-\xa8\x80\xd8j\xba') + tlv = bytearray(client.recv(2)) + if tlv[0] != 1: + client.close() + return + nodename = client.recv(tlv[1]) + tlv = bytearray(client.recv(2)) + apiarmed = self.cfm.get_node_attributes(nodename, 'api.armed') + apiarmed = apiarmed.get(nodename, {}).get('api.armed', {}).get('value', None) + if not apiarmed: + client.close() + return + now = datetime.datetime.utcnow() + expiry = datetime.datetime.strptime(apiarmed, "%Y-%m-%dT%H:%M:%SZ") + if now > expiry: + self.cfm.set_node_attributes({nodename: {'api.armed': ''}}) + client.close() + return + client.send(b'\x02\x20') + rttoken = os.urandom(32) + client.send(rttoken) + client.send('\x00\x00') + tlv = bytearray(client.recv(2)) + if tlv[0] != 3: + client.close() + return + echotoken = client.recv(tlv[1]) + if echotoken != rttoken: + client.close() + return + tlv = bytearray(client.recv(2)) + if tlv[0] != 4: + client.close() + return + echotoken = client.recv(tlv[1]) + self.cfm.set_node_attributes({nodename: {'api.key': echotoken, 'api.armed': ''}}) + client.recv(2) # drain end of message + client.send('\x05\x00') # report success + client.close() + +if __name__ == '__main__': + a = CredServer() + while True: + eventlet.sleep(86400) diff --git a/confluent_server/confluent/discovery/core.py b/confluent_server/confluent/discovery/core.py index 8e06b2f6..5171fcac 100644 --- a/confluent_server/confluent/discovery/core.py +++ b/confluent_server/confluent/discovery/core.py @@ -65,7 +65,7 @@ import base64 import confluent.config.configmanager as cfm import confluent.collective.manager as collective import confluent.discovery.protocols.pxe as pxe -#import confluent.discovery.protocols.ssdp as ssdp +import confluent.discovery.protocols.ssdp as ssdp import confluent.discovery.protocols.slp as slp import confluent.discovery.handlers.imm as imm import confluent.discovery.handlers.cpstorage as cpstorage @@ -877,6 +877,8 @@ def get_nodename_from_chained_smms(cfg, handler, info): nodename = newnodename return nodename +def get_node_by_uuid(uuid): + return nodes_by_uuid.get(uuid, None) def get_nodename_from_enclosures(cfg, info): nodename = None @@ -1238,8 +1240,7 @@ def start_detection(): if rechecker is None: rechecktime = util.monotonic_time() + 900 rechecker = eventlet.spawn_after(900, _periodic_recheck, cfg) - - # eventlet.spawn_n(ssdp.snoop, safe_detected) + eventlet.spawn_n(ssdp.snoop, None, None, ssdp, get_node_by_uuid) def stop_autosense(): for watcher in list(autosensors): diff --git a/confluent_server/confluent/discovery/protocols/ssdp.py b/confluent_server/confluent/discovery/protocols/ssdp.py index 288f733e..a2c2ad3e 100644 --- a/confluent_server/confluent/discovery/protocols/ssdp.py +++ b/confluent_server/confluent/discovery/protocols/ssdp.py @@ -30,9 +30,11 @@ import confluent.neighutil as neighutil import confluent.util as util +import confluent.log as log import eventlet.green.select as select import eventlet.green.socket as socket import struct +import traceback mcastv4addr = '239.255.255.250' mcastv6addr = 'ff02::c' @@ -51,7 +53,7 @@ def scan(services, target=None): yield rply -def snoop(handler, byehandler=None): +def snoop(handler, byehandler=None, protocol=None, uuidlookup=None): """Watch for SSDP notify messages The handler shall be called on any service coming online. @@ -67,6 +69,7 @@ def snoop(handler, byehandler=None): # Normally, I like using v6/v4 agnostic socket. However, since we are # dabbling in multicast wizardry here, such sockets can cause big problems, # so we will have two distinct sockets + tracelog = log.Logger('trace') known_peers = set([]) net6 = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) net6.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 1) @@ -90,50 +93,73 @@ def snoop(handler, byehandler=None): net6.bind(('', 1900)) peerbymacaddress = {} while True: - newmacs = set([]) - machandlers = {} - r, _, _ = select.select((net4, net6), (), (), 60) - neighutil.update_neigh() - while r: - for s in r: - (rsp, peer) = s.recvfrom(9000) - rsp = rsp.split('\r\n') - method, _, _ = rsp[0].split(' ', 2) - if method == 'NOTIFY': - ip = peer[0].partition('%')[0] - if ip not in neighutil.neightable: - continue - if peer in known_peers: - continue - mac = neighutil.neightable[ip] - known_peers.add(peer) - newmacs.add(mac) - if mac in peerbymacaddress: - peerbymacaddress[mac]['peers'].append(peer) - else: - peerbymacaddress[mac] = { - 'hwaddr': mac, - 'peers': [peer], - } - peerdata = peerbymacaddress[mac] + try: + newmacs = set([]) + machandlers = {} + r, _, _ = select.select((net4, net6), (), (), 60) + neighutil.update_neigh() + while r: + for s in r: + (rsp, peer) = s.recvfrom(9000) + rsp = rsp.split('\r\n') + method, _, _ = rsp[0].split(' ', 2) + if method == 'NOTIFY': + ip = peer[0].partition('%')[0] + if ip not in neighutil.neightable: + continue + if peer in known_peers: + continue + mac = neighutil.neightable[ip] + known_peers.add(peer) + newmacs.add(mac) + if mac in peerbymacaddress: + peerbymacaddress[mac]['peers'].append(peer) + else: + peerbymacaddress[mac] = { + 'hwaddr': mac, + 'peers': [peer], + } + peerdata = peerbymacaddress[mac] + for headline in rsp[1:]: + if not headline: + continue + header, _, value = headline.partition(':') + header = header.strip() + value = value.strip() + if header == 'NT': + peerdata['service'] = value + elif header == 'NTS': + if value == 'ssdp:byebye': + machandlers[mac] = byehandler + elif value == 'ssdp:alive': + machandlers[mac] = None # handler + elif method == 'M-SEARCH': + if not uuidlookup: + continue + #ip = peer[0].partition('%')[0] for headline in rsp[1:]: if not headline: continue - header, _, value = headline.partition(':') - header = header.strip() - value = value.strip() - if header == 'NT': - peerdata['service'] = value - elif header == 'NTS': - if value == 'ssdp:byebye': - machandlers[mac] = byehandler - elif value == 'ssdp:alive': - machandlers[mac] = handler - r, _, _ = select.select((net4, net6), (), (), 0.1) - for mac in newmacs: - thehandler = machandlers.get(mac, None) - if thehandler: - thehandler(peerbymacaddress[mac]) + headline = headline.partition(':') + if len(headline) < 3: + continue + if headline[0] == 'ST' and headline[-1].startswith(' urn:xcat.org:service:confluent:'): + for query in headline[-1].split('/'): + if query.startswith('uuid='): + curruuid = query.split('=', 1)[1].lower() + node = uuidlookup(curruuid) + if not node: + break + reply = 'HTTP/1.1 200 OK\r\nNODENAME: {0}'.format(node) + s.sendto(reply, peer) + r, _, _ = select.select((net4, net6), (), (), 0.2) + for mac in newmacs: + thehandler = machandlers.get(mac, None) + if thehandler: + thehandler(peerbymacaddress[mac]) + except Exception: + tracelog.log(traceback.format_exc(), ltype=log.DataTypes.event, + event=log.Events.stacktrace) def _find_service(service, target): diff --git a/misc/clortho.c b/misc/clortho.c index bfe48d00..73753fd2 100644 --- a/misc/clortho.c +++ b/misc/clortho.c @@ -2,84 +2,125 @@ #include #include #include +#include #include #include #include #include +#include #include #include #include #include #include -#define OUI_ETHERTYPE 0x88b7 #define MAXPACKET 1024 -#define CHDR "\xa4\x8c\xdb\x30\x01" -int get_interface_index(int sock, char *interface) { - struct ifreq req; - memset(&req, 0, sizeof(req)); - strncpy(req.ifr_name, interface, IFNAMSIZ); - if (ioctl(sock, SIOCGIFINDEX, &req) < 0) { - return -1; - } - return req.ifr_ifindex; -} +static const char cryptalpha[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789./"; -unsigned char* genpasswd() { +unsigned char* genpasswd(int len) { unsigned char * passwd; int urandom; - passwd = calloc(33, sizeof(char)); + passwd = calloc(len + 1, sizeof(char)); urandom = open("/dev/urandom", O_RDONLY); - read(urandom, passwd, 32); + read(urandom, passwd, len); close(urandom); - for (urandom = 0; urandom < 32; urandom++) { - passwd[urandom] = 0x30 + (passwd[urandom] >> 2); + for (urandom = 0; urandom < len; urandom++) { + passwd[urandom] = cryptalpha[passwd[urandom] >> 2]; } return passwd; } -int parse_macaddr(char* macaddr) { - unsigned char *curr; - unsigned char idx; - curr = strtok(macaddr, ":-"); - idx = 0; - - while (curr != NULL) { - macaddr[idx++] = strtoul(curr, NULL, 16); - curr = strtok(NULL, ":-"); - } - -} int main(int argc, char* argv[]) { int sock; - int iface; + unsigned char currlen, currtype; unsigned char* passwd; + unsigned char* cryptedpass; unsigned char* macaddr; - + struct timeval timeout; + struct addrinfo hints; + struct addrinfo *addrs; + struct addrinfo *curr; + struct sockaddr_in net4bind; + struct sockaddr_in net6bind; unsigned char buffer[MAXPACKET]; + memset(&hints, 0, sizeof(struct addrinfo)); + memset(&net4bind, 0, sizeof(struct sockaddr_in)); + memset(&net6bind, 0, sizeof(struct sockaddr_in)); + memset(&buffer, 0, MAXPACKET); + memset(&timeout, 0, sizeof(struct timeval)); + timeout.tv_sec = 10; + net4bind.sin_port = htons(302); + net6bind.sin_port = htons(302); + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; - passwd = genpasswd(); + passwd = genpasswd(32); + memset(buffer, 0, MAXPACKET); + strncpy(buffer, "$5$", 3); + cryptedpass = genpasswd(8); + strncpy(buffer + 3, cryptedpass, 8); + free(cryptedpass); + cryptedpass = crypt(passwd, buffer); if (argc < 3) { - fprintf(stderr, "Missing interface name and target MAC\n"); + fprintf(stderr, "Missing node name and manager\n"); exit(1); } - printf("%s\n", argv[2]); - parse_macaddr(argv[2]); - printf("%s\n", argv[2]); - sock = socket(AF_PACKET, SOCK_DGRAM, htons(OUI_ETHERTYPE)); - if (sock < 0) { - fprintf(stderr, "Unable to open socket (run as root?)\n"); + sock = getaddrinfo(argv[2], "301", &hints, &addrs); + if (sock != 0) { + fprintf(stderr, "Error trying to resolve %s\n", argv[2]); exit(1); } - iface = get_interface_index(sock, argv[1]); - if (iface < 0) { - fprintf(stderr, "Unable to find specified interface '%s'\n", argv[1]); + for (curr = addrs; curr != NULL; curr = curr->ai_next) { + sock = socket(curr->ai_family, curr->ai_socktype, curr->ai_protocol); + if (sock < 0) continue; + setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &(int){1}, sizeof(int)); + if (curr->ai_family == AF_INET) { + bind(sock, (struct sockaddr*)&net4bind, sizeof(struct sockaddr_in)); + } else if (curr->ai_family == AF_INET6) { + bind(sock, (struct sockaddr*)&net6bind, sizeof(struct sockaddr_in6)); + } else { + continue; + } + if (connect(sock, curr->ai_addr, curr->ai_addrlen) == 0) break; + } + if (curr == NULL) { + fprintf(stderr, "Unable to reach %s\n", argv[2]); exit(1); } - - + setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)); + freeaddrinfo(addrs); + read(sock, buffer, 8); + if (memcmp(buffer, "\xc2\xd1-\xa8\x80\xd8j\xba", 8) != 0) { + fprintf(stderr, "Unrecognized server\n"); + exit(1); + } + dprintf(sock, "\x01%c%s", strlen(argv[1]), argv[1]); + write(sock, "\x00\x00", 2); + memset(buffer, 0, MAXPACKET); + read(sock, buffer, 2); + while (buffer[0] != 255) { + currtype = buffer[0]; + currlen = buffer[1]; + memset(buffer, 0, MAXPACKET); + if (currlen) { + read(sock, buffer, currlen); // Max is 255, well under MAX_PACKET + } + if (currtype == 2) { + dprintf(sock, "\x03%c", currlen); + write(sock, buffer, currlen); + dprintf(sock, "\x04%c%s", strlen(cryptedpass), cryptedpass); + write(sock, "\x00\x00", 2); + } else if (currtype == 5) { + printf(passwd); + printf("\n"); + exit(0); + } + buffer[0] = 255; + read(sock, buffer, 2); + } + fprintf(stderr, "Password was not accepted\n"); + exit(1); } - diff --git a/misc/copernicus.c b/misc/copernicus.c index a9943f11..b727de2a 100644 --- a/misc/copernicus.c +++ b/misc/copernicus.c @@ -68,7 +68,11 @@ int main(int argc, char* argv[]) { struct sockaddr_in6 addr, dst; struct sockaddr_in addr4, dst4; char msg[1024]; + char *nodenameidx; + char nodename[1024]; + char lastnodename[1024]; char lastmsg[1024]; + char last6msg[1024]; int ifidx, offset; fd_set rfds; struct timeval tv; @@ -80,6 +84,10 @@ int main(int argc, char* argv[]) { memset(&addr, 0, sizeof(addr)); memset(&dst, 0, sizeof(dst)); memset(&dst4, 0, sizeof(dst4)); + memset(nodename, 0, 1024); + memset(lastnodename, 0, 1024); + memset(lastmsg, 0, 1024); + memset(last6msg, 0, 1024); addr.sin6_family = AF_INET6; addr.sin6_addr = in6addr_any; addr.sin6_port = htons(190); @@ -141,20 +149,52 @@ int main(int argc, char* argv[]) { if (ifidx == -1) perror("Unable to select"); if (ifidx) { if (FD_ISSET(n4, &rfds)) { - recvfrom(n4, msg, 1024, 0, (struct sockaddr *)&dst4, &dst4size); + memset(msg, 0, 1024); + /* Deny packet access to the last 24 bytes to assure null */ + recvfrom(n4, msg, 1000, 0, (struct sockaddr *)&dst4, &dst4size); + if (nodenameidx = strstr(msg, "NODENAME: ")) { + nodenameidx += 10; + strncpy(nodename, nodenameidx, 1024); + nodenameidx = strstr(nodenameidx, "\r"); + if (nodenameidx) { nodenameidx[0] = 0; } + if (strncmp(lastnodename, nodename, 1024) != 0) { + printf("NODENAME: %s\n", nodename); + strncpy(lastnodename, nodename, 1024); + } + } + memset(msg, 0, 1024); inet_ntop(dst4.sin_family, &dst4.sin_addr, msg, dst4size); /* Take measure from printing out the same ip twice in a row */ if (strncmp(lastmsg, msg, 1024) != 0) { - printf("%s\n", msg); + sendto(n4, "PING", 4, 0, (const struct sockaddr *)&dst4, dst4size); + printf("MANAGER: %s\n", msg); strncpy(lastmsg, msg, 1024); } } if (FD_ISSET(ns, &rfds)) { - recvfrom(ns, msg, 1024, 0, (struct sockaddr *)&dst, &dstsize); + memset(msg, 0, 1024); + /* Deny packet access to the last 24 bytes to assure null */ + recvfrom(ns, msg, 1000, 0, (struct sockaddr *)&dst, &dstsize); + if (nodenameidx = strstr(msg, "NODENAME: ")) { + nodenameidx += 10; + strncpy(nodename, nodenameidx, 1024); + nodenameidx = strstr(nodenameidx, "\r"); + if (nodenameidx) { nodenameidx[0] = 0; } + if (strncmp(lastnodename, nodename, 1024) != 0) { + printf("NODENAME: %s\n", nodename); + strncpy(lastnodename, nodename, 1024); + } + } + memset(msg, 0, 1024); inet_ntop(dst.sin6_family, &dst.sin6_addr, msg, dstsize); - if (strncmp(lastmsg, msg, 1024) != 0) { - printf("%s\n", msg); - strncpy(lastmsg, msg, 1024); + if (strncmp(last6msg, msg, 1024) != 0) { + sendto(ns, "PING", 4, 0, (const struct sockaddr *)&dst, dstsize); + printf("MANAGER: %s", msg); + if (strncmp(msg, "fe80::", 6) == 0) { + printf("%%%u", dst.sin6_scope_id); + } + printf("\n"); + strncpy(last6msg, msg, 1024); } } }