static void setup_me(aClient *mp) { struct passwd *p; p = getpwuid(getuid()); strncpyzt(mp->username, (p) ? p->pw_name : "unknown", sizeof(mp->username)); (void)get_my_name(mp, mp->sockhost, sizeof(mp->sockhost)-1); /* I think we need no hostp, especially fake one --B. */ mp->hostp = NULL; if (mp->serv->namebuf[0] == '\0') strncpyzt(mp->serv->namebuf, mp->sockhost, sizeof(mp->serv->namebuf)); if (me.info == DefInfo) me.info = mystrdup("IRCers United"); mp->lasttime = mp->since = mp->firsttime = time(NULL); mp->hopcount = 0; mp->authfd = -1; mp->auth = mp->username; mp->confs = NULL; mp->flags = 0; mp->acpt = mp->from = mp; mp->next = NULL; mp->user = NULL; mp->fd = -1; SetMe(mp); mp->serv->snum = find_server_num (ME); /* we don't fill our own IP -> 0 as ip lenght */ (void) make_user(mp,0); istat.is_users++; /* here, cptr->next is NULL, see make_user() */ mp->user->flags |= FLAGS_OPER; mp->serv->up = mp; mp->serv->maskedby = mp; mp->serv->version |= SV_UID; mp->user->server = find_server_string(mp->serv->snum); strncpyzt(mp->user->username, (p) ? p->pw_name : "unknown", sizeof(mp->user->username)); (void) strcpy(mp->user->host, mp->name); SetEOB(mp); istat.is_eobservers = 1; (void)add_to_client_hash_table(mp->name, mp); (void)add_to_sid_hash_table(mp->serv->sid, mp); strncpyzt(mp->serv->verstr, PATCHLEVEL, sizeof(mp->serv->verstr)); setup_server_channels(mp); }
int main(int argc, char *argv[]) { uid_t uid, euid; int portarg = 0, fd; #ifdef SAVE_MAXCLIENT_STATS FILE *mcsfp; #endif memset(&me, 0, sizeof(aClient)); if ((timeofday = time(NULL)) == -1) { (void) fprintf(stderr, "ERROR: Clock Failure (%d)\n", errno); exit(errno); } build_version(); Count.server = 1; /* us */ Count.oper = 0; Count.chan = 0; Count.local = 0; Count.total = 0; Count.invisi = 0; Count.unknown = 0; Count.max_loc = 0; Count.max_tot = 0; Count.today = 0; Count.weekly = 0; Count.monthly = 0; Count.yearly = 0; Count.start = NOW; Count.day = NOW; Count.week = NOW; Count.month = NOW; Count.year = NOW; #ifdef SAVE_MAXCLIENT_STATS mcsfp=fopen(DPATH "/.maxclients", "r"); if(mcsfp!=NULL) { fscanf(mcsfp, "%d %d %li %li %li %ld %ld %ld %ld", &Count.max_loc, &Count.max_tot, &Count.weekly, &Count.monthly, &Count.yearly, &Count.start, &Count.week, &Count.month, &Count.year); fclose(mcsfp); } #endif /* * this code by [email protected] * it is intended to keep the ircd from being swapped out. BSD * swapping criteria do not match the requirements of ircd */ #ifdef INITIAL_DBUFS dbuf_init(); /* set up some dbuf stuff to control paging */ #endif sbrk0 = (char *) sbrk((size_t) 0); uid = getuid(); euid = geteuid(); #ifdef PROFIL (void) monstartup(0, etext); (void) moncontrol(1); (void) signal(SIGUSR1, s_monitor); #endif myargv = argv; (void) umask(077); /* better safe than sorry --SRB */ memset((char *) &me, '\0', sizeof(me)); setup_signals(); /* * * All command line parameters have the syntax "-fstring" or "-f * string" (e.g. the space is optional). String may be empty. Flag * characters cannot be concatenated (like "-fxyz"), it would * conflict with the form "-fstring". */ while (--argc > 0 && (*++argv)[0] == '-') { char *p = argv[0] + 1; int flag = *p++; if (flag == '\0' || *p == '\0') { if (argc > 1 && argv[1][0] != '-') { p = *++argv; argc -= 1; } else p = ""; } switch (flag) { case 'a': bootopt |= BOOT_AUTODIE; break; case 'c': bootopt |= BOOT_CONSOLE; break; case 'q': bootopt |= BOOT_QUICK; break; case 'd': (void) setuid((uid_t) uid); dpath = p; break; case 'o': /* Per user local daemon... */ (void) setuid((uid_t) uid); bootopt |= BOOT_OPER; break; #ifdef CMDLINE_CONFIG case 'f': (void) setuid((uid_t) uid); configfile = p; break; # ifdef KPATH case 'k': (void) setuid((uid_t) uid); klinefile = p; break; # endif #endif case 'h': strncpyzt(me.name, p, sizeof(me.name)); break; case 'i': bootopt |= BOOT_INETD | BOOT_AUTODIE; break; case 'p': if ((portarg = atoi(p)) > 0) portnum = portarg; break; case 's': bootopt |= BOOT_STDERR; break; case 't': (void) setuid((uid_t) uid); bootopt |= BOOT_TTY; break; case 'v': (void) printf("ircd %s\n", version); exit(0); case 'x': #ifdef DEBUGMODE (void) setuid((uid_t) uid); debuglevel = atoi(p); debugmode = *p ? p : "0"; bootopt |= BOOT_DEBUG; break; #else (void) fprintf(stderr, "%s: DEBUGMODE must be defined for -x y\n", myargv[0]); exit(0); #endif default: bad_command(); break; } } if (chdir(dpath)) { perror("chdir"); exit(-1); } if ((uid != euid) && !euid) { (void) fprintf(stderr, "ERROR: do not run ircd setuid root. Make it setuid a normal user.\n"); exit(-1); } if (argc > 0) return bad_command(); /* This should exit out */ initialize_ssl(); motd = (aMotd *) NULL; helpfile = (aMotd *) NULL; motd_tm = NULL; #ifdef SHORT_MOTD shortmotd = NULL; #endif read_motd(MOTD); read_help(HELPFILE); #ifdef SHORT_MOTD read_shortmotd(SHORTMOTD); #endif clear_client_hash_table(); clear_channel_hash_table(); clear_scache_hash_table(); /* server cache name table */ clear_ip_hash_table(); /* client host ip hash table */ initlists(); initclass(); initwhowas(); initstats(); init_tree_parse(msgtab); init_send(); NOW = time(NULL); open_debugfile(); NOW = time(NULL); init_fdlist(&serv_fdlist); init_fdlist(&oper_fdlist); init_fdlist(&listen_fdlist); #ifndef NO_PRIORITY init_fdlist(&busycli_fdlist); #endif init_fdlist(&default_fdlist); { int i; for (i = MAXCONNECTIONS + 1; i > 0; i--) { default_fdlist.entry[i] = i - 1; } } if ((timeofday = time(NULL)) == -1) { #ifdef USE_SYSLOG syslog(LOG_WARNING, "Clock Failure (%d), TS can be corrupted", errno); #endif sendto_ops("Clock Failure (%d), TS can be corrupted", errno); } #ifdef WINGATE_NOTICE strcpy(ProxyMonURL, "http://"); strncpyzt((ProxyMonURL + 7), DEFAULT_PROXY_INFO_URL, (TOPICLEN + 1) - 7); strncpyzt(ProxyMonHost, MONITOR_HOST, (HOSTLEN + 1)); #endif if (portnum < 0) portnum = PORTNUM; me.port = portnum; (void) init_sys(); me.flags = FLAGS_LISTEN; #ifndef _WIN32 if (bootopt & BOOT_INETD) { me.fd = 0; local[0] = &me; me.flags = FLAGS_LISTEN; } else #endif me.fd = -1; #ifdef USE_SYSLOG # define SYSLOG_ME "ircd" openlog(SYSLOG_ME, LOG_PID | LOG_NDELAY, LOG_FACILITY); #endif if ((fd = openconf(configfile)) == -1) { Debug((DEBUG_FATAL, "Failed in reading configuration file %s", configfile)); (void) printf("Couldn't open configuration file %s\n", configfile); exit(-1); } (void) initconf(bootopt, fd); /* comstuds SEPARATE_QUOTE_KLINES_BY_DATE code */ #ifdef SEPARATE_QUOTE_KLINES_BY_DATE { struct tm *tmptr; char timebuffer[20], filename[200]; tmptr = localtime(&NOW); (void) strftime(timebuffer, 20, "%y%m%d", tmptr); ircsprintf(filename, "%s.%s", klinefile, timebuffer); if ((fd = openconf(filename)) == -1) { Debug((DEBUG_ERROR, "Failed reading kline file %s", filename)); (void) printf("Couldn't open kline file %s\n", filename); } else (void) initconf(0, fd); } #else # ifdef KPATH if ((fd = openconf(klinefile)) == -1) { Debug((DEBUG_ERROR, "Failed reading kline file %s", klinefile)); (void) printf("Couldn't open kline file %s\n", klinefile); } else (void) initconf(0, fd); # endif #endif if (!(bootopt & BOOT_INETD)) { static char star[] = "*"; aConfItem *aconf; u_long vaddr; if ((aconf = find_me()) && portarg <= 0 && aconf->port > 0) portnum = aconf->port; Debug((DEBUG_ERROR, "Port = %d", portnum)); if ((aconf->passwd[0] != '\0') && (aconf->passwd[0] != '*')) vaddr = inet_addr(aconf->passwd); else vaddr = (u_long) NULL; if (inetport(&me, star, portnum, vaddr)) { if (bootopt & BOOT_STDERR) fprintf(stderr, "Couldn't bind to primary port %d\n", portnum); #ifdef USE_SYSLOG (void) syslog(LOG_CRIT, "Couldn't bind to primary port %d\n", portnum); #endif exit(1); } } else if (inetport(&me, "*", 0, 0)) { if (bootopt & BOOT_STDERR) fprintf(stderr, "Couldn't bind to port passed from inetd\n"); #ifdef USE_SYSLOG (void) syslog(LOG_CRIT, "Couldn't bind to port passed from inetd\n"); #endif exit(1); } (void) get_my_name(&me, me.sockhost, sizeof(me.sockhost) - 1); if (me.name[0] == '\0') strncpyzt(me.name, me.sockhost, sizeof(me.name)); me.hopcount = 0; me.authfd = -1; me.confs = NULL; me.next = NULL; me.user = NULL; me.from = &me; SetMe(&me); make_server(&me); me.serv->up = me.name; me.lasttime = me.since = me.firsttime = NOW; (void) add_to_client_hash_table(me.name, &me); /* We don't want to calculate these every time they are used :) */ sprintf(REPORT_DO_DNS, REPORT_DO_DNS_, me.name); sprintf(REPORT_FIN_DNS, REPORT_FIN_DNS_, me.name); sprintf(REPORT_FIN_DNSC, REPORT_FIN_DNSC_, me.name); sprintf(REPORT_FAIL_DNS, REPORT_FAIL_DNS_, me.name); sprintf(REPORT_DO_ID, REPORT_DO_ID_, me.name); sprintf(REPORT_FIN_ID, REPORT_FIN_ID_, me.name); sprintf(REPORT_FAIL_ID, REPORT_FAIL_ID_, me.name); R_do_dns = strlen(REPORT_DO_DNS); R_fin_dns = strlen(REPORT_FIN_DNS); R_fin_dnsc = strlen(REPORT_FIN_DNSC); R_fail_dns = strlen(REPORT_FAIL_DNS); R_do_id = strlen(REPORT_DO_ID); R_fin_id = strlen(REPORT_FIN_ID); R_fail_id = strlen(REPORT_FAIL_ID); check_class(); if (bootopt & BOOT_OPER) { aClient *tmp = add_connection(&me, 0); if (!tmp) exit(1); SetMaster(tmp); } else write_pidfile(); Debug((DEBUG_NOTICE, "Server ready...")); #ifdef USE_SYSLOG syslog(LOG_NOTICE, "Server Ready"); #endif NOW = time(NULL); #ifndef NO_PRIORITY check_fdlists(); #endif if ((timeofday = time(NULL)) == -1) { #ifdef USE_SYSLOG syslog(LOG_WARNING, "Clock Failure (%d), TS can be corrupted", errno); #endif sendto_ops("Clock Failure (%d), TS can be corrupted", errno); } #ifdef DUMP_DEBUG dumpfp=fopen("dump.log", "w"); #endif io_loop(); return 0; }
char *get_my_hostname() { char *hostname = NULL; char *port = NULL; char *hostport = NULL; char *name = get_my_name(false); char filename[PATH_MAX] = {0}; // Use first Address statement in own host config file if(check_id(name)) { snprintf(filename, sizeof filename, "%s" SLASH "hosts" SLASH "%s", confbase, name); scan_for_hostname(filename, &hostname, &port); scan_for_hostname(tinc_conf, &hostname, &port); } if(hostname) goto done; // If that doesn't work, guess externally visible hostname fprintf(stderr, "Trying to discover externally visible hostname...\n"); struct addrinfo *ai = str2addrinfo("tinc-vpn.org", "80", SOCK_STREAM); struct addrinfo *aip = ai; static const char request[] = "GET http://tinc-vpn.org/host.cgi HTTP/1.0\r\n\r\n"; while(aip) { int s = socket(aip->ai_family, aip->ai_socktype, aip->ai_protocol); if(s >= 0) { if(connect(s, aip->ai_addr, aip->ai_addrlen)) { closesocket(s); s = -1; } } if(s >= 0) { send(s, request, sizeof request - 1, 0); int len = recv(s, line, sizeof line - 1, MSG_WAITALL); if(len > 0) { line[len] = 0; if(line[len - 1] == '\n') line[--len] = 0; char *p = strrchr(line, '\n'); if(p && p[1]) hostname = xstrdup(p + 1); } closesocket(s); if(hostname) break; } aip = aip->ai_next; continue; } if(ai) freeaddrinfo(ai); // Check that the hostname is reasonable if(hostname) { for(char *p = hostname; *p; p++) { if(isalnum(*p) || *p == '-' || *p == '.' || *p == ':') continue; // If not, forget it. free(hostname); hostname = NULL; break; } } if(!tty) { if(!hostname) { fprintf(stderr, "Could not determine the external address or hostname. Please set Address manually.\n"); return NULL; } goto save; } again: fprintf(stderr, "Please enter your host's external address or hostname"); if(hostname) fprintf(stderr, " [%s]", hostname); fprintf(stderr, ": "); if(!fgets(line, sizeof line, stdin)) { fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno)); free(hostname); return NULL; } if(!rstrip(line)) { if(hostname) goto save; else goto again; } for(char *p = line; *p; p++) { if(isalnum(*p) || *p == '-' || *p == '.') continue; fprintf(stderr, "Invalid address or hostname.\n"); goto again; } free(hostname); hostname = xstrdup(line); save: if(*filename) { FILE *f = fopen(filename, "a"); if(f) { fprintf(f, "\nAddress = %s\n", hostname); fclose(f); } else { fprintf(stderr, "Could not append Address to %s: %s\n", filename, strerror(errno)); } } done: if(port) { if(strchr(hostname, ':')) xasprintf(&hostport, "[%s]:%s", hostname, port); else xasprintf(&hostport, "%s:%s", hostname, port); } else { if(strchr(hostname, ':')) xasprintf(&hostport, "[%s]", hostname); else hostport = xstrdup(hostname); } free(hostname); free(port); return hostport; }
int fsck(const char *argv0) { #ifdef HAVE_MINGW int uid = 0; #else uid_t uid = getuid(); #endif // Check that tinc.conf is readable. if(access(tinc_conf, R_OK)) { fprintf(stderr, "ERROR: cannot read %s: %s\n", tinc_conf, strerror(errno)); if(errno == ENOENT) { fprintf(stderr, "No tinc configuration found. Create a new one with:\n\n"); print_tinc_cmd(argv0, "init"); } else if(errno == EACCES) { if(uid != 0) fprintf(stderr, "You are currently not running tinc as root. Use sudo?\n"); else fprintf(stderr, "Check the permissions of each component of the path %s.\n", tinc_conf); } return 1; } char *name = get_my_name(true); if(!name) { fprintf(stderr, "ERROR: tinc cannot run without a valid Name.\n"); return 1; } // Check for private keys. // TODO: use RSAPrivateKeyFile and Ed25519PrivateKeyFile variables if present. struct stat st; char fname[PATH_MAX]; char dname[PATH_MAX]; ecdsa_t *ecdsa_priv = NULL; snprintf(fname, sizeof fname, "%s/ed25519_key.priv", confbase); if(stat(fname, &st)) { if(errno != ENOENT) { // Something is seriously wrong here. If we can access the directory with tinc.conf in it, we should certainly be able to stat() an existing file. fprintf(stderr, "ERROR: cannot read %s: %s\n", fname, strerror(errno)); fprintf(stderr, "Please correct this error.\n"); return 1; } } else { FILE *f = fopen(fname, "r"); if(!f) { fprintf(stderr, "ERROR: could not open %s: %s\n", fname, strerror(errno)); return 1; } ecdsa_priv = ecdsa_read_pem_private_key(f); fclose(f); if(!ecdsa_priv) { fprintf(stderr, "ERROR: No key or unusable key found in %s.\n", fname); fprintf(stderr, "You can generate a new Ed25519 key with:\n\n"); print_tinc_cmd(argv0, "generate-ed25519-keys"); return 1; } if(st.st_mode & 077) { fprintf(stderr, "WARNING: unsafe file permissions on %s.\n", fname); if(st.st_uid != uid) { fprintf(stderr, "You are not running %s as the same uid as %s.\n", argv0, fname); } else if(ask_fix()) { if(chmod(fname, st.st_mode & ~077)) fprintf(stderr, "ERROR: could not change permissions of %s: %s\n", fname, strerror(errno)); else fprintf(stderr, "Fixed permissions of %s.\n", fname); } } } if(!ecdsa_priv) { fprintf(stderr, "ERROR: No Ed25519 private key found.\n"); fprintf(stderr, "You can generate new keys with:\n\n"); print_tinc_cmd(argv0, "generate-keys"); return 1; } // Check for public keys. // TODO: use RSAPublicKeyFile and Ed25519PublicKeyFile variables if present. snprintf(fname, sizeof fname, "%s/hosts/%s", confbase, name); if(access(fname, R_OK)) fprintf(stderr, "WARNING: cannot read %s\n", fname); FILE *f; // TODO: this should read the Ed25519PublicKey config variable instead. ecdsa_t *ecdsa_pub = NULL; f = fopen(fname, "r"); if(f) ecdsa_pub = ecdsa_read_pem_public_key(f); fclose(f); if(ecdsa_priv) { if(!ecdsa_pub) { fprintf(stderr, "WARNING: No (usable) public Ed25519 key found.\n"); if(ask_fix()) { FILE *f = fopen(fname, "a"); if(f) { if(ecdsa_write_pem_public_key(ecdsa_priv, f)) fprintf(stderr, "Wrote Ed25519 public key to %s.\n", fname); else fprintf(stderr, "ERROR: could not write Ed25519 public key to %s.\n", fname); fclose(f); } else { fprintf(stderr, "ERROR: could not append to %s: %s\n", fname, strerror(errno)); } } } else { // TODO: suggest remedies char *key1 = ecdsa_get_base64_public_key(ecdsa_pub); if(!key1) { fprintf(stderr, "ERROR: public Ed25519 key does not work.\n"); return 1; } char *key2 = ecdsa_get_base64_public_key(ecdsa_priv); if(!key2) { free(key1); fprintf(stderr, "ERROR: private Ed25519 key does not work.\n"); return 1; } int result = strcmp(key1, key2); free(key1); free(key2); if(result) { fprintf(stderr, "ERROR: public and private Ed25519 keys do not match.\n"); return 1; } } } else { if(ecdsa_pub) fprintf(stderr, "WARNING: A public Ed25519 key was found but no private key is known.\n"); } // Check whether scripts are executable struct dirent *ent; DIR *dir = opendir(confbase); if(!dir) { fprintf(stderr, "ERROR: cannot read directory %s: %s\n", confbase, strerror(errno)); return 1; } while((ent = readdir(dir))) { if(strtailcmp(ent->d_name, "-up") && strtailcmp(ent->d_name, "-down")) continue; strncpy(fname, ent->d_name, sizeof fname); char *dash = strrchr(fname, '-'); if(!dash) continue; *dash = 0; if(strcmp(fname, "tinc") && strcmp(fname, "host") && strcmp(fname, "subnet")) { static bool explained = false; fprintf(stderr, "WARNING: Unknown script %s" SLASH "%s found.\n", confbase, ent->d_name); if(!explained) { fprintf(stderr, "The only scripts in %s executed by tinc are:\n", confbase); fprintf(stderr, "tinc-up, tinc-down, host-up, host-down, subnet-up and subnet-down.\n"); explained = true; } continue; } snprintf(fname, sizeof fname, "%s" SLASH "%s", confbase, ent->d_name); if(access(fname, R_OK | X_OK)) { if(errno != EACCES) { fprintf(stderr, "ERROR: cannot access %s: %s\n", fname, strerror(errno)); continue; } fprintf(stderr, "WARNING: cannot read and execute %s: %s\n", fname, strerror(errno)); if(ask_fix()) { if(chmod(fname, 0755)) fprintf(stderr, "ERROR: cannot change permissions on %s: %s\n", fname, strerror(errno)); } } } closedir(dir); snprintf(dname, sizeof dname, "%s" SLASH "hosts", confbase); dir = opendir(dname); if(!dir) { fprintf(stderr, "ERROR: cannot read directory %s: %s\n", dname, strerror(errno)); return 1; } while((ent = readdir(dir))) { if(strtailcmp(ent->d_name, "-up") && strtailcmp(ent->d_name, "-down")) continue; strncpy(fname, ent->d_name, sizeof fname); char *dash = strrchr(fname, '-'); if(!dash) continue; *dash = 0; snprintf(fname, sizeof fname, "%s" SLASH "hosts" SLASH "%s", confbase, ent->d_name); if(access(fname, R_OK | X_OK)) { if(errno != EACCES) { fprintf(stderr, "ERROR: cannot access %s: %s\n", fname, strerror(errno)); continue; } fprintf(stderr, "WARNING: cannot read and execute %s: %s\n", fname, strerror(errno)); if(ask_fix()) { if(chmod(fname, 0755)) fprintf(stderr, "ERROR: cannot change permissions on %s: %s\n", fname, strerror(errno)); } } } closedir(dir); // Check for obsolete / unsafe / unknown configuration variables. check_conffile(tinc_conf, true); dir = opendir(dname); if(dir) { while((ent = readdir(dir))) { if(!check_id(ent->d_name)) continue; snprintf(fname, sizeof fname, "%s" SLASH "hosts" SLASH "%s", confbase, ent->d_name); check_conffile(fname, false); } closedir(dir); } return 0; }
int cmd_invite(int argc, char *argv[]) { if(argc < 2) { fprintf(stderr, "Not enough arguments!\n"); return 1; } // Check validity of the new node's name if(!check_id(argv[1])) { fprintf(stderr, "Invalid name for node.\n"); return 1; } char *myname = get_my_name(true); if(!myname) return 1; // Ensure no host configuration file with that name exists char filename[PATH_MAX]; snprintf(filename, sizeof filename, "%s" SLASH "hosts" SLASH "%s", confbase, argv[1]); if(!access(filename, F_OK)) { fprintf(stderr, "A host config file for %s already exists!\n", argv[1]); return 1; } // If a daemon is running, ensure no other nodes know about this name bool found = false; if(connect_tincd(false)) { sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES); while(recvline(fd, line, sizeof line)) { char node[4096]; int code, req; if(sscanf(line, "%d %d %s", &code, &req, node) != 3) break; if(!strcmp(node, argv[1])) found = true; } if(found) { fprintf(stderr, "A node with name %s is already known!\n", argv[1]); return 1; } } snprintf(filename, sizeof filename, "%s" SLASH "invitations", confbase); if(mkdir(filename, 0700) && errno != EEXIST) { fprintf(stderr, "Could not create directory %s: %s\n", filename, strerror(errno)); return 1; } // Count the number of valid invitations, clean up old ones DIR *dir = opendir(filename); if(!dir) { fprintf(stderr, "Could not read directory %s: %s\n", filename, strerror(errno)); return 1; } errno = 0; int count = 0; struct dirent *ent; time_t deadline = time(NULL) - 604800; // 1 week in the past while((ent = readdir(dir))) { if(strlen(ent->d_name) != 24) continue; char invname[PATH_MAX]; struct stat st; snprintf(invname, sizeof invname, "%s" SLASH "%s", filename, ent->d_name); if(!stat(invname, &st)) { if(deadline < st.st_mtime) count++; else unlink(invname); } else { fprintf(stderr, "Could not stat %s: %s\n", invname, strerror(errno)); errno = 0; } } closedir(dir); if(errno) { fprintf(stderr, "Error while reading directory %s: %s\n", filename, strerror(errno)); return 1; } ecdsa_t *key; snprintf(filename, sizeof filename, "%s" SLASH "invitations" SLASH "ed25519_key.priv", confbase); // Remove the key if there are no outstanding invitations. if(!count) unlink(filename); // Create a new key if necessary. FILE *f = fopen(filename, "r"); if(!f) { if(errno != ENOENT) { fprintf(stderr, "Could not read %s: %s\n", filename, strerror(errno)); return 1; } key = ecdsa_generate(); if(!key) return 1; f = fopen(filename, "w"); if(!f) { fprintf(stderr, "Could not write %s: %s\n", filename, strerror(errno)); return 1; } chmod(filename, 0600); if(!ecdsa_write_pem_private_key(key, f)) { fprintf(stderr, "Could not write ECDSA private key\n"); fclose(f); return 1; } fclose(f); if(connect_tincd(false)) sendline(fd, "%d %d", CONTROL, REQ_RELOAD); } else { key = ecdsa_read_pem_private_key(f); fclose(f); if(!key) fprintf(stderr, "Could not read private key from %s\n", filename); } if(!key) return 1; // Create a hash of the key. char hash[64]; char *fingerprint = ecdsa_get_base64_public_key(key); sha512(fingerprint, strlen(fingerprint), hash); b64encode_urlsafe(hash, hash, 18); // Create a random cookie for this invitation. char cookie[25]; randomize(cookie, 18); // Create a filename that doesn't reveal the cookie itself char buf[18 + strlen(fingerprint)]; char cookiehash[64]; memcpy(buf, cookie, 18); memcpy(buf + 18, fingerprint, sizeof buf - 18); sha512(buf, sizeof buf, cookiehash); b64encode_urlsafe(cookiehash, cookiehash, 18); b64encode_urlsafe(cookie, cookie, 18); // Create a file containing the details of the invitation. snprintf(filename, sizeof filename, "%s" SLASH "invitations" SLASH "%s", confbase, cookiehash); int ifd = open(filename, O_RDWR | O_CREAT | O_EXCL, 0600); if(!ifd) { fprintf(stderr, "Could not create invitation file %s: %s\n", filename, strerror(errno)); return 1; } f = fdopen(ifd, "w"); if(!f) abort(); // Get the local address char *address = get_my_hostname(); // Fill in the details. fprintf(f, "Name = %s\n", argv[1]); if(netname) fprintf(f, "NetName = %s\n", netname); fprintf(f, "ConnectTo = %s\n", myname); // Copy Broadcast and Mode FILE *tc = fopen(tinc_conf, "r"); if(tc) { char buf[1024]; while(fgets(buf, sizeof buf, tc)) { if((!strncasecmp(buf, "Mode", 4) && strchr(" \t=", buf[4])) || (!strncasecmp(buf, "Broadcast", 9) && strchr(" \t=", buf[9]))) { fputs(buf, f); // Make sure there is a newline character. if(!strchr(buf, '\n')) fputc('\n', f); } } fclose(tc); } fprintf(f, "#---------------------------------------------------------------#\n"); fprintf(f, "Name = %s\n", myname); char filename2[PATH_MAX]; snprintf(filename2, sizeof filename2, "%s" SLASH "hosts" SLASH "%s", confbase, myname); fcopy(f, filename2); fclose(f); // Create an URL from the local address, key hash and cookie char *url; xasprintf(&url, "%s/%s%s", address, hash, cookie); // Call the inviation-created script char *envp[6] = {}; xasprintf(&envp[0], "NAME=%s", myname); xasprintf(&envp[1], "NETNAME=%s", netname); xasprintf(&envp[2], "NODE=%s", argv[1]); xasprintf(&envp[3], "INVITATION_FILE=%s", filename); xasprintf(&envp[4], "INVITATION_URL=%s", url); execute_script("invitation-created", envp); for(int i = 0; i < 6 && envp[i]; i++) free(envp[i]); puts(url); free(url); free(address); return 0; }