1 #include "shared.hh"
2 #include "local-store.hh"
3 #include "util.hh"
4 #include "serialise.hh"
5 #include "worker-protocol.hh"
6 #include "archive.hh"
7 #include "affinity.hh"
8 #include "globals.hh"
9 #include "monitor-fd.hh"
10 #include "derivations.hh"
11 #include "finally.hh"
12 #include "legacy.hh"
13 
14 #include <algorithm>
15 
16 #include <cstring>
17 #include <unistd.h>
18 #include <signal.h>
19 #include <sys/types.h>
20 #include <sys/wait.h>
21 #include <sys/stat.h>
22 #include <sys/socket.h>
23 #include <sys/un.h>
24 #include <errno.h>
25 #include <pwd.h>
26 #include <grp.h>
27 #include <fcntl.h>
28 #include <limits.h>
29 
30 #if __APPLE__ || __FreeBSD__
31 #include <sys/ucred.h>
32 #endif
33 
34 using namespace nix;
35 
36 #ifndef __linux__
37 #define SPLICE_F_MOVE 0
splice(int fd_in,void * off_in,int fd_out,void * off_out,size_t len,unsigned int flags)38 static ssize_t splice(int fd_in, void *off_in, int fd_out, void *off_out, size_t len, unsigned int flags)
39 {
40     /* We ignore most parameters, we just have them for conformance with the linux syscall */
41     std::vector<char> buf(8192);
42     auto read_count = read(fd_in, buf.data(), buf.size());
43     if (read_count == -1)
44         return read_count;
45     auto write_count = decltype(read_count)(0);
46     while (write_count < read_count) {
47         auto res = write(fd_out, buf.data() + write_count, read_count - write_count);
48         if (res == -1)
49             return res;
50         write_count += res;
51     }
52     return read_count;
53 }
54 #endif
55 
56 static FdSource from(STDIN_FILENO);
57 static FdSink to(STDOUT_FILENO);
58 
59 
operator <<(Sink & sink,const Logger::Fields & fields)60 Sink & operator << (Sink & sink, const Logger::Fields & fields)
61 {
62     sink << fields.size();
63     for (auto & f : fields) {
64         sink << f.type;
65         if (f.type == Logger::Field::tInt)
66             sink << f.i;
67         else if (f.type == Logger::Field::tString)
68             sink << f.s;
69         else abort();
70     }
71     return sink;
72 }
73 
74 
75 /* Logger that forwards log messages to the client, *if* we're in a
76    state where the protocol allows it (i.e., when canSendStderr is
77    true). */
78 struct TunnelLogger : public Logger
79 {
80     struct State
81     {
82         bool canSendStderr = false;
83         std::vector<std::string> pendingMsgs;
84     };
85 
86     Sync<State> state_;
87 
88     unsigned int clientVersion;
89 
TunnelLoggerTunnelLogger90     TunnelLogger(unsigned int clientVersion) : clientVersion(clientVersion) { }
91 
enqueueMsgTunnelLogger92     void enqueueMsg(const std::string & s)
93     {
94         auto state(state_.lock());
95 
96         if (state->canSendStderr) {
97             assert(state->pendingMsgs.empty());
98             try {
99                 to(s);
100                 to.flush();
101             } catch (...) {
102                 /* Write failed; that means that the other side is
103                    gone. */
104                 state->canSendStderr = false;
105                 throw;
106             }
107         } else
108             state->pendingMsgs.push_back(s);
109     }
110 
logTunnelLogger111     void log(Verbosity lvl, const FormatOrString & fs) override
112     {
113         if (lvl > verbosity) return;
114 
115         StringSink buf;
116         buf << STDERR_NEXT << (fs.s + "\n");
117         enqueueMsg(*buf.s);
118     }
119 
120     /* startWork() means that we're starting an operation for which we
121       want to send out stderr to the client. */
startWorkTunnelLogger122     void startWork()
123     {
124         auto state(state_.lock());
125         state->canSendStderr = true;
126 
127         for (auto & msg : state->pendingMsgs)
128             to(msg);
129 
130         state->pendingMsgs.clear();
131 
132         to.flush();
133     }
134 
135     /* stopWork() means that we're done; stop sending stderr to the
136        client. */
stopWorkTunnelLogger137     void stopWork(bool success = true, const string & msg = "", unsigned int status = 0)
138     {
139         auto state(state_.lock());
140 
141         state->canSendStderr = false;
142 
143         if (success)
144             to << STDERR_LAST;
145         else {
146             to << STDERR_ERROR << msg;
147             if (status != 0) to << status;
148         }
149     }
150 
startActivityTunnelLogger151     void startActivity(ActivityId act, Verbosity lvl, ActivityType type,
152         const std::string & s, const Fields & fields, ActivityId parent) override
153     {
154         if (GET_PROTOCOL_MINOR(clientVersion) < 20) {
155             if (!s.empty())
156                 log(lvl, s + "...");
157             return;
158         }
159 
160         StringSink buf;
161         buf << STDERR_START_ACTIVITY << act << lvl << type << s << fields << parent;
162         enqueueMsg(*buf.s);
163     }
164 
stopActivityTunnelLogger165     void stopActivity(ActivityId act) override
166     {
167         if (GET_PROTOCOL_MINOR(clientVersion) < 20) return;
168         StringSink buf;
169         buf << STDERR_STOP_ACTIVITY << act;
170         enqueueMsg(*buf.s);
171     }
172 
resultTunnelLogger173     void result(ActivityId act, ResultType type, const Fields & fields) override
174     {
175         if (GET_PROTOCOL_MINOR(clientVersion) < 20) return;
176         StringSink buf;
177         buf << STDERR_RESULT << act << type << fields;
178         enqueueMsg(*buf.s);
179     }
180 };
181 
182 
183 struct TunnelSink : Sink
184 {
185     Sink & to;
TunnelSinkTunnelSink186     TunnelSink(Sink & to) : to(to) { }
operator ()TunnelSink187     virtual void operator () (const unsigned char * data, size_t len)
188     {
189         to << STDERR_WRITE;
190         writeString(data, len, to);
191     }
192 };
193 
194 
195 struct TunnelSource : BufferedSource
196 {
197     Source & from;
TunnelSourceTunnelSource198     TunnelSource(Source & from) : from(from) { }
199 protected:
readUnbufferedTunnelSource200     size_t readUnbuffered(unsigned char * data, size_t len) override
201     {
202         to << STDERR_READ << len;
203         to.flush();
204         size_t n = readString(data, len, from);
205         if (n == 0) throw EndOfFile("unexpected end-of-file");
206         return n;
207     }
208 };
209 
210 
211 /* If the NAR archive contains a single file at top-level, then save
212    the contents of the file to `s'.  Otherwise barf. */
213 struct RetrieveRegularNARSink : ParseSink
214 {
215     bool regular;
216     string s;
217 
RetrieveRegularNARSinkRetrieveRegularNARSink218     RetrieveRegularNARSink() : regular(true) { }
219 
createDirectoryRetrieveRegularNARSink220     void createDirectory(const Path & path)
221     {
222         regular = false;
223     }
224 
receiveContentsRetrieveRegularNARSink225     void receiveContents(unsigned char * data, unsigned int len)
226     {
227         s.append((const char *) data, len);
228     }
229 
createSymlinkRetrieveRegularNARSink230     void createSymlink(const Path & path, const string & target)
231     {
232         regular = false;
233     }
234 };
235 
236 
performOp(TunnelLogger * logger,ref<Store> store,bool trusted,unsigned int clientVersion,Source & from,Sink & to,unsigned int op)237 static void performOp(TunnelLogger * logger, ref<Store> store,
238     bool trusted, unsigned int clientVersion,
239     Source & from, Sink & to, unsigned int op)
240 {
241     switch (op) {
242 
243     case wopIsValidPath: {
244         /* 'readStorePath' could raise an error leading to the connection
245            being closed.  To be able to recover from an invalid path error,
246            call 'startWork' early, and do 'assertStorePath' afterwards so
247            that the 'Error' exception handler doesn't close the
248            connection.  */
249         Path path = readString(from);
250         logger->startWork();
251         store->assertStorePath(path);
252         bool result = store->isValidPath(path);
253         logger->stopWork();
254         to << result;
255         break;
256     }
257 
258     case wopQueryValidPaths: {
259         PathSet paths = readStorePaths<PathSet>(*store, from);
260         logger->startWork();
261         PathSet res = store->queryValidPaths(paths);
262         logger->stopWork();
263         to << res;
264         break;
265     }
266 
267     case wopHasSubstitutes: {
268         Path path = readStorePath(*store, from);
269         logger->startWork();
270         PathSet res = store->querySubstitutablePaths({path});
271         logger->stopWork();
272         to << (res.find(path) != res.end());
273         break;
274     }
275 
276     case wopQuerySubstitutablePaths: {
277         PathSet paths = readStorePaths<PathSet>(*store, from);
278         logger->startWork();
279         PathSet res = store->querySubstitutablePaths(paths);
280         logger->stopWork();
281         to << res;
282         break;
283     }
284 
285     case wopQueryPathHash: {
286         Path path = readStorePath(*store, from);
287         logger->startWork();
288         auto hash = store->queryPathInfo(path)->narHash;
289         logger->stopWork();
290         to << hash.to_string(Base16, false);
291         break;
292     }
293 
294     case wopQueryReferences:
295     case wopQueryReferrers:
296     case wopQueryValidDerivers:
297     case wopQueryDerivationOutputs: {
298         Path path = readStorePath(*store, from);
299         logger->startWork();
300         PathSet paths;
301         if (op == wopQueryReferences)
302             paths = store->queryPathInfo(path)->references;
303         else if (op == wopQueryReferrers)
304             store->queryReferrers(path, paths);
305         else if (op == wopQueryValidDerivers)
306             paths = store->queryValidDerivers(path);
307         else paths = store->queryDerivationOutputs(path);
308         logger->stopWork();
309         to << paths;
310         break;
311     }
312 
313     case wopQueryDerivationOutputNames: {
314         Path path = readStorePath(*store, from);
315         logger->startWork();
316         StringSet names;
317         names = store->queryDerivationOutputNames(path);
318         logger->stopWork();
319         to << names;
320         break;
321     }
322 
323     case wopQueryDeriver: {
324         Path path = readStorePath(*store, from);
325         logger->startWork();
326         auto deriver = store->queryPathInfo(path)->deriver;
327         logger->stopWork();
328         to << deriver;
329         break;
330     }
331 
332     case wopQueryPathFromHashPart: {
333         string hashPart = readString(from);
334         logger->startWork();
335         Path path = store->queryPathFromHashPart(hashPart);
336         logger->stopWork();
337         to << path;
338         break;
339     }
340 
341     case wopAddToStore: {
342         bool fixed, recursive;
343         std::string s, baseName;
344         from >> baseName >> fixed /* obsolete */ >> recursive >> s;
345         /* Compatibility hack. */
346         if (!fixed) {
347             s = "sha256";
348             recursive = true;
349         }
350         HashType hashAlgo = parseHashType(s);
351 
352         TeeSource savedNAR(from);
353         RetrieveRegularNARSink savedRegular;
354 
355         if (recursive) {
356             /* Get the entire NAR dump from the client and save it to
357                a string so that we can pass it to
358                addToStoreFromDump(). */
359             ParseSink sink; /* null sink; just parse the NAR */
360             parseDump(sink, savedNAR);
361         } else
362             parseDump(savedRegular, from);
363 
364         logger->startWork();
365         if (!savedRegular.regular) throw Error("regular file expected");
366 
367         auto store2 = store.dynamic_pointer_cast<LocalStore>();
368         if (!store2) throw Error("operation is only supported by LocalStore");
369 
370         Path path = store2->addToStoreFromDump(recursive ? *savedNAR.data : savedRegular.s, baseName, recursive, hashAlgo);
371         logger->stopWork();
372 
373         to << path;
374         break;
375     }
376 
377     case wopAddTextToStore: {
378         string suffix = readString(from);
379         string s = readString(from);
380         PathSet refs = readStorePaths<PathSet>(*store, from);
381         logger->startWork();
382         Path path = store->addTextToStore(suffix, s, refs, NoRepair);
383         logger->stopWork();
384         to << path;
385         break;
386     }
387 
388     case wopExportPath: {
389         Path path = readStorePath(*store, from);
390         readInt(from); // obsolete
391         logger->startWork();
392         TunnelSink sink(to);
393         store->exportPath(path, sink);
394         logger->stopWork();
395         to << 1;
396         break;
397     }
398 
399     case wopImportPaths: {
400         logger->startWork();
401         TunnelSource source(from);
402         Paths paths = store->importPaths(source, nullptr,
403             trusted ? NoCheckSigs : CheckSigs);
404         logger->stopWork();
405         to << paths;
406         break;
407     }
408 
409     case wopBuildPaths: {
410         PathSet drvs = readStorePaths<PathSet>(*store, from);
411         BuildMode mode = bmNormal;
412         if (GET_PROTOCOL_MINOR(clientVersion) >= 15) {
413             mode = (BuildMode) readInt(from);
414 
415             /* Repairing is not atomic, so disallowed for "untrusted"
416                clients.  */
417             if (mode == bmRepair && !trusted)
418                 throw Error("repairing is not allowed because you are not in 'trusted-users'");
419         }
420         logger->startWork();
421         store->buildPaths(drvs, mode);
422         logger->stopWork();
423         to << 1;
424         break;
425     }
426 
427     case wopBuildDerivation: {
428         Path drvPath = readStorePath(*store, from);
429         BasicDerivation drv;
430         readDerivation(from, *store, drv);
431         BuildMode buildMode = (BuildMode) readInt(from);
432         logger->startWork();
433         if (!trusted)
434             throw Error("you are not privileged to build derivations");
435         auto res = store->buildDerivation(drvPath, drv, buildMode);
436         logger->stopWork();
437         to << res.status << res.errorMsg;
438         break;
439     }
440 
441     case wopEnsurePath: {
442         Path path = readStorePath(*store, from);
443         logger->startWork();
444         store->ensurePath(path);
445         logger->stopWork();
446         to << 1;
447         break;
448     }
449 
450     case wopAddTempRoot: {
451         Path path = readStorePath(*store, from);
452         logger->startWork();
453         store->addTempRoot(path);
454         logger->stopWork();
455         to << 1;
456         break;
457     }
458 
459     case wopAddIndirectRoot: {
460         Path path = absPath(readString(from));
461         logger->startWork();
462         store->addIndirectRoot(path);
463         logger->stopWork();
464         to << 1;
465         break;
466     }
467 
468     case wopSyncWithGC: {
469         logger->startWork();
470         store->syncWithGC();
471         logger->stopWork();
472         to << 1;
473         break;
474     }
475 
476     case wopFindRoots: {
477         logger->startWork();
478         Roots roots = store->findRoots(!trusted);
479         logger->stopWork();
480 
481         size_t size = 0;
482         for (auto & i : roots)
483             size += i.second.size();
484 
485         to << size;
486 
487         for (auto & [target, links] : roots)
488             for (auto & link : links)
489                 to << link << target;
490 
491         break;
492     }
493 
494     case wopCollectGarbage: {
495         GCOptions options;
496         options.action = (GCOptions::GCAction) readInt(from);
497         options.pathsToDelete = readStorePaths<PathSet>(*store, from);
498         from >> options.ignoreLiveness >> options.maxFreed;
499         // obsolete fields
500         readInt(from);
501         readInt(from);
502         readInt(from);
503 
504         GCResults results;
505 
506         logger->startWork();
507         if (options.ignoreLiveness)
508             throw Error("you are not allowed to ignore liveness");
509         store->collectGarbage(options, results);
510         logger->stopWork();
511 
512         to << results.paths << results.bytesFreed << 0 /* obsolete */;
513 
514         break;
515     }
516 
517     case wopSetOptions: {
518         settings.keepFailed = readInt(from);
519         settings.keepGoing = readInt(from);
520         settings.tryFallback = readInt(from);
521         verbosity = (Verbosity) readInt(from);
522         settings.maxBuildJobs.assign(readInt(from));
523         settings.maxSilentTime = readInt(from);
524         readInt(from); // obsolete useBuildHook
525         settings.verboseBuild = lvlError == (Verbosity) readInt(from);
526         readInt(from); // obsolete logType
527         readInt(from); // obsolete printBuildTrace
528         settings.buildCores = readInt(from);
529         settings.useSubstitutes  = readInt(from);
530 
531         StringMap overrides;
532         if (GET_PROTOCOL_MINOR(clientVersion) >= 12) {
533             unsigned int n = readInt(from);
534             for (unsigned int i = 0; i < n; i++) {
535                 string name = readString(from);
536                 string value = readString(from);
537                 overrides.emplace(name, value);
538             }
539         }
540 
541         logger->startWork();
542 
543         for (auto & i : overrides) {
544             auto & name(i.first);
545             auto & value(i.second);
546 
547             auto setSubstituters = [&](Setting<Strings> & res) {
548                 if (name != res.name && res.aliases.count(name) == 0)
549                     return false;
550                 StringSet trusted = settings.trustedSubstituters;
551                 for (auto & s : settings.substituters.get())
552                     trusted.insert(s);
553                 Strings subs;
554                 auto ss = tokenizeString<Strings>(value);
555                 for (auto & s : ss)
556                     if (trusted.count(s))
557                         subs.push_back(s);
558                     else
559                         warn("ignoring untrusted substituter '%s'", s);
560                 res = subs;
561                 return true;
562             };
563 
564             try {
565                 if (name == "ssh-auth-sock") // obsolete
566                     ;
567                 else if (trusted
568                     || name == settings.buildTimeout.name
569                     || name == "connect-timeout"
570                     || (name == "builders" && value == ""))
571                     settings.set(name, value);
572                 else if (setSubstituters(settings.substituters))
573                     ;
574                 else if (setSubstituters(settings.extraSubstituters))
575                     ;
576                 else
577                     warn("ignoring the user-specified setting '%s', because it is a restricted setting and you are not a trusted user", name);
578             } catch (UsageError & e) {
579                 warn(e.what());
580             }
581         }
582 
583         logger->stopWork();
584         break;
585     }
586 
587     case wopQuerySubstitutablePathInfo: {
588         Path path = absPath(readString(from));
589         logger->startWork();
590         SubstitutablePathInfos infos;
591         store->querySubstitutablePathInfos({path}, infos);
592         logger->stopWork();
593         SubstitutablePathInfos::iterator i = infos.find(path);
594         if (i == infos.end())
595             to << 0;
596         else {
597             to << 1 << i->second.deriver << i->second.references << i->second.downloadSize << i->second.narSize;
598         }
599         break;
600     }
601 
602     case wopQuerySubstitutablePathInfos: {
603         PathSet paths = readStorePaths<PathSet>(*store, from);
604         logger->startWork();
605         SubstitutablePathInfos infos;
606         store->querySubstitutablePathInfos(paths, infos);
607         logger->stopWork();
608         to << infos.size();
609         for (auto & i : infos) {
610             to << i.first << i.second.deriver << i.second.references
611                << i.second.downloadSize << i.second.narSize;
612         }
613         break;
614     }
615 
616     case wopQueryAllValidPaths: {
617         logger->startWork();
618         PathSet paths = store->queryAllValidPaths();
619         logger->stopWork();
620         to << paths;
621         break;
622     }
623 
624     case wopQueryPathInfo: {
625         Path path = readStorePath(*store, from);
626         std::shared_ptr<const ValidPathInfo> info;
627         logger->startWork();
628         try {
629             info = store->queryPathInfo(path);
630         } catch (InvalidPath &) {
631             if (GET_PROTOCOL_MINOR(clientVersion) < 17) throw;
632         }
633         logger->stopWork();
634         if (info) {
635             if (GET_PROTOCOL_MINOR(clientVersion) >= 17)
636                 to << 1;
637             to << info->deriver << info->narHash.to_string(Base16, false) << info->references
638                << info->registrationTime << info->narSize;
639             if (GET_PROTOCOL_MINOR(clientVersion) >= 16) {
640                 to << info->ultimate
641                    << info->sigs
642                    << info->ca;
643             }
644         } else {
645             assert(GET_PROTOCOL_MINOR(clientVersion) >= 17);
646             to << 0;
647         }
648         break;
649     }
650 
651     case wopOptimiseStore:
652         logger->startWork();
653         store->optimiseStore();
654         logger->stopWork();
655         to << 1;
656         break;
657 
658     case wopVerifyStore: {
659         bool checkContents, repair;
660         from >> checkContents >> repair;
661         logger->startWork();
662         if (repair && !trusted)
663             throw Error("you are not privileged to repair paths");
664         bool errors = store->verifyStore(checkContents, (RepairFlag) repair);
665         logger->stopWork();
666         to << errors;
667         break;
668     }
669 
670     case wopAddSignatures: {
671         Path path = readStorePath(*store, from);
672         StringSet sigs = readStrings<StringSet>(from);
673         logger->startWork();
674         if (!trusted)
675             throw Error("you are not privileged to add signatures");
676         store->addSignatures(path, sigs);
677         logger->stopWork();
678         to << 1;
679         break;
680     }
681 
682     case wopNarFromPath: {
683         auto path = readStorePath(*store, from);
684         logger->startWork();
685         logger->stopWork();
686         dumpPath(path, to);
687         break;
688     }
689 
690     case wopAddToStoreNar: {
691         bool repair, dontCheckSigs;
692         ValidPathInfo info;
693         info.path = readStorePath(*store, from);
694         from >> info.deriver;
695         if (!info.deriver.empty())
696             store->assertStorePath(info.deriver);
697         info.narHash = Hash(readString(from), htSHA256);
698         info.references = readStorePaths<PathSet>(*store, from);
699         from >> info.registrationTime >> info.narSize >> info.ultimate;
700         info.sigs = readStrings<StringSet>(from);
701         from >> info.ca >> repair >> dontCheckSigs;
702         if (!trusted && dontCheckSigs)
703             dontCheckSigs = false;
704         if (!trusted)
705             info.ultimate = false;
706 
707         std::string saved;
708         std::unique_ptr<Source> source;
709         if (GET_PROTOCOL_MINOR(clientVersion) >= 21)
710             source = std::make_unique<TunnelSource>(from);
711         else {
712             TeeSink tee(from);
713             parseDump(tee, tee.source);
714             saved = std::move(*tee.source.data);
715             source = std::make_unique<StringSource>(saved);
716         }
717 
718         logger->startWork();
719 
720         // FIXME: race if addToStore doesn't read source?
721         store->addToStore(info, *source, (RepairFlag) repair,
722             dontCheckSigs ? NoCheckSigs : CheckSigs, nullptr);
723 
724         logger->stopWork();
725         break;
726     }
727 
728     case wopQueryMissing: {
729         PathSet targets = readStorePaths<PathSet>(*store, from);
730         logger->startWork();
731         PathSet willBuild, willSubstitute, unknown;
732         unsigned long long downloadSize, narSize;
733         store->queryMissing(targets, willBuild, willSubstitute, unknown, downloadSize, narSize);
734         logger->stopWork();
735         to << willBuild << willSubstitute << unknown << downloadSize << narSize;
736         break;
737     }
738 
739     default:
740         throw Error(format("invalid operation %1%") % op);
741     }
742 }
743 
744 
processConnection(bool trusted,const std::string & userName,uid_t userId)745 static void processConnection(bool trusted,
746     const std::string & userName, uid_t userId)
747 {
748     MonitorFdHup monitor(from.fd);
749 
750     /* Exchange the greeting. */
751     unsigned int magic = readInt(from);
752     if (magic != WORKER_MAGIC_1) throw Error("protocol mismatch");
753     to << WORKER_MAGIC_2 << PROTOCOL_VERSION;
754     to.flush();
755     unsigned int clientVersion = readInt(from);
756 
757     if (clientVersion < 0x10a)
758         throw Error("the Nix client version is too old");
759 
760     auto tunnelLogger = new TunnelLogger(clientVersion);
761     auto prevLogger = nix::logger;
762     logger = tunnelLogger;
763 
764     unsigned int opCount = 0;
765 
766     Finally finally([&]() {
767         _isInterrupted = false;
768         prevLogger->log(lvlDebug, fmt("%d operations", opCount));
769     });
770 
771     if (GET_PROTOCOL_MINOR(clientVersion) >= 14 && readInt(from))
772         setAffinityTo(readInt(from));
773 
774     readInt(from); // obsolete reserveSpace
775 
776     /* Send startup error messages to the client. */
777     tunnelLogger->startWork();
778 
779     try {
780 
781         /* If we can't accept clientVersion, then throw an error
782            *here* (not above). */
783 
784 #if 0
785         /* Prevent users from doing something very dangerous. */
786         if (geteuid() == 0 &&
787             querySetting("build-users-group", "") == "")
788             throw Error("if you run 'nix-daemon' as root, then you MUST set 'build-users-group'!");
789 #endif
790 
791         /* Open the store. */
792         Store::Params params; // FIXME: get params from somewhere
793         // Disable caching since the client already does that.
794         params["path-info-cache-size"] = "0";
795         auto store = openStore(settings.storeUri, params);
796 
797         store->createUser(userName, userId);
798 
799         tunnelLogger->stopWork();
800         to.flush();
801 
802         /* Process client requests. */
803         while (true) {
804             WorkerOp op;
805             try {
806                 op = (WorkerOp) readInt(from);
807             } catch (Interrupted & e) {
808                 break;
809             } catch (EndOfFile & e) {
810                 break;
811             }
812 
813             opCount++;
814 
815             try {
816                 performOp(tunnelLogger, store, trusted, clientVersion, from, to, op);
817             } catch (Error & e) {
818                 /* If we're not in a state where we can send replies, then
819                    something went wrong processing the input of the
820                    client.  This can happen especially if I/O errors occur
821                    during addTextToStore() / importPath().  If that
822                    happens, just send the error message and exit. */
823                 bool errorAllowed = tunnelLogger->state_.lock()->canSendStderr;
824                 tunnelLogger->stopWork(false, e.msg(), e.status);
825                 if (!errorAllowed) throw;
826             } catch (std::bad_alloc & e) {
827                 tunnelLogger->stopWork(false, "Nix daemon out of memory", 1);
828                 throw;
829             }
830 
831             to.flush();
832 
833             assert(!tunnelLogger->state_.lock()->canSendStderr);
834         };
835 
836     } catch (std::exception & e) {
837         tunnelLogger->stopWork(false, e.what(), 1);
838         to.flush();
839         return;
840     }
841 }
842 
843 
sigChldHandler(int sigNo)844 static void sigChldHandler(int sigNo)
845 {
846     // Ensure we don't modify errno of whatever we've interrupted
847     auto saved_errno = errno;
848     /* Reap all dead children. */
849     while (waitpid(-1, 0, WNOHANG) > 0) ;
850     errno = saved_errno;
851 }
852 
853 
setSigChldAction(bool autoReap)854 static void setSigChldAction(bool autoReap)
855 {
856     struct sigaction act, oact;
857     act.sa_handler = autoReap ? sigChldHandler : SIG_DFL;
858     sigfillset(&act.sa_mask);
859     act.sa_flags = 0;
860     if (sigaction(SIGCHLD, &act, &oact))
861         throw SysError("setting SIGCHLD handler");
862 }
863 
864 
matchUser(const string & user,const string & group,const Strings & users)865 bool matchUser(const string & user, const string & group, const Strings & users)
866 {
867     if (find(users.begin(), users.end(), "*") != users.end())
868         return true;
869 
870     if (find(users.begin(), users.end(), user) != users.end())
871         return true;
872 
873     for (auto & i : users)
874         if (string(i, 0, 1) == "@") {
875             if (group == string(i, 1)) return true;
876             struct group * gr = getgrnam(i.c_str() + 1);
877             if (!gr) continue;
878             for (char * * mem = gr->gr_mem; *mem; mem++)
879                 if (user == string(*mem)) return true;
880         }
881 
882     return false;
883 }
884 
885 
886 struct PeerInfo
887 {
888     bool pidKnown;
889     pid_t pid;
890     bool uidKnown;
891     uid_t uid;
892     bool gidKnown;
893     gid_t gid;
894 };
895 
896 
897 /* Get the identity of the caller, if possible. */
getPeerInfo(int remote)898 static PeerInfo getPeerInfo(int remote)
899 {
900     PeerInfo peer = { false, 0, false, 0, false, 0 };
901 
902 #if defined(SO_PEERCRED)
903 
904     ucred cred;
905     socklen_t credLen = sizeof(cred);
906     if (getsockopt(remote, SOL_SOCKET, SO_PEERCRED, &cred, &credLen) == -1)
907         throw SysError("getting peer credentials");
908     peer = { true, cred.pid, true, cred.uid, true, cred.gid };
909 
910 #elif defined(LOCAL_PEERCRED)
911 
912 #if !defined(SOL_LOCAL)
913 #define SOL_LOCAL 0
914 #endif
915 
916     xucred cred;
917     socklen_t credLen = sizeof(cred);
918     if (getsockopt(remote, SOL_LOCAL, LOCAL_PEERCRED, &cred, &credLen) == -1)
919         throw SysError("getting peer credentials");
920     peer = { false, 0, true, cred.cr_uid, false, 0 };
921 
922 #endif
923 
924     return peer;
925 }
926 
927 
928 #define SD_LISTEN_FDS_START 3
929 
930 
daemonLoop(char ** argv)931 static void daemonLoop(char * * argv)
932 {
933     if (chdir("/") == -1)
934         throw SysError("cannot change current directory");
935 
936     /* Get rid of children automatically; don't let them become
937        zombies. */
938     setSigChldAction(true);
939 
940     AutoCloseFD fdSocket;
941 
942     /* Handle socket-based activation by systemd. */
943     if (getEnv("LISTEN_FDS") != "") {
944         if (getEnv("LISTEN_PID") != std::to_string(getpid()) || getEnv("LISTEN_FDS") != "1")
945             throw Error("unexpected systemd environment variables");
946         fdSocket = SD_LISTEN_FDS_START;
947     }
948 
949     /* Otherwise, create and bind to a Unix domain socket. */
950     else {
951 
952         /* Create and bind to a Unix domain socket. */
953         fdSocket = socket(PF_UNIX, SOCK_STREAM, 0);
954         if (!fdSocket)
955             throw SysError("cannot create Unix domain socket");
956 
957         string socketPath = settings.nixDaemonSocketFile;
958 
959         createDirs(dirOf(socketPath));
960 
961         /* Urgh, sockaddr_un allows path names of only 108 characters.
962            So chdir to the socket directory so that we can pass a
963            relative path name. */
964         if (chdir(dirOf(socketPath).c_str()) == -1)
965             throw SysError("cannot change current directory");
966         Path socketPathRel = "./" + baseNameOf(socketPath);
967 
968         struct sockaddr_un addr;
969         addr.sun_family = AF_UNIX;
970         if (socketPathRel.size() + 1 >= sizeof(addr.sun_path))
971             throw Error(format("socket path '%1%' is too long") % socketPathRel);
972         strcpy(addr.sun_path, socketPathRel.c_str());
973 
974         unlink(socketPath.c_str());
975 
976         /* Make sure that the socket is created with 0666 permission
977            (everybody can connect --- provided they have access to the
978            directory containing the socket). */
979         mode_t oldMode = umask(0111);
980         int res = bind(fdSocket.get(), (struct sockaddr *) &addr, sizeof(addr));
981         umask(oldMode);
982         if (res == -1)
983             throw SysError(format("cannot bind to socket '%1%'") % socketPath);
984 
985         if (chdir("/") == -1) /* back to the root */
986             throw SysError("cannot change current directory");
987 
988         if (listen(fdSocket.get(), 5) == -1)
989             throw SysError(format("cannot listen on socket '%1%'") % socketPath);
990     }
991 
992     closeOnExec(fdSocket.get());
993 
994     /* Loop accepting connections. */
995     while (1) {
996 
997         try {
998             /* Accept a connection. */
999             struct sockaddr_un remoteAddr;
1000             socklen_t remoteAddrLen = sizeof(remoteAddr);
1001 
1002             AutoCloseFD remote = accept(fdSocket.get(),
1003                 (struct sockaddr *) &remoteAddr, &remoteAddrLen);
1004             checkInterrupt();
1005             if (!remote) {
1006                 if (errno == EINTR) continue;
1007                 throw SysError("accepting connection");
1008             }
1009 
1010             closeOnExec(remote.get());
1011 
1012             bool trusted = false;
1013             PeerInfo peer = getPeerInfo(remote.get());
1014 
1015             struct passwd * pw = peer.uidKnown ? getpwuid(peer.uid) : 0;
1016             string user = pw ? pw->pw_name : std::to_string(peer.uid);
1017 
1018             struct group * gr = peer.gidKnown ? getgrgid(peer.gid) : 0;
1019             string group = gr ? gr->gr_name : std::to_string(peer.gid);
1020 
1021             Strings trustedUsers = settings.trustedUsers;
1022             Strings allowedUsers = settings.allowedUsers;
1023 
1024             if (matchUser(user, group, trustedUsers))
1025                 trusted = true;
1026 
1027             if ((!trusted && !matchUser(user, group, allowedUsers)) || group == settings.buildUsersGroup)
1028                 throw Error(format("user '%1%' is not allowed to connect to the Nix daemon") % user);
1029 
1030             printInfo(format((string) "accepted connection from pid %1%, user %2%" + (trusted ? " (trusted)" : ""))
1031                 % (peer.pidKnown ? std::to_string(peer.pid) : "<unknown>")
1032                 % (peer.uidKnown ? user : "<unknown>"));
1033 
1034             /* Fork a child to handle the connection. */
1035             ProcessOptions options;
1036             options.errorPrefix = "unexpected Nix daemon error: ";
1037             options.dieWithParent = false;
1038             options.runExitHandlers = true;
1039             options.allowVfork = false;
1040             startProcess([&]() {
1041                 fdSocket = -1;
1042 
1043                 /* Background the daemon. */
1044                 if (setsid() == -1)
1045                     throw SysError(format("creating a new session"));
1046 
1047                 /* Restore normal handling of SIGCHLD. */
1048                 setSigChldAction(false);
1049 
1050                 /* For debugging, stuff the pid into argv[1]. */
1051                 if (peer.pidKnown && argv[1]) {
1052                     string processName = std::to_string(peer.pid);
1053                     strncpy(argv[1], processName.c_str(), strlen(argv[1]));
1054                 }
1055 
1056                 /* Handle the connection. */
1057                 from.fd = remote.get();
1058                 to.fd = remote.get();
1059                 processConnection(trusted, user, peer.uid);
1060 
1061                 exit(0);
1062             }, options);
1063 
1064         } catch (Interrupted & e) {
1065             return;
1066         } catch (Error & e) {
1067             printError(format("error processing connection: %1%") % e.msg());
1068         }
1069     }
1070 }
1071 
1072 
_main(int argc,char ** argv)1073 static int _main(int argc, char * * argv)
1074 {
1075     {
1076         auto stdio = false;
1077 
1078         parseCmdLine(argc, argv, [&](Strings::iterator & arg, const Strings::iterator & end) {
1079             if (*arg == "--daemon")
1080                 ; /* ignored for backwards compatibility */
1081             else if (*arg == "--help")
1082                 showManPage("nix-daemon");
1083             else if (*arg == "--version")
1084                 printVersion("nix-daemon");
1085             else if (*arg == "--stdio")
1086                 stdio = true;
1087             else return false;
1088             return true;
1089         });
1090 
1091         initPlugins();
1092 
1093         if (stdio) {
1094             if (getStoreType() == tDaemon) {
1095                 /* Forward on this connection to the real daemon */
1096                 auto socketPath = settings.nixDaemonSocketFile;
1097                 auto s = socket(PF_UNIX, SOCK_STREAM, 0);
1098                 if (s == -1)
1099                     throw SysError("creating Unix domain socket");
1100 
1101                 auto socketDir = dirOf(socketPath);
1102                 if (chdir(socketDir.c_str()) == -1)
1103                     throw SysError(format("changing to socket directory '%1%'") % socketDir);
1104 
1105                 auto socketName = baseNameOf(socketPath);
1106                 auto addr = sockaddr_un{};
1107                 addr.sun_family = AF_UNIX;
1108                 if (socketName.size() + 1 >= sizeof(addr.sun_path))
1109                     throw Error(format("socket name %1% is too long") % socketName);
1110                 strcpy(addr.sun_path, socketName.c_str());
1111 
1112                 if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) == -1)
1113                     throw SysError(format("cannot connect to daemon at %1%") % socketPath);
1114 
1115                 auto nfds = (s > STDIN_FILENO ? s : STDIN_FILENO) + 1;
1116                 while (true) {
1117                     fd_set fds;
1118                     FD_ZERO(&fds);
1119                     FD_SET(s, &fds);
1120                     FD_SET(STDIN_FILENO, &fds);
1121                     if (select(nfds, &fds, nullptr, nullptr, nullptr) == -1)
1122                         throw SysError("waiting for data from client or server");
1123                     if (FD_ISSET(s, &fds)) {
1124                         auto res = splice(s, nullptr, STDOUT_FILENO, nullptr, SSIZE_MAX, SPLICE_F_MOVE);
1125                         if (res == -1)
1126                             throw SysError("splicing data from daemon socket to stdout");
1127                         else if (res == 0)
1128                             throw EndOfFile("unexpected EOF from daemon socket");
1129                     }
1130                     if (FD_ISSET(STDIN_FILENO, &fds)) {
1131                         auto res = splice(STDIN_FILENO, nullptr, s, nullptr, SSIZE_MAX, SPLICE_F_MOVE);
1132                         if (res == -1)
1133                             throw SysError("splicing data from stdin to daemon socket");
1134                         else if (res == 0)
1135                             return 0;
1136                     }
1137                 }
1138             } else {
1139                 processConnection(true, "root", 0);
1140             }
1141         } else {
1142             daemonLoop(argv);
1143         }
1144 
1145         return 0;
1146     }
1147 }
1148 
1149 static RegisterLegacyCommand s1("nix-daemon", _main);
1150