Re: [PATCH] CIFS.upcall to accomodate new namespace mount opt

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Ritvik,

LGTM

Reviewed-by: Ronnie Sahlberg <ronniesahlberg@xxxxxxxxx>

On Tue, 19 Nov 2024 at 16:26, Ritvik Budhiraja
<budhirajaritviksmb@xxxxxxxxx> wrote:
>
> Attaching the previously sent patch (on which the current is dependent)
> adding +ronniesahlberg@xxxxxxxxx
>
> ------------------------
>
> [PATCH] CIFS: New mount option for cifs.upcall namespace resolution
> From: Ritvik Budhiraja <rbudhiraja@xxxxxxxxxxxxx>
>
> In the current implementation, the SMB filesystem on a mount point can
> trigger upcalls from the kernel to the userspace to enable certain
> functionalities like spnego, dns_resolution, amongst others. These upcalls
> usually either happen in the context of the mount or in the context of an
> application/user. The upcall handler for cifs, cifs.upcall already has
> existing code which switches the namespaces to the caller's namespace
> before handling the upcall. This behaviour is expected for scenarios like
> multiuser mounts, but might not cover all single user scenario with
> services such as Kubernetes, where the mount can happen from different
> locations such as on the host, from an app container, or a driver pod
> which does the mount on behalf of a different pod.
>
> This patch introduces a new mount option called upcall_target, to
> customise the upcall behaviour. upcall_target can take 'mount' and 'app'
> as possible values. This aids use cases like Kubernetes where the mount
> happens on behalf of the application in another container altogether.
> Having this new mount option allows the mount command to specify where the
> upcall should happen: 'mount' for resolving the upcall to the host
> namespace, and 'app' for resolving the upcall to the ns of the calling
> thread. This will enable both the scenarios where the Kerberos credentials
> can be found on the application namespace or the host namespace to which
> just the mount operation is "delegated".
>
> Reviewed-by: Shyam Prasad <shyam.prasad@xxxxxxxxxxxxx>
> Reviewed-by: Bharath S M <bharathsm@xxxxxxxxxxxxx>
> Signed-off-by: Ritvik Budhiraja <rbudhiraja@xxxxxxxxxxxxx>
> ---
>  fs/smb/client/cifs_spnego.c | 16 +++++++++++++++
>  fs/smb/client/cifsfs.c      | 25 ++++++++++++++++++++++++
>  fs/smb/client/cifsglob.h    |  7 +++++++
>  fs/smb/client/connect.c     | 20 +++++++++++++++++++
>  fs/smb/client/fs_context.c  | 39 +++++++++++++++++++++++++++++++++++++
>  fs/smb/client/fs_context.h  | 10 ++++++++++
>  6 files changed, 117 insertions(+)
>
> diff --git a/fs/smb/client/cifs_spnego.c b/fs/smb/client/cifs_spnego.c
> index af7849e59..28f568b5f 100644
> --- a/fs/smb/client/cifs_spnego.c
> +++ b/fs/smb/client/cifs_spnego.c
> @@ -82,6 +82,9 @@ struct key_type cifs_spnego_key_type = {
>  /* strlen of ";pid=0x" */
>  #define PID_KEY_LEN            7
>
> +/* strlen of ";upcall_target=" */
> +#define UPCALL_TARGET_KEY_LEN  15
> +
>  /* get a key struct with a SPNEGO security blob, suitable for session setup */
>  struct key *
>  cifs_get_spnego_key(struct cifs_ses *sesInfo,
> @@ -108,6 +111,11 @@ cifs_get_spnego_key(struct cifs_ses *sesInfo,
>         if (sesInfo->user_name)
>                 desc_len += USER_KEY_LEN + strlen(sesInfo->user_name);
>
> +       if (sesInfo->upcall_target == UPTARGET_MOUNT)
> +               desc_len += UPCALL_TARGET_KEY_LEN + 5; // strlen("mount")
> +       else
> +               desc_len += UPCALL_TARGET_KEY_LEN + 3; // strlen("app")
> +
>         spnego_key = ERR_PTR(-ENOMEM);
>         description = kzalloc(desc_len, GFP_KERNEL);
>         if (description == NULL)
> @@ -156,6 +164,14 @@ cifs_get_spnego_key(struct cifs_ses *sesInfo,
>         dp = description + strlen(description);
>         sprintf(dp, ";pid=0x%x", current->pid);
>
> +       if (sesInfo->upcall_target == UPTARGET_MOUNT) {
> +               dp = description + strlen(description);
> +               sprintf(dp, ";upcall_target=mount");
> +       } else {
> +               dp = description + strlen(description);
> +               sprintf(dp, ";upcall_target=app");
> +       }
> +
>         cifs_dbg(FYI, "key description = %s\n", description);
>         saved_cred = override_creds(spnego_cred);
>         spnego_key = request_key(&cifs_spnego_key_type, description, "");
> diff --git a/fs/smb/client/cifsfs.c b/fs/smb/client/cifsfs.c
> index 2a2523c93..ac89bd199 100644
> --- a/fs/smb/client/cifsfs.c
> +++ b/fs/smb/client/cifsfs.c
> @@ -535,6 +535,30 @@ static int cifs_show_devname(struct seq_file *m, struct dentry *root)
>         return 0;
>  }
>
> +static void
> +cifs_show_upcall_target(struct seq_file *s, struct cifs_sb_info *cifs_sb)
> +{
> +       if (cifs_sb->ctx->upcall_target == UPTARGET_UNSPECIFIED) {
> +               seq_puts(s, ",upcall_target=app");
> +               return;
> +       }
> +
> +       seq_puts(s, ",upcall_target=");
> +
> +       switch (cifs_sb->ctx->upcall_target) {
> +       case UPTARGET_APP:
> +               seq_puts(s, "app");
> +               break;
> +       case UPTARGET_MOUNT:
> +               seq_puts(s, "mount");
> +               break;
> +       default:
> +               /* shouldn't ever happen */
> +               seq_puts(s, "unknown");
> +               break;
> +       }
> +}
> +
>  /*
>   * cifs_show_options() is for displaying mount options in /proc/mounts.
>   * Not all settable options are displayed but most of the important
> @@ -551,6 +575,7 @@ cifs_show_options(struct seq_file *s, struct dentry *root)
>         seq_show_option(s, "vers", tcon->ses->server->vals->version_string);
>         cifs_show_security(s, tcon->ses);
>         cifs_show_cache_flavor(s, cifs_sb);
> +       cifs_show_upcall_target(s, cifs_sb);
>
>         if (tcon->no_lease)
>                 seq_puts(s, ",nolease");
> diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h
> index 9eae8649f..7878d1197 100644
> --- a/fs/smb/client/cifsglob.h
> +++ b/fs/smb/client/cifsglob.h
> @@ -153,6 +153,12 @@ enum securityEnum {
>         Kerberos,               /* Kerberos via SPNEGO */
>  };
>
> +enum upcall_target_enum {
> +       UPTARGET_UNSPECIFIED, /* not specified, defaults to app */
> +       UPTARGET_MOUNT, /* upcall to the mount namespace */
> +       UPTARGET_APP, /* upcall to the application namespace which did the mount */
> +};
> +
>  enum cifs_reparse_type {
>         CIFS_REPARSE_TYPE_NFS,
>         CIFS_REPARSE_TYPE_WSL,
> @@ -1083,6 +1089,7 @@ struct cifs_ses {
>         struct session_key auth_key;
>         struct ntlmssp_auth *ntlmssp; /* ciphertext, flags, server challenge */
>         enum securityEnum sectype; /* what security flavor was specified? */
> +       enum upcall_target_enum upcall_target; /* what upcall target was specified? */
>         bool sign;              /* is signing required? */
>         bool domainAuto:1;
>         bool expired_pwd;  /* track if access denied or expired pwd so can know if need to update */
> diff --git a/fs/smb/client/connect.c b/fs/smb/client/connect.c
> index 5375b0c1d..57766bca0 100644
> --- a/fs/smb/client/connect.c
> +++ b/fs/smb/client/connect.c
> @@ -2352,6 +2352,26 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx)
>
>         ses->sectype = ctx->sectype;
>         ses->sign = ctx->sign;
> +
> +       /*
> +        *Explicitly marking upcall_target mount option for easier handling
> +        * by cifs_spnego.c and eventually cifs.upcall.c
> +        */
> +
> +       switch (ctx->upcall_target) {
> +       case UPTARGET_UNSPECIFIED: /* default to app */
> +       case UPTARGET_APP:
> +               ses->upcall_target = UPTARGET_APP;
> +               break;
> +       case UPTARGET_MOUNT:
> +               ses->upcall_target = UPTARGET_MOUNT;
> +               break;
> +       default:
> +               // should never happen
> +               ses->upcall_target = UPTARGET_APP;
> +               break;
> +       }
> +
>         ses->local_nls = load_nls(ctx->local_nls->charset);
>
>         /* add server as first channel */
> diff --git a/fs/smb/client/fs_context.c b/fs/smb/client/fs_context.c
> index bc926ab25..2bae49507 100644
> --- a/fs/smb/client/fs_context.c
> +++ b/fs/smb/client/fs_context.c
> @@ -67,6 +67,12 @@ static const match_table_t cifs_secflavor_tokens = {
>         { Opt_sec_err, NULL }
>  };
>
> +static const match_table_t cifs_upcall_target = {
> +       { Opt_upcall_target_mount, "mount" },
> +       { Opt_upcall_target_application, "app" },
> +       { Opt_upcall_target_err, NULL }
> +};
> +
>  const struct fs_parameter_spec smb3_fs_parameters[] = {
>         /* Mount options that take no arguments */
>         fsparam_flag_no("user_xattr", Opt_user_xattr),
> @@ -178,6 +184,7 @@ const struct fs_parameter_spec smb3_fs_parameters[] = {
>         fsparam_string("sec", Opt_sec),
>         fsparam_string("cache", Opt_cache),
>         fsparam_string("reparse", Opt_reparse),
> +       fsparam_string("upcall_target", Opt_upcalltarget),
>
>         /* Arguments that should be ignored */
>         fsparam_flag("guest", Opt_ignore),
> @@ -248,6 +255,29 @@ cifs_parse_security_flavors(struct fs_context *fc, char *value, struct smb3_fs_c
>         return 0;
>  }
>
> +static int
> +cifs_parse_upcall_target(struct fs_context *fc, char *value, struct smb3_fs_context *ctx)
> +{
> +       substring_t args[MAX_OPT_ARGS];
> +
> +       ctx->upcall_target = UPTARGET_UNSPECIFIED;
> +
> +       switch (match_token(value, cifs_upcall_target, args)) {
> +       case Opt_upcall_target_mount:
> +               ctx->upcall_target = UPTARGET_MOUNT;
> +               break;
> +       case Opt_upcall_target_application:
> +               ctx->upcall_target = UPTARGET_APP;
> +               break;
> +
> +       default:
> +               cifs_errorf(fc, "bad upcall target: %s\n", value);
> +               return 1;
> +       }
> +
> +       return 0;
> +}
> +
>  static const match_table_t cifs_cacheflavor_tokens = {
>         { Opt_cache_loose, "loose" },
>         { Opt_cache_strict, "strict" },
> @@ -1440,6 +1470,10 @@ static int smb3_fs_context_parse_param(struct fs_context *fc,
>                 if (cifs_parse_security_flavors(fc, param->string, ctx) != 0)
>                         goto cifs_parse_mount_err;
>                 break;
> +       case Opt_upcalltarget:
> +               if (cifs_parse_upcall_target(fc, param->string, ctx) != 0)
> +                       goto cifs_parse_mount_err;
> +               break;
>         case Opt_cache:
>                 if (cifs_parse_cache_flavor(fc, param->string, ctx) != 0)
>                         goto cifs_parse_mount_err;
> @@ -1617,6 +1651,11 @@ static int smb3_fs_context_parse_param(struct fs_context *fc,
>         }
>         /* case Opt_ignore: - is ignored as expected ... */
>
> +       if (ctx->multiuser && ctx->upcall_target == UPTARGET_MOUNT) {
> +               cifs_errorf(fc, "multiuser mount option not supported with upcalltarget set as 'mount'\n");
> +               goto cifs_parse_mount_err;
> +       }
> +
>         return 0;
>
>   cifs_parse_mount_err:
> diff --git a/fs/smb/client/fs_context.h b/fs/smb/client/fs_context.h
> index cf577ec0d..e3ceed48c 100644
> --- a/fs/smb/client/fs_context.h
> +++ b/fs/smb/client/fs_context.h
> @@ -61,6 +61,12 @@ enum cifs_sec_param {
>         Opt_sec_err
>  };
>
> +enum cifs_upcall_target_param {
> +       Opt_upcall_target_mount,
> +       Opt_upcall_target_application,
> +       Opt_upcall_target_err
> +};
> +
>  enum cifs_param {
>         /* Mount options that take no arguments */
>         Opt_user_xattr,
> @@ -114,6 +120,8 @@ enum cifs_param {
>         Opt_multichannel,
>         Opt_compress,
>         Opt_witness,
> +       Opt_is_upcall_target_mount,
> +       Opt_is_upcall_target_application,
>
>         /* Mount options which take numeric value */
>         Opt_backupuid,
> @@ -157,6 +165,7 @@ enum cifs_param {
>         Opt_sec,
>         Opt_cache,
>         Opt_reparse,
> +       Opt_upcalltarget,
>
>         /* Mount options to be ignored */
>         Opt_ignore,
> @@ -198,6 +207,7 @@ struct smb3_fs_context {
>         umode_t file_mode;
>         umode_t dir_mode;
>         enum securityEnum sectype; /* sectype requested via mnt opts */
> +       enum upcall_target_enum upcall_target; /* where to upcall for mount */
>         bool sign; /* was signing requested via mnt opts? */
>         bool ignore_signature:1;
>         bool retry:1;
> --
> 2.43.0
>
> On Mon, 18 Nov 2024 at 22:08, <budhirajaritviksmb@xxxxxxxxx> wrote:
>>
>> From: Ritvik Budhiraja <rbudhiraja@xxxxxxxxxxxxx>
>>
>> NOTE: This patch is dependent on one of the previously sent patches:
>> [PATCH] CIFS: New mount option for cifs.upcall namespace resolution
>> which introduces a new mount option called upcall_target, to
>> customise the upcall behaviour.
>>
>> Building upon the above patch, the following patch adds functionality
>> to handle upcall_target as a mount option in cifs.upcall. It can have 2 values -
>> mount, app.
>> Having this new mount option allows the mount command to specify where the
>> upcall should happen: 'mount' for resolving the upcall to the host
>> namespace, and 'app' for resolving the upcall to the ns of the calling
>> thread. This will enable both the scenarios where the Kerberos credentials
>> can be found on the application namespace or the host namespace to which
>> just the mount operation is "delegated".
>> This aids use cases like Kubernetes where the mount
>> happens on behalf of the application in another container altogether.
>>
>> Signed-off-by: Ritvik Budhiraja <rbudhiraja@xxxxxxxxxxxxx>
>> ---
>>  cifs.upcall.c | 55 +++++++++++++++++++++++++++++++++++++++++++--------
>>  1 file changed, 47 insertions(+), 8 deletions(-)
>>
>> diff --git a/cifs.upcall.c b/cifs.upcall.c
>> index ff6f2bd..a790aca 100644
>> --- a/cifs.upcall.c
>> +++ b/cifs.upcall.c
>> @@ -954,6 +954,13 @@ struct decoded_args {
>>  #define MAX_USERNAME_SIZE 256
>>         char username[MAX_USERNAME_SIZE + 1];
>>
>> +#define MAX_UPCALL_STRING_LEN 6 /* "mount\0" */
>> +       enum upcall_target_enum {
>> +               UPTARGET_UNSPECIFIED, /* not specified, defaults to app */
>> +               UPTARGET_MOUNT, /* upcall to the mount namespace */
>> +               UPTARGET_APP, /* upcall to the application namespace which did the mount */
>> +       } upcall_target;
>> +
>>         uid_t uid;
>>         uid_t creduid;
>>         pid_t pid;
>> @@ -970,6 +977,7 @@ struct decoded_args {
>>  #define DKD_HAVE_PID           0x20
>>  #define DKD_HAVE_CREDUID       0x40
>>  #define DKD_HAVE_USERNAME      0x80
>> +#define DKD_HAVE_UPCALL_TARGET 0x100
>>  #define DKD_MUSTHAVE_SET (DKD_HAVE_HOSTNAME|DKD_HAVE_VERSION|DKD_HAVE_SEC)
>>         int have;
>>  };
>> @@ -980,6 +988,7 @@ __decode_key_description(const char *desc, struct decoded_args *arg)
>>         size_t len;
>>         char *pos;
>>         const char *tkn = desc;
>> +       arg->upcall_target = UPTARGET_UNSPECIFIED;
>>
>>         do {
>>                 pos = index(tkn, ';');
>> @@ -1078,6 +1087,31 @@ __decode_key_description(const char *desc, struct decoded_args *arg)
>>                         }
>>                         arg->have |= DKD_HAVE_VERSION;
>>                         syslog(LOG_DEBUG, "ver=%d", arg->ver);
>> +               } else if (strncmp(tkn, "upcall_target=", 14) == 0) {
>> +                       if (pos == NULL)
>> +                               len = strlen(tkn);
>> +                       else
>> +                               len = pos - tkn;
>> +
>> +                       len -= 14;
>> +                       if (len > MAX_UPCALL_STRING_LEN) {
>> +                               syslog(LOG_ERR, "upcall_target= value too long for buffer");
>> +                               return 1;
>> +                       }
>> +                       if (strncmp(tkn + 14, "mount", 5) == 0) {
>> +                               arg->upcall_target = UPTARGET_MOUNT;
>> +                               syslog(LOG_DEBUG, "upcall_target=mount");
>> +                       } else if (strncmp(tkn + 14, "app", 3) == 0) {
>> +                               arg->upcall_target = UPTARGET_APP;
>> +                               syslog(LOG_DEBUG, "upcall_target=app");
>> +                       } else {
>> +                               // Should never happen
>> +                               syslog(LOG_ERR, "Invalid upcall_target value: %s, defaulting to app",
>> +                                      tkn + 14);
>> +                               arg->upcall_target = UPTARGET_APP;
>> +                               syslog(LOG_DEBUG, "upcall_target=app");
>> +                       }
>> +                       arg->have |= DKD_HAVE_UPCALL_TARGET;
>>                 }
>>                 if (pos == NULL)
>>                         break;
>> @@ -1441,15 +1475,20 @@ int main(const int argc, char *const argv[])
>>          * acceptably in containers, because we'll be looking at the correct
>>          * filesystem and have the correct network configuration.
>>          */
>> -       rc = switch_to_process_ns(arg->pid);
>> -       if (rc == -1) {
>> -               syslog(LOG_ERR, "unable to switch to process namespace: %s", strerror(errno));
>> -               rc = 1;
>> -               goto out;
>> +       if (arg->upcall_target == UPTARGET_APP || arg->upcall_target == UPTARGET_UNSPECIFIED) {
>> +               syslog(LOG_INFO, "upcall_target=app, switching namespaces to application thread");
>> +               rc = switch_to_process_ns(arg->pid);
>> +               if (rc == -1) {
>> +                       syslog(LOG_ERR, "unable to switch to process namespace: %s", strerror(errno));
>> +                       rc = 1;
>> +                       goto out;
>> +               }
>> +               if (trim_capabilities(env_probe))
>> +                       goto out;
>> +       } else {
>> +               syslog(LOG_INFO, "upcall_target=mount, not switching namespaces to application thread");
>>         }
>>
>> -       if (trim_capabilities(env_probe))
>> -               goto out;
>>
>>         /*
>>          * The kernel doesn't pass down the gid, so we resort here to scraping
>> @@ -1496,7 +1535,7 @@ int main(const int argc, char *const argv[])
>>          * look at the environ file.
>>          */
>>         env_cachename =
>> -               get_cachename_from_process_env(env_probe ? arg->pid : 0);
>> +               get_cachename_from_process_env((env_probe && (arg->upcall_target == UPTARGET_APP)) ? arg->pid : 0);
>>
>>         rc = setuid(uid);
>>         if (rc == -1) {
>> --
>> 2.43.0
>>




[Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux