Make ccache init atomic

This commit is contained in:
Nicolas Williams
2019-09-06 18:15:20 -05:00
parent ec84667763
commit e163bfd81b

View File

@@ -37,6 +37,7 @@
typedef struct krb5_fcache{ typedef struct krb5_fcache{
char *filename; char *filename;
char *tmpfn;
int version; int version;
}krb5_fcache; }krb5_fcache;
@@ -57,6 +58,7 @@ struct fcc_cursor {
#define FCACHE(X) ((krb5_fcache*)(X)->data.data) #define FCACHE(X) ((krb5_fcache*)(X)->data.data)
#define FILENAME(X) (FCACHE(X)->filename) #define FILENAME(X) (FCACHE(X)->filename)
#define TMPFILENAME(X) (FCACHE(X)->tmpfn)
#define FCC_CURSOR(C) ((struct fcc_cursor*)(C)) #define FCC_CURSOR(C) ((struct fcc_cursor*)(C))
@@ -185,12 +187,13 @@ static krb5_error_code KRB5_CALLCONV
fcc_resolve(krb5_context context, krb5_ccache *id, const char *res) fcc_resolve(krb5_context context, krb5_ccache *id, const char *res)
{ {
krb5_fcache *f; krb5_fcache *f;
f = malloc(sizeof(*f)); f = calloc(1, sizeof(*f));
if(f == NULL) { if(f == NULL) {
krb5_set_error_message(context, KRB5_CC_NOMEM, krb5_set_error_message(context, KRB5_CC_NOMEM,
N_("malloc: out of memory", "")); N_("malloc: out of memory", ""));
return KRB5_CC_NOMEM; return KRB5_CC_NOMEM;
} }
f->tmpfn = NULL;
f->filename = strdup(res); f->filename = strdup(res);
if(f->filename == NULL){ if(f->filename == NULL){
free(f); free(f);
@@ -316,12 +319,13 @@ fcc_gen_new(krb5_context context, krb5_ccache *id)
krb5_fcache *f; krb5_fcache *f;
int fd; int fd;
f = malloc(sizeof(*f)); f = calloc(1, sizeof(*f));
if(f == NULL) { if(f == NULL) {
krb5_set_error_message(context, KRB5_CC_NOMEM, krb5_set_error_message(context, KRB5_CC_NOMEM,
N_("malloc: out of memory", "")); N_("malloc: out of memory", ""));
return KRB5_CC_NOMEM; return KRB5_CC_NOMEM;
} }
f->tmpfn = NULL;
ret = asprintf(&file, "%sXXXXXX", KRB5_DEFAULT_CCFILE_ROOT); ret = asprintf(&file, "%sXXXXXX", KRB5_DEFAULT_CCFILE_ROOT);
if(ret < 0 || file == NULL) { if(ret < 0 || file == NULL) {
free(f); free(f);
@@ -338,7 +342,7 @@ fcc_gen_new(krb5_context context, krb5_ccache *id)
file = exp_file; file = exp_file;
fd = mkstemp(exp_file); fd = mkostemp(exp_file, O_CLOEXEC);
if(fd < 0) { if(fd < 0) {
ret = (krb5_error_code)errno; ret = (krb5_error_code)errno;
krb5_set_error_message(context, ret, N_("mkstemp %s failed", ""), exp_file); krb5_set_error_message(context, ret, N_("mkstemp %s failed", ""), exp_file);
@@ -406,8 +410,22 @@ fcc_open(krb5_context context,
if (FCACHE(id) == NULL) if (FCACHE(id) == NULL)
return krb5_einval(context, 2); return krb5_einval(context, 2);
filename = FILENAME(id); if ((flags & O_EXCL)) {
flags &= ~O_EXCL;
if (asprintf(&TMPFILENAME(id), "%s-XXXXXX", FILENAME(id)) < 0 ||
TMPFILENAME(id) == NULL)
return krb5_enomem(context);
if ((*fd_ret = mkostemp(TMPFILENAME(id), O_CLOEXEC)) == -1) {
free(TMPFILENAME(id));
TMPFILENAME(id) = NULL;
krb5_set_error_message(context, ret = errno,
N_("Refuses to open symlinks for caches FILE:%s", ""),
FILENAME(id));
return ret;
}
}
filename = TMPFILENAME(id) ? TMPFILENAME(id) : FILENAME(id);
strict_checking = (flags & O_CREAT) == 0 && strict_checking = (flags & O_CREAT) == 0 &&
(context->flags & KRB5_CTX_F_FCACHE_STRICT_CHECKING) != 0; (context->flags & KRB5_CTX_F_FCACHE_STRICT_CHECKING) != 0;
@@ -537,8 +555,10 @@ fcc_initialize(krb5_context context,
if (f == NULL) if (f == NULL)
return krb5_einval(context, 2); return krb5_einval(context, 2);
unlink (f->filename); /*
* fcc_open() will notice the O_EXCL and will make a temporary file that
* will later be renamed into place.
*/
ret = fcc_open(context, id, "initialize", &fd, O_RDWR | O_CREAT | O_EXCL, 0600); ret = fcc_open(context, id, "initialize", &fd, O_RDWR | O_CREAT | O_EXCL, 0600);
if(ret) if(ret)
return ret; return ret;
@@ -601,7 +621,10 @@ fcc_close(krb5_context context,
if (FCACHE(id) == NULL) if (FCACHE(id) == NULL)
return krb5_einval(context, 2); return krb5_einval(context, 2);
free (FILENAME(id)); if (TMPFILENAME(id))
(void) unlink(TMPFILENAME(id));
free(TMPFILENAME(id));
free(FILENAME(id));
krb5_data_free(&id->data); krb5_data_free(&id->data);
return 0; return 0;
} }
@@ -613,6 +636,11 @@ fcc_destroy(krb5_context context,
if (FCACHE(id) == NULL) if (FCACHE(id) == NULL)
return krb5_einval(context, 2); return krb5_einval(context, 2);
if (TMPFILENAME(id)) {
(void) _krb5_erase_file(context, TMPFILENAME(id));
free(TMPFILENAME(id));
TMPFILENAME(id) = NULL;
}
return _krb5_erase_file(context, FILENAME(id)); return _krb5_erase_file(context, FILENAME(id));
} }
@@ -649,6 +677,21 @@ fcc_store_cred(krb5_context context,
FILENAME(id), buf); FILENAME(id), buf);
} }
} }
if (ret == 0 && TMPFILENAME(id) &&
!krb5_is_config_principal(context, creds->server)) {
/*
* Portability note: there's no need to have WIN32 or other code here
* for odd rename cases because rk_rename() is meant to handle that.
*/
ret = rk_rename(TMPFILENAME(id), FILENAME(id));
if (ret == 0) {
free(TMPFILENAME(id));
TMPFILENAME(id) = NULL;
} else {
ret = errno;
}
}
return ret; return ret;
} }
@@ -840,12 +883,11 @@ fcc_get_first (krb5_context context,
if (FCACHE(id) == NULL) if (FCACHE(id) == NULL)
return krb5_einval(context, 2); return krb5_einval(context, 2);
*cursor = malloc(sizeof(struct fcc_cursor)); *cursor = calloc(1, sizeof(struct fcc_cursor));
if (*cursor == NULL) { if (*cursor == NULL) {
krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", "")); krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
return ENOMEM; return ENOMEM;
} }
memset(*cursor, 0, sizeof(struct fcc_cursor));
ret = init_fcc(context, id, "get-first", &FCC_CURSOR(*cursor)->sp, ret = init_fcc(context, id, "get-first", &FCC_CURSOR(*cursor)->sp,
&FCC_CURSOR(*cursor)->fd, NULL); &FCC_CURSOR(*cursor)->fd, NULL);