1 /*
2   Copyright 2021 Northern.tech AS
3 
4   This file is part of CFEngine 3 - written and maintained by Northern.tech AS.
5 
6   This program is free software; you can redistribute it and/or modify it
7   under the terms of the GNU General Public License as published by the
8   Free Software Foundation; version 3.
9 
10   This program is distributed in the hope that it will be useful,
11   but WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   GNU General Public License for more details.
14 
15   You should have received a copy of the GNU General Public License
16   along with this program; if not, write to the Free Software
17   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
18 
19   To the extent this program is licensed as part of the Enterprise
20   versions of CFEngine, the applicable Commercial Open Source License
21   (COSL) may apply to this file if you as a licensee so wish it. See
22   included file COSL.txt.
23 */
24 
25 #include <locks.h>
26 #include <global_mutex.h>
27 #include <mutex.h>
28 #include <string_lib.h>
29 #include <files_interfaces.h>
30 #include <files_lib.h>
31 #include <cleanup.h>
32 #include <policy.h>
33 #include <hash.h>
34 #include <rb-tree.h>
35 #include <files_names.h>
36 #include <rlist.h>
37 #include <process_lib.h>
38 #include <fncall.h>
39 #include <eval_context.h>
40 #include <misc_lib.h>
41 #include <known_dirs.h>
42 #include <sysinfo.h>
43 #include <openssl/evp.h>
44 #include <libcrypto-compat.h>
45 
46 #ifdef LMDB
47 // Be careful if you want to change this,
48 // it must match mdb_env_get_maxkeysize(env)
49 #define LMDB_MAX_KEY_SIZE 511
50 #endif
51 
52 #define CFLOGSIZE 1048576       /* Size of lock-log before rotation */
53 #define CF_LOCKHORIZON ((time_t)(SECONDS_PER_WEEK * 4))
54 #define CF_MAXLOCKNUM 8192
55 
56 #define CF_CRITIAL_SECTION "CF_CRITICAL_SECTION"
57 
58 #define LOG_LOCK_ENTRY(__lock, __lock_sum, __lock_data)         \
59     log_lock("Entering", __FUNCTION__, __lock, __lock_sum, __lock_data)
60 #define LOG_LOCK_EXIT(__lock, __lock_sum, __lock_data)          \
61     log_lock("Exiting", __FUNCTION__, __lock, __lock_sum, __lock_data)
62 #define LOG_LOCK_OP(__lock, __lock_sum, __lock_data)            \
63     log_lock("Performing", __FUNCTION__, __lock, __lock_sum, __lock_data)
64 
65 typedef struct CfLockStack_ {
66     char lock[CF_BUFSIZE];
67     char last[CF_BUFSIZE];
68     struct CfLockStack_ *previous;
69 } CfLockStack;
70 
71 static CfLockStack *LOCK_STACK = NULL;
72 
PushLock(char * lock,char * last)73 static void PushLock(char *lock, char *last)
74 {
75     CfLockStack *new_lock = malloc(sizeof(CfLockStack));
76     strlcpy(new_lock->lock, lock, CF_BUFSIZE);
77     strlcpy(new_lock->last, last, CF_BUFSIZE);
78 
79     new_lock->previous = LOCK_STACK;
80     LOCK_STACK = new_lock;
81 }
82 
PopLock()83 static CfLockStack *PopLock()
84 {
85     if (!LOCK_STACK)
86     {
87         return NULL;
88     }
89     CfLockStack *lock = LOCK_STACK;
90     LOCK_STACK = lock->previous;
91     return lock;
92 }
93 
94 static void CopyLockDatabaseAtomically(const char *from, const char *to,
95                                        const char *from_pretty_name,
96                                        const char *to_pretty_name);
97 
98 static void RestoreLockDatabase(void);
99 
VerifyThatDatabaseIsNotCorrupt_once(void)100 static void VerifyThatDatabaseIsNotCorrupt_once(void)
101 {
102     int uptime = GetUptimeSeconds(time(NULL));
103     if (uptime <= 0)
104     {
105         Log(LOG_LEVEL_VERBOSE,
106             "Not able to determine uptime when verifying lock database. "
107             "Will assume the database is in order.");
108         return;
109     }
110 
111     char *db_path = DBIdToPath(dbid_locks);
112     struct stat statbuf;
113     if (stat(db_path, &statbuf) == 0)
114     {
115         if (statbuf.st_mtime < time(NULL) - uptime)
116         {
117             // We have rebooted since the database was last updated.
118             // Restore it from our backup.
119             RestoreLockDatabase();
120         }
121     }
122 
123     free(db_path);
124 }
125 
VerifyThatDatabaseIsNotCorrupt(void)126 static void VerifyThatDatabaseIsNotCorrupt(void)
127 {
128     static pthread_once_t uptime_verified = PTHREAD_ONCE_INIT;
129     pthread_once(&uptime_verified, &VerifyThatDatabaseIsNotCorrupt_once);
130 }
131 
OpenLock()132 CF_DB *OpenLock()
133 {
134     CF_DB *dbp;
135 
136     VerifyThatDatabaseIsNotCorrupt();
137 
138     if (!OpenDB(&dbp, dbid_locks))
139     {
140         return NULL;
141     }
142 
143     return dbp;
144 }
145 
CloseLock(CF_DB * dbp)146 void CloseLock(CF_DB *dbp)
147 {
148     if (dbp)
149     {
150         CloseDB(dbp);
151     }
152 }
153 
154 static pthread_once_t lock_cleanup_once = PTHREAD_ONCE_INIT; /* GLOBAL_X */
155 
156 #ifdef LMDB
log_lock(const char * op,const char * function,const char * lock,const char * lock_sum,const LockData * lock_data)157 static inline void log_lock(const char *op,
158                             const char *function,
159                             const char *lock,
160                             const char *lock_sum,
161                             const LockData *lock_data)
162 {
163     /* Check log level first to save cycles when not in debug mode. */
164     if (LogGetGlobalLevel() >= LOG_LEVEL_DEBUG)
165     {
166         if (lock_data)
167         {
168             LogDebug(LOG_MOD_LOCKS, "%s lock operation in '%s()': "
169                      "lock_id = '%s', lock_checksum = '%s', "
170                      "lock.pid = '%d', lock.time = '%d', "
171                      "lock.process_start_time = '%d'",
172                      op, function, lock, lock_sum,
173                      (int)lock_data->pid, (int)lock_data->time,
174                      (int)lock_data->process_start_time);
175         }
176         else
177         {
178             LogDebug(LOG_MOD_LOCKS, "%s lock operation in '%s()'. "
179                      "lock_id = '%s', lock_checksum = '%s'",
180                      op, function, lock, lock_sum);
181         }
182     }
183 }
184 
HashLockKeyIfNecessary(const char * const istring,char * const ohash)185 static void HashLockKeyIfNecessary(const char *const istring, char *const ohash)
186 {
187     assert(strlen("CF_CRITICAL_SECTION") < LMDB_MAX_KEY_SIZE);
188     assert(strlen("lock.track_license_bundle.track_license") < LMDB_MAX_KEY_SIZE);
189     StringCopyTruncateAndHashIfNecessary(istring, ohash, LMDB_MAX_KEY_SIZE);
190 }
191 #endif
192 
WriteLockData(CF_DB * dbp,const char * lock_id,LockData * lock_data)193 static bool WriteLockData(CF_DB *dbp, const char *lock_id, LockData *lock_data)
194 {
195     bool ret;
196 
197 #ifdef LMDB
198     unsigned char digest2[LMDB_MAX_KEY_SIZE];
199 
200     HashLockKeyIfNecessary(lock_id, digest2);
201 
202     LOG_LOCK_ENTRY(lock_id, digest2, lock_data);
203     ret = WriteDB(dbp, digest2, lock_data, sizeof(LockData));
204     LOG_LOCK_EXIT(lock_id, digest2, lock_data);
205 #else
206     ret = WriteDB(dbp, lock_id, lock_data, sizeof(LockData));
207 #endif
208 
209     return ret;
210 }
211 
WriteLockDataCurrent(CF_DB * dbp,const char * lock_id)212 static bool WriteLockDataCurrent(CF_DB *dbp, const char *lock_id)
213 {
214     LockData lock_data = { 0 };
215     lock_data.pid = getpid();
216     lock_data.time = time(NULL);
217     lock_data.process_start_time = GetProcessStartTime(getpid());
218 
219     return WriteLockData(dbp, lock_id, &lock_data);
220 }
221 
WriteLock(const char * name)222 static int WriteLock(const char *name)
223 {
224     CF_DB *dbp = OpenLock();
225 
226     if (dbp == NULL)
227     {
228         return -1;
229     }
230 
231     ThreadLock(cft_lock);
232     WriteLockDataCurrent(dbp, name);
233 
234     CloseLock(dbp);
235     ThreadUnlock(cft_lock);
236 
237     return 0;
238 }
239 
FindLockTime(const char * name)240 static time_t FindLockTime(const char *name)
241 {
242     bool ret;
243     CF_DB *dbp = OpenLock();
244     if (dbp == NULL)
245     {
246         return -1;
247     }
248 
249     LockData entry = { 0 };
250     entry.process_start_time = PROCESS_START_TIME_UNKNOWN;
251 
252 #ifdef LMDB
253     unsigned char ohash[LMDB_MAX_KEY_SIZE];
254     HashLockKeyIfNecessary(name, ohash);
255 
256     LOG_LOCK_ENTRY(name, ohash, &entry);
257     ret = ReadDB(dbp, ohash, &entry, sizeof(entry));
258     LOG_LOCK_EXIT(name, ohash, &entry);
259 #else
260     ret = ReadDB(dbp, name, &entry, sizeof(entry));
261 #endif
262 
263     if (ret)
264     {
265         CloseLock(dbp);
266         return entry.time;
267     }
268     else
269     {
270         CloseLock(dbp);
271         return -1;
272     }
273 }
274 
RemoveDates(char * s)275 static void RemoveDates(char *s)
276 {
277     int i, a = 0, b = 0, c = 0, d = 0;
278     char *dayp = NULL, *monthp = NULL, *sp;
279     char *days[7] = { "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun" };
280     char *months[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
281                          "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
282 
283 // Canonifies or blanks our times/dates for locks where there would be an explosion of state
284 
285     /* Has s always been generated by something that uses two-digit hh:mm:ss?
286      * Are there any time-zones whose abbreviations are shorter than three
287      * letters?
288      */
289     if (strlen(s) < sizeof("Fri Oct 1 15:15:23 EST 2010") - 1)
290     {
291         // Probably not a full date
292         return;
293     }
294 
295     for (i = 0; i < 7; i++)
296     {
297         if ((dayp = strstr(s, days[i])))
298         {
299             *dayp = 'D';
300             *(dayp + 1) = 'A';
301             *(dayp + 2) = 'Y';
302             break;
303         }
304     }
305 
306     for (i = 0; i < 12; i++)
307     {
308         if ((monthp = strstr(s, months[i])))
309         {
310             *monthp = 'M';
311             *(monthp + 1) = 'O';
312             *(monthp + 2) = 'N';
313             break;
314         }
315     }
316 
317     if (dayp && monthp)         // looks like a full date
318     {
319         sscanf(monthp + 4, "%d %d:%d:%d", &a, &b, &c, &d);
320 
321         if (a * b * c * d == 0)
322         {
323             // Probably not a date
324             return;
325         }
326 
327         for (sp = monthp + 4; *sp != '\0'; sp++)
328         {
329             if (sp > monthp + 15)
330             {
331                 break;
332             }
333 
334             if (isdigit((int)*sp))
335             {
336                 *sp = 't';
337             }
338         }
339     }
340 }
341 
RemoveLock(const char * name)342 static int RemoveLock(const char *name)
343 {
344     CF_DB *dbp = OpenLock();
345     if (dbp == NULL)
346     {
347         return -1;
348     }
349 
350     ThreadLock(cft_lock);
351 #ifdef LMDB
352     unsigned char digest2[LMDB_MAX_KEY_SIZE];
353 
354     HashLockKeyIfNecessary(name, digest2);
355 
356     LOG_LOCK_ENTRY(name, digest2, NULL);
357     DeleteDB(dbp, digest2);
358     LOG_LOCK_EXIT(name, digest2, NULL);
359 #else
360     DeleteDB(dbp, name);
361 #endif
362     ThreadUnlock(cft_lock);
363 
364     CloseLock(dbp);
365     return 0;
366 }
367 
NoOrObsoleteLock(LockData * entry,ARG_UNUSED size_t entry_size,size_t * max_old)368 static bool NoOrObsoleteLock(LockData *entry, ARG_UNUSED size_t entry_size, size_t *max_old)
369 {
370     assert((entry == NULL) || (entry_size == sizeof(LockData)));
371 
372     if (entry == NULL)
373     {
374         return true;
375     }
376 
377     time_t now = time(NULL);
378     if ((now - entry->time) <= (time_t) *max_old)
379     {
380         Log(LOG_LEVEL_DEBUG, "Giving time to process '%d' (holding lock for %ld s)", entry->pid, (now - entry->time));
381     }
382     return ((now - entry->time) > (time_t) *max_old);
383 }
384 
WaitForCriticalSection(const char * section_id)385 void WaitForCriticalSection(const char *section_id)
386 {
387     ThreadLock(cft_lock);
388 
389     CF_DB *dbp = OpenLock();
390     if (dbp == NULL)
391     {
392         Log(LOG_LEVEL_CRIT, "Failed to open lock database when waiting for critical section");
393         ThreadUnlock(cft_lock);
394         return;
395     }
396 
397     time_t started = time(NULL);
398     LockData entry = { 0 };
399     entry.pid = getpid();
400     entry.process_start_time = PROCESS_START_TIME_UNKNOWN;
401 
402 #ifdef LMDB
403     unsigned char ohash[LMDB_MAX_KEY_SIZE];
404     HashLockKeyIfNecessary(section_id, ohash);
405     Log(LOG_LEVEL_DEBUG, "Hashed critical section lock '%s' to '%s'", section_id, ohash);
406     section_id = ohash;
407 #endif
408 
409     /* If another agent has been waiting more than a minute, it means there
410        is likely crash detritus to clear up... After a minute we take our
411        chances ... */
412     size_t max_old = 60;
413 
414     Log(LOG_LEVEL_DEBUG, "Acquiring critical section lock '%s'", section_id);
415     bool got_lock = false;
416     while (!got_lock && ((time(NULL) - started) <= (time_t) max_old))
417     {
418         entry.time = time(NULL);
419         got_lock = OverwriteDB(dbp, section_id, &entry, sizeof(entry),
420                                (OverwriteCondition) NoOrObsoleteLock, &max_old);
421         if (!got_lock)
422         {
423             Log(LOG_LEVEL_DEBUG, "Waiting for critical section lock '%s'", section_id);
424             sleep(1);
425         }
426     }
427 
428     /* If we still haven't gotten the lock, let's try the biggest hammer we
429      * have. */
430     if (!got_lock)
431     {
432         Log(LOG_LEVEL_NOTICE, "Failed to wait for critical section lock '%s', force-writing new lock", section_id);
433         if (!WriteDB(dbp, section_id, &entry, sizeof(entry)))
434         {
435             Log(LOG_LEVEL_CRIT, "Failed to force-write critical section lock '%s'", section_id);
436         }
437     }
438     else
439     {
440         Log(LOG_LEVEL_DEBUG, "Acquired critical section lock '%s'", section_id);
441     }
442 
443     CloseLock(dbp);
444     ThreadUnlock(cft_lock);
445 }
446 
ReleaseCriticalSection(const char * section_id)447 void ReleaseCriticalSection(const char *section_id)
448 {
449     Log(LOG_LEVEL_DEBUG, "Releasing critical section lock '%s'", section_id);
450     if (RemoveLock(section_id) == 0)
451     {
452         Log(LOG_LEVEL_DEBUG, "Released critical section lock '%s'", section_id);
453     }
454     else
455     {
456         Log(LOG_LEVEL_DEBUG, "Failed to release critical section lock '%s'", section_id);
457     }
458 }
459 
FindLock(char * last)460 static time_t FindLock(char *last)
461 {
462     time_t mtime;
463 
464     if ((mtime = FindLockTime(last)) == -1)
465     {
466         /* Do this to prevent deadlock loops from surviving if IfElapsed > T_sched */
467 
468         if (WriteLock(last) == -1)
469         {
470             Log(LOG_LEVEL_ERR, "Unable to lock %s", last);
471             return 0;
472         }
473 
474         return 0;
475     }
476     else
477     {
478         return mtime;
479     }
480 }
481 
LocksCleanup(void)482 static void LocksCleanup(void)
483 {
484     CfLockStack *lock;
485     while ((lock = PopLock()) != NULL)
486     {
487         CfLock best_guess = {
488             .lock = xstrdup(lock->lock),
489             .last = xstrdup(lock->last),
490         };
491         YieldCurrentLock(best_guess);
492         free(lock);
493     }
494 }
495 
RegisterLockCleanup(void)496 static void RegisterLockCleanup(void)
497 {
498     RegisterCleanupFunction(&LocksCleanup);
499 }
500 
501 /**
502  * Return a type template for the promise for lock-type
503  * identification. WARNING: instead of truncation, it does not include any
504  * parts (i.e. constraints or promise type) that don't fit.
505  *
506  * @WARNING currently it only prints up to the 5 first constraints (WHY?)
507  */
PromiseTypeString(char * dst,size_t dst_size,const Promise * pp)508 static void PromiseTypeString(char *dst, size_t dst_size, const Promise *pp)
509 {
510     const char *sp = PromiseGetPromiseType(pp);
511     size_t sp_len  = strlen(sp);
512 
513     dst[0]         = '\0';
514     size_t dst_len = 0;
515 
516     if (sp_len + 1 < dst_size)
517     {
518         strcpy(dst, sp);
519         strcat(dst, ".");
520         dst_len += sp_len + 1;
521     }
522 
523     if (pp->conlist != NULL)
524     {
525         /* Number of constraints (attributes) of that promise. */
526         size_t cons_num = SeqLength(pp->conlist);
527         for (size_t i = 0;  (i < 5) && (i < cons_num);  i++)
528         {
529             Constraint *cp  = SeqAt(pp->conlist, i);
530             const char *con = cp->lval;                    /* the constraint */
531 
532             /* Exception for args (promise type commands),
533                by symmetry, for locking. */
534             if (strcmp(con, "args") == 0)
535             {
536                 continue;
537             }
538 
539             /* Exception for arglist (promise type commands),
540                by symmetry, for locking. */
541             if (strcmp(con, "arglist") == 0)
542             {
543                 continue;
544             }
545 
546             size_t con_len = strlen(con);
547             if (dst_len + con_len + 1 < dst_size)
548             {
549                 strcat(dst, con);
550                 strcat(dst, ".");
551                 dst_len += con_len + 1;
552             }
553         }
554     }
555 }
556 
KillLockHolder(const char * lock)557 static bool KillLockHolder(const char *lock)
558 {
559     bool ret;
560     CF_DB *dbp = OpenLock();
561     if (dbp == NULL)
562     {
563         Log(LOG_LEVEL_ERR, "Unable to open locks database");
564         return false;
565     }
566 
567     LockData lock_data = { 0 };
568     lock_data.process_start_time = PROCESS_START_TIME_UNKNOWN;
569 
570 #ifdef LMDB
571     unsigned char ohash[LMDB_MAX_KEY_SIZE];
572     HashLockKeyIfNecessary(lock, ohash);
573 
574     LOG_LOCK_ENTRY(lock, ohash, &lock_data);
575     ret = ReadDB(dbp, ohash, &lock_data, sizeof(lock_data));
576     LOG_LOCK_EXIT(lock, ohash, &lock_data);
577 #else
578     ret = ReadDB(dbp, lock, &lock_data, sizeof(lock_data));
579 #endif
580 
581     if (!ret)
582     {
583         /* No lock found */
584         CloseLock(dbp);
585         return true;
586     }
587 
588     CloseLock(dbp);
589 
590     if (GracefulTerminate(lock_data.pid, lock_data.process_start_time))
591     {
592         Log(LOG_LEVEL_INFO,
593             "Process with PID %jd successfully killed",
594             (intmax_t) lock_data.pid);
595         return true;
596     }
597     else
598     {
599         if (errno == ESRCH)
600         {
601             Log(LOG_LEVEL_VERBOSE,
602                 "Process with PID %jd has already been killed",
603                 (intmax_t) lock_data.pid);
604             return true;
605         }
606         else
607         {
608             Log(LOG_LEVEL_ERR,
609                 "Failed to kill process with PID: %jd (kill: %s)",
610                 (intmax_t) lock_data.pid, GetErrorStr());
611             return false;
612         }
613     }
614 }
615 
RvalDigestUpdate(EVP_MD_CTX * context,Rlist * rp)616 static void RvalDigestUpdate(EVP_MD_CTX *context, Rlist *rp)
617 {
618     assert(context != NULL);
619     assert(rp != NULL);
620 
621     switch (rp->val.type)
622     {
623     case RVAL_TYPE_SCALAR:
624         EVP_DigestUpdate(context, RlistScalarValue(rp),
625                          strlen(RlistScalarValue(rp)));
626         break;
627 
628     case RVAL_TYPE_FNCALL:
629         // TODO: This should be recursive and not just hash the function name
630         EVP_DigestUpdate(context, RlistFnCallValue(rp)->name,
631                          strlen(RlistFnCallValue(rp)->name));
632         break;
633 
634     default:
635         ProgrammingError("Unhandled case in switch");
636         break;
637     }
638 }
639 
PromiseRuntimeHash(const Promise * pp,const char * salt,unsigned char digest[EVP_MAX_MD_SIZE+1],HashMethod type)640 void PromiseRuntimeHash(const Promise *pp, const char *salt,
641                         unsigned char digest[EVP_MAX_MD_SIZE + 1],
642                         HashMethod type)
643 {
644     static const char PACK_UPIFELAPSED_SALT[] = "packageuplist";
645 
646     int md_len;
647     const EVP_MD *md = NULL;
648     Rlist *rp;
649     FnCall *fp;
650 
651     char *noRvalHash[] = { "mtime", "atime", "ctime", "stime_range", "ttime_range", "log_string", NULL };
652     int doHash;
653 
654     md = HashDigestFromId(type);
655     if (md == NULL)
656     {
657         Log(LOG_LEVEL_ERR,
658             "Could not determine function for file hashing (type=%d)",
659             (int) type);
660         return;
661     }
662 
663     EVP_MD_CTX *context = EVP_MD_CTX_new();
664     if (context == NULL)
665     {
666         Log(LOG_LEVEL_ERR, "Could not allocate openssl hash context");
667         return;
668     }
669 
670     EVP_DigestInit(context, md);
671 
672 // multiple packages (promisers) may share same package_list_update_ifelapsed lock
673     if ( (!salt) || strcmp(salt, PACK_UPIFELAPSED_SALT) )
674     {
675         EVP_DigestUpdate(context, pp->promiser, strlen(pp->promiser));
676     }
677 
678     if (pp->comment)
679     {
680         EVP_DigestUpdate(context, pp->comment, strlen(pp->comment));
681     }
682 
683     if (pp->parent_section && pp->parent_section->parent_bundle)
684     {
685         if (pp->parent_section->parent_bundle->ns)
686         {
687             EVP_DigestUpdate(context,
688                              pp->parent_section->parent_bundle->ns,
689                              strlen(pp->parent_section->parent_bundle->ns));
690         }
691 
692         if (pp->parent_section->parent_bundle->name)
693         {
694             EVP_DigestUpdate(context,
695                              pp->parent_section->parent_bundle->name,
696                              strlen(pp->parent_section->parent_bundle->name));
697         }
698     }
699 
700     // Unused: pp start, end, and line attributes (describing source position).
701 
702     if (salt)
703     {
704         EVP_DigestUpdate(context, salt, strlen(salt));
705     }
706 
707     if (pp->conlist)
708     {
709         for (size_t i = 0; i < SeqLength(pp->conlist); i++)
710         {
711             Constraint *cp = SeqAt(pp->conlist, i);
712 
713             EVP_DigestUpdate(context, cp->lval, strlen(cp->lval));
714 
715             // don't hash rvals that change (e.g. times)
716             doHash = true;
717 
718             for (int j = 0; noRvalHash[j] != NULL; j++)
719             {
720                 if (strcmp(cp->lval, noRvalHash[j]) == 0)
721                 {
722                     doHash = false;
723                     break;
724                 }
725             }
726 
727             if (!doHash)
728             {
729                 continue;
730             }
731 
732             switch (cp->rval.type)
733             {
734             case RVAL_TYPE_SCALAR:
735                 EVP_DigestUpdate(context, cp->rval.item, strlen(cp->rval.item));
736                 break;
737 
738             case RVAL_TYPE_LIST:
739                 for (rp = cp->rval.item; rp != NULL; rp = rp->next)
740                 {
741                     RvalDigestUpdate(context, rp);
742                 }
743                 break;
744 
745             case RVAL_TYPE_FNCALL:
746 
747                 /* Body or bundle */
748 
749                 fp = (FnCall *) cp->rval.item;
750 
751                 EVP_DigestUpdate(context, fp->name, strlen(fp->name));
752 
753                 for (rp = fp->args; rp != NULL; rp = rp->next)
754                 {
755                     RvalDigestUpdate(context, rp);
756                 }
757                 break;
758 
759             default:
760                 break;
761             }
762         }
763     }
764 
765     EVP_DigestFinal(context, digest, &md_len);
766     EVP_MD_CTX_free(context);
767 
768 /* Digest length stored in md_len */
769 }
770 
CfLockNew(const char * last,const char * lock,bool is_dummy)771 static CfLock CfLockNew(const char *last, const char *lock, bool is_dummy)
772 {
773     return (CfLock) {
774         .last = last ? xstrdup(last) : NULL,
775         .lock = lock ? xstrdup(lock) : NULL,
776         .is_dummy = is_dummy
777     };
778 }
779 
CfLockNull(void)780 static CfLock CfLockNull(void)
781 {
782     return (CfLock) {
783         .last = NULL,
784         .lock = NULL,
785         .is_dummy = false
786     };
787 }
788 
AcquireLock(EvalContext * ctx,const char * operand,const char * host,time_t now,int ifelapsed,int expireafter,const Promise * pp,bool ignoreProcesses)789 CfLock AcquireLock(EvalContext *ctx, const char *operand, const char *host,
790                    time_t now, int ifelapsed, int expireafter, const Promise *pp,
791                    bool ignoreProcesses)
792 {
793     if (now == 0)
794     {
795         return CfLockNull();
796     }
797 
798     char str_digest[CF_HOSTKEY_STRING_SIZE];
799     {
800         unsigned char digest[EVP_MAX_MD_SIZE + 1];
801         PromiseRuntimeHash(pp, operand, digest, CF_DEFAULT_DIGEST);
802         HashPrintSafe(str_digest, sizeof(str_digest), digest,
803                       CF_DEFAULT_DIGEST, true);
804     }
805 
806     if (EvalContextPromiseLockCacheContains(ctx, str_digest))
807     {
808 //        Log(LOG_LEVEL_DEBUG, "This promise has already been verified");
809         return CfLockNull();
810     }
811 
812     EvalContextPromiseLockCachePut(ctx, str_digest);
813 
814     // Finally if we're supposed to ignore locks ... do the remaining stuff
815     if (EvalContextIsIgnoringLocks(ctx))
816     {
817         return CfLockNew(NULL, "dummy", true);
818     }
819 
820     char cc_operator[CF_MAXVARSIZE];
821     {
822         char promise[CF_MAXVARSIZE - CF_BUFFERMARGIN];
823         PromiseTypeString(promise, sizeof(promise), pp);
824         snprintf(cc_operator, sizeof(cc_operator), "%s-%s", promise, host);
825     }
826 
827     char cc_operand[CF_BUFSIZE];
828     strlcpy(cc_operand, operand, CF_BUFSIZE);
829     CanonifyNameInPlace(cc_operand);
830     RemoveDates(cc_operand);
831 
832 
833     Log(LOG_LEVEL_DEBUG,
834         "AcquireLock(%s,%s), ExpireAfter = %d, IfElapsed = %d",
835         cc_operator, cc_operand, expireafter, ifelapsed);
836 
837     int sum = 0;
838     for (int i = 0; cc_operator[i] != '\0'; i++)
839     {
840         sum = (CF_MACROALPHABET * sum + cc_operator[i]) % CF_MAXLOCKNUM;
841     }
842 
843     for (int i = 0; cc_operand[i] != '\0'; i++)
844     {
845         sum = (CF_MACROALPHABET * sum + cc_operand[i]) % CF_MAXLOCKNUM;
846     }
847 
848     const char *bundle_name = PromiseGetBundle(pp)->name;
849 
850     char cflock[CF_BUFSIZE] = "";
851     snprintf(cflock, CF_BUFSIZE, "lock.%.100s.%s.%.100s_%d_%s",
852              bundle_name, cc_operator, cc_operand, sum, str_digest);
853 
854     char cflast[CF_BUFSIZE] = "";
855     snprintf(cflast, CF_BUFSIZE, "last.%.100s.%s.%.100s_%d_%s",
856              bundle_name, cc_operator, cc_operand, sum, str_digest);
857 
858     Log(LOG_LEVEL_DEBUG, "Locking bundle '%s' with lock '%s'",
859         bundle_name, cflock);
860 
861     // Now see if we can get exclusivity to edit the locks
862     WaitForCriticalSection(CF_CRITIAL_SECTION);
863 
864     // Look for non-existent (old) processes
865     time_t lastcompleted = FindLock(cflast);
866     time_t elapsedtime = (time_t) (now - lastcompleted) / 60;
867 
868     // For promises/locks with ifelapsed == 0, skip all detection logic of
869     // previously acquired locks, whether in this agent or a parallel one.
870     if (ifelapsed != 0)
871     {
872         if (elapsedtime < 0)
873         {
874             Log(LOG_LEVEL_VERBOSE,
875                 "Another cf-agent seems to have done this since I started "
876                 "(elapsed=%jd)",
877                 (intmax_t) elapsedtime);
878             ReleaseCriticalSection(CF_CRITIAL_SECTION);
879             return CfLockNull();
880         }
881 
882         if (elapsedtime < ifelapsed)
883         {
884             Log(LOG_LEVEL_VERBOSE,
885                 "Nothing promised here [%.40s] (%jd/%u minutes elapsed)",
886                 cflast, (intmax_t) elapsedtime, ifelapsed);
887             ReleaseCriticalSection(CF_CRITIAL_SECTION);
888             return CfLockNull();
889         }
890     }
891 
892     // Look for existing (current) processes
893     lastcompleted = FindLock(cflock);
894     if (!ignoreProcesses)
895     {
896         elapsedtime = (time_t) (now - lastcompleted) / 60;
897 
898         if (lastcompleted != 0)
899         {
900             if (elapsedtime >= expireafter)
901             {
902                 Log(LOG_LEVEL_INFO,
903                     "Lock expired after %jd/%u minutes: %s",
904                     (intmax_t) elapsedtime, expireafter, cflock);
905 
906                 if (KillLockHolder(cflock))
907                 {
908                     Log(LOG_LEVEL_VERBOSE,
909                         "Lock successfully expired: %s", cflock);
910                     unlink(cflock);
911                 }
912                 else
913                 {
914                     Log(LOG_LEVEL_ERR, "Failed to expire lock: %s", cflock);
915                 }
916             }
917             else
918             {
919                 ReleaseCriticalSection(CF_CRITIAL_SECTION);
920                 Log(LOG_LEVEL_VERBOSE,
921                     "Couldn't obtain lock for %s (already running!)",
922                     cflock);
923                 return CfLockNull();
924             }
925         }
926 
927         int ret = WriteLock(cflock);
928         if (ret != -1)
929         {
930             /* Register a cleanup handler *after* having opened the DB, so that
931              * CloseAllDB() atexit() handler is registered in advance, and it
932              * is called after removing this lock.
933 
934              * There is a small race condition here that we'll leave a stale
935              * lock if we exit before the following line. */
936             pthread_once(&lock_cleanup_once, &RegisterLockCleanup);
937         }
938     }
939 
940     ReleaseCriticalSection(CF_CRITIAL_SECTION);
941 
942     // Keep this as a global for signal handling
943     PushLock(cflock, cflast);
944 
945     return CfLockNew(cflast, cflock, false);
946 }
947 
YieldCurrentLock(CfLock lock)948 void YieldCurrentLock(CfLock lock)
949 {
950     if (lock.is_dummy)
951     {
952         free(lock.lock);        /* allocated in AquireLock as a special case */
953         return;
954     }
955 
956     if (lock.lock == (char *) CF_UNDEFINED)
957     {
958         return;
959     }
960 
961     Log(LOG_LEVEL_DEBUG, "Yielding lock '%s'", lock.lock);
962 
963     if (RemoveLock(lock.lock) == -1)
964     {
965         Log(LOG_LEVEL_VERBOSE, "Unable to remove lock %s", lock.lock);
966         free(lock.last);
967         free(lock.lock);
968         return;
969     }
970 
971     if (WriteLock(lock.last) == -1)
972     {
973         Log(LOG_LEVEL_ERR, "Unable to create '%s'. (create: %s)",
974             lock.last, GetErrorStr());
975         free(lock.last);
976         free(lock.lock);
977         return;
978     }
979 
980     /* This lock has ben yield'ed, don't try to yield it again in case process
981      * is terminated abnormally.
982      */
983     CfLockStack *stack = LOCK_STACK;
984     CfLockStack *last = NULL;
985     while (stack)
986     {
987         if ((strcmp(stack->lock, lock.lock) == 0)
988          && (strcmp(stack->last, lock.last) == 0))
989         {
990             CfLockStack *delete_me = stack;
991             stack = stack->previous;
992             if (!last)
993             {
994                 assert(delete_me == LOCK_STACK);
995                 LOCK_STACK = stack;
996             } else {
997                 last->previous = stack;
998             }
999             free(delete_me);
1000             continue;
1001         }
1002         last = stack;
1003         stack = stack->previous;
1004     }
1005 
1006     free(lock.last);
1007     free(lock.lock);
1008 }
1009 
YieldCurrentLockAndRemoveFromCache(EvalContext * ctx,CfLock lock,const char * operand,const Promise * pp)1010 void YieldCurrentLockAndRemoveFromCache(EvalContext *ctx, CfLock lock,
1011                                         const char *operand, const Promise *pp)
1012 {
1013     unsigned char digest[EVP_MAX_MD_SIZE + 1];
1014     PromiseRuntimeHash(pp, operand, digest, CF_DEFAULT_DIGEST);
1015     char str_digest[CF_HOSTKEY_STRING_SIZE];
1016     HashPrintSafe(str_digest, sizeof(str_digest), digest,
1017                   CF_DEFAULT_DIGEST, true);
1018 
1019     YieldCurrentLock(lock);
1020     EvalContextPromiseLockCacheRemove(ctx, str_digest);
1021 }
1022 
1023 
GetLockName(char * lockname,const char * locktype,const char * base,const Rlist * params)1024 void GetLockName(char *lockname, const char *locktype,
1025                  const char *base, const Rlist *params)
1026 {
1027     int max_sample, count = 0;
1028 
1029     for (const Rlist *rp = params; rp != NULL; rp = rp->next)
1030     {
1031         count++;
1032     }
1033 
1034     if (count)
1035     {
1036         max_sample = CF_BUFSIZE / (2 * count);
1037     }
1038     else
1039     {
1040         max_sample = 0;
1041     }
1042 
1043     strlcpy(lockname, locktype, CF_BUFSIZE / 10);
1044     strlcat(lockname, "_", CF_BUFSIZE / 10);
1045     strlcat(lockname, base, CF_BUFSIZE / 10);
1046     strlcat(lockname, "_", CF_BUFSIZE / 10);
1047 
1048     for (const Rlist *rp = params; rp != NULL; rp = rp->next)
1049     {
1050         switch (rp->val.type)
1051         {
1052         case RVAL_TYPE_SCALAR:
1053             strncat(lockname, RlistScalarValue(rp), max_sample);
1054             break;
1055 
1056         case RVAL_TYPE_FNCALL:
1057             strncat(lockname, RlistFnCallValue(rp)->name, max_sample);
1058             break;
1059 
1060         default:
1061             ProgrammingError("Unhandled case in switch %d", rp->val.type);
1062             break;
1063         }
1064     }
1065 }
1066 
RestoreLockDatabase(void)1067 static void RestoreLockDatabase(void)
1068 {
1069     // We don't do any locking here (since we can't trust the database), but
1070     // this should be right after bootup, so we should be the only one.
1071     // Worst case someone else will just copy the same file to the same
1072     // location.
1073     char *db_path = DBIdToPath(dbid_locks);
1074     char *db_path_backup;
1075     xasprintf(&db_path_backup, "%s.backup", db_path);
1076 
1077     CopyLockDatabaseAtomically(db_path_backup, db_path, "lock database backup",
1078                                "lock database");
1079 
1080     free(db_path);
1081     free(db_path_backup);
1082 }
1083 
CopyLockDatabaseAtomically(const char * from,const char * to,const char * from_pretty_name,const char * to_pretty_name)1084 static void CopyLockDatabaseAtomically(const char *from, const char *to,
1085                                        const char *from_pretty_name,
1086                                        const char *to_pretty_name)
1087 {
1088     char *tmp_file_name;
1089     xasprintf(&tmp_file_name, "%s.tmp", to);
1090 
1091     int from_fd = open(from, O_RDONLY | O_BINARY);
1092     if (from_fd < 0)
1093     {
1094         Log(LOG_LEVEL_WARNING,
1095             "Could not open '%s' (open: %s)",
1096             from_pretty_name, GetErrorStr());
1097         goto cleanup;
1098     }
1099 
1100     int to_fd = open(tmp_file_name, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0600);
1101     if (to_fd < 0)
1102     {
1103         Log(LOG_LEVEL_WARNING,
1104             "Could not open '%s' temporary file (open: %s)",
1105             to_pretty_name, GetErrorStr());
1106         goto cleanup;
1107     }
1108 
1109     size_t total_bytes_written;
1110     bool   last_write_was_hole;
1111     bool ok1 = FileSparseCopy(from_fd, from_pretty_name,
1112                               to_fd,   to_pretty_name, DEV_BSIZE,
1113                               &total_bytes_written, &last_write_was_hole);
1114 
1115     /* Make sure changes are persistent on disk, so database cannot get
1116      * corrupted at system crash. */
1117     bool do_sync = true;
1118     bool ok2 = FileSparseClose(to_fd, to_pretty_name, do_sync,
1119                                total_bytes_written, last_write_was_hole);
1120 
1121     if (!ok1 || !ok2)
1122     {
1123         Log(LOG_LEVEL_WARNING,
1124             "Error while moving database from '%s' to '%s'",
1125             from_pretty_name, to_pretty_name);
1126     }
1127 
1128     if (rename(tmp_file_name, to) != 0)
1129     {
1130         Log(LOG_LEVEL_WARNING,
1131             "Could not move '%s' into place (rename: %s)",
1132             to_pretty_name, GetErrorStr());
1133     }
1134 
1135   cleanup:
1136     if (from_fd != -1)
1137     {
1138         close(from_fd);
1139     }
1140     unlink(tmp_file_name);
1141     free(tmp_file_name);
1142 }
1143 
BackupLockDatabase(void)1144 void BackupLockDatabase(void)
1145 {
1146     WaitForCriticalSection(CF_CRITIAL_SECTION);
1147 
1148     char *db_path = DBIdToPath(dbid_locks);
1149     char *db_path_backup;
1150     xasprintf(&db_path_backup, "%s.backup", db_path);
1151 
1152     CopyLockDatabaseAtomically(db_path, db_path_backup, "lock database",
1153                                "lock database backup");
1154 
1155     free(db_path);
1156     free(db_path_backup);
1157 
1158     ReleaseCriticalSection(CF_CRITIAL_SECTION);
1159 }
1160 
PurgeLocks(void)1161 void PurgeLocks(void)
1162 {
1163     CF_DBC *dbcp;
1164     char *key;
1165     int ksize, vsize;
1166     LockData lock_horizon;
1167     LockData *entry = NULL;
1168     time_t now = time(NULL);
1169 
1170     CF_DB *dbp = OpenLock();
1171     if (dbp == NULL)
1172     {
1173         return;
1174     }
1175 
1176     memset(&lock_horizon, 0, sizeof(lock_horizon));
1177 
1178     if (ReadDB(dbp, "lock_horizon", &lock_horizon, sizeof(lock_horizon)))
1179     {
1180         if (now - lock_horizon.time < SECONDS_PER_WEEK * 4)
1181         {
1182             Log(LOG_LEVEL_VERBOSE, "No lock purging scheduled");
1183             CloseLock(dbp);
1184             return;
1185         }
1186     }
1187 
1188     Log(LOG_LEVEL_VERBOSE, "Looking for stale locks to purge");
1189 
1190     if (!NewDBCursor(dbp, &dbcp))
1191     {
1192         char *db_path = DBIdToPath(dbid_locks);
1193         Log(LOG_LEVEL_ERR, "Unable to get cursor for locks database '%s'",
1194             db_path);
1195         free(db_path);
1196         CloseLock(dbp);
1197         return;
1198     }
1199 
1200     while (NextDB(dbcp, &key, &ksize, (void **)&entry, &vsize))
1201     {
1202 #ifdef LMDB
1203         LOG_LOCK_OP("<unknown>", key, entry);
1204 #endif
1205         if (STARTSWITH(key, "last.internal_bundle.track_license.handle"))
1206         {
1207             continue;
1208         }
1209 
1210         if (now - entry->time > (time_t) CF_LOCKHORIZON)
1211         {
1212             Log(LOG_LEVEL_VERBOSE, "Purging lock (%jd s elapsed): %s",
1213                 (intmax_t) (now - entry->time), key);
1214             DBCursorDeleteEntry(dbcp);
1215         }
1216     }
1217 
1218     Log(LOG_LEVEL_DEBUG, "Finished purging locks");
1219 
1220     lock_horizon.time = now;
1221     DeleteDBCursor(dbcp);
1222 
1223     WriteDB(dbp, "lock_horizon", &lock_horizon, sizeof(lock_horizon));
1224     CloseLock(dbp);
1225 }
1226